community / src /index.mts
jbilcke-hf's picture
jbilcke-hf HF staff
trying to improve performance
65dda73
import { validate as uuidValidate } from "uuid"
import express from "express"
import { v4 as uuidv4 } from "uuid"
import { hasValidAuthorization } from "./utils/hasValidAuthorization.mts"
import { initFolders } from "./initFolders.mts"
import { getValidNumber } from "./utils/getValidNumber.mts"
import { CreatePostResponse, GetAppPostResponse, GetAppPostsResponse, Post, PostVisibility } from "./types.mts"
import { savePost } from "./core/savePost.mts"
import { getAppPosts } from "./core/getAppPosts.mts"
import { deletePost } from "./core/deletePost.mts"
import { getPost } from "./core/getPost.mts"
import { getValidBoolean } from "./utils/getValidBoolean.mts"
initFolders()
const app = express()
const port = 7860
// fix this error: "PayloadTooLargeError: request entity too large"
// there are multiple version because.. yeah well, it's Express!
// app.use(bodyParser.json({limit: '50mb'}));
//app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(express.json({limit: '50mb'}));
app.use(express.urlencoded({limit: '50mb', extended: true}));
app.post("/posts/:appId", async (req, res) => {
if (!hasValidAuthorization(req.headers)) {
console.log("Invalid authorization")
res.status(401)
res.write(JSON.stringify({ error: "invalid token" }))
res.end()
return
}
const appId = req.params.appId
if (!uuidValidate(appId)) {
console.error("invalid appId")
res.status(400)
res.write(JSON.stringify({ error: `invalid appId` }))
res.end()
return
}
const postId = `${req.body.postId || uuidv4()}`
const prompt = `${req.body.prompt || ""}`
const model = `${req.body.model || ""}`
const assetUrl = `${req.body.assetUrl || ""}`
const previewUrl = `${req.body.previewUrl || assetUrl}`
const createdAt = `${req.body.createdAt || new Date().toISOString()}`
const visibility = `${req.body.visibility || "normal"}` as PostVisibility
const upvotes = getValidNumber(req.body.upvotes, 0, 1e15, 0)
const downvotes = getValidNumber(req.body.upvotes, 0, 1e15, 0)
if (!uuidValidate(postId)) {
console.error(`invalid postId ${postId}`)
res.status(400)
res.write(JSON.stringify({ error: `invalid postId ${postId}` }))
res.end()
return
}
if (!prompt.length) {
console.error(`invalid prompt length: cannot be zero`)
res.status(400)
res.write(JSON.stringify({ error: `invalid prompt length: cannot be zero` }))
res.end()
return
}
if (!previewUrl.length) {
console.error(`invalid preview URL length: cannot be zero`)
res.status(400)
res.write(JSON.stringify({ error: `invalid preview URL length: cannot be zero` }))
res.end()
return
}
if (!assetUrl.length) {
console.error(`invalid asset URL length: cannot be zero`)
res.status(400)
res.write(JSON.stringify({ error: `invalid asset URL length: cannot be zero` }))
res.end()
return
}
const post: Post = {
postId,
appId,
prompt,
model,
previewUrl,
assetUrl,
createdAt,
visibility,
upvotes,
downvotes,
}
try {
await savePost(post)
console.log(`saved post:`, post)
} catch (err) {
console.error(`failed to save the post: ${err}`)
res.status(400)
res.write(JSON.stringify({
success: false,
error: `failed to save the post: ${err}`,
post: undefined
} as unknown as CreatePostResponse
))
res.end()
return
}
res.status(201)
res.write(JSON.stringify({ success: true, error: "", post } as CreatePostResponse))
res.end()
})
app.get("/posts/:appId/firehose/:visibility/:maxNbItems/:shuffle", async (req, res) => {
const appId = `${req.params.appId}`
if (!uuidValidate(appId)) {
console.error("invalid appId")
res.status(400)
res.write(JSON.stringify({ error: `invalid appId` }))
res.end()
return
}
const visibility = `${req.params.visibility}`
const shuffle = getValidBoolean(`${req.params.shuffle}`, false)
const limit = getValidNumber(`${req.params.maxNbItems}`, 1, 80, 20)
try {
const posts = await getAppPosts({
appId,
visibility: visibility === "all" ? undefined : visibility as PostVisibility,
shuffle,
limit
})
res.status(200)
console.log(`returning ${posts.length} community posts for app ${appId} (visibility: ${visibility})`)
res.write(JSON.stringify({ posts } as GetAppPostsResponse))
res.end()
return
} catch (err) {
const error = `failed to load the posts for app ${appId} and visibility ${visibility}: ${err}`
console.error(error)
res.status(500)
res.write(JSON.stringify({ posts: [], error } as GetAppPostsResponse))
res.end()
return
}
})
app.get("/posts/:appId/:postId", async (req, res) => {
const appId = `${req.params.appId}`
if (!uuidValidate(appId)) {
console.error("invalid appId")
res.status(400)
res.write(JSON.stringify({ error: `invalid appId` }))
res.end()
return
}
const postId = `${req.params.postId}`
if (!uuidValidate(postId)) {
console.error("invalid postId")
res.status(400)
res.write(JSON.stringify({ error: `invalid postId` }))
res.end()
return
}
try {
const post = await getPost(appId, postId)
res.status(200)
console.log(`returning post ${postId} for app ${appId}`)
res.write(JSON.stringify({ post } as GetAppPostResponse))
res.end()
return
} catch (err) {
const error = `failed to load the post for app ${appId} and postId: ${postId}: ${err}`
console.error(error)
res.status(500)
res.write(JSON.stringify({ error } as GetAppPostResponse))
res.end()
return
}
})
// delete a post
app.delete("/posts/:appId/:postId", async (req, res) => {
if (!hasValidAuthorization(req.headers)) {
console.log("Invalid authorization")
res.status(401)
res.write(JSON.stringify({ error: "invalid token" }))
res.end()
return
}
const appId = req.params.appId
if (!uuidValidate(appId)) {
console.error("invalid appId")
res.status(400)
res.write(JSON.stringify({ error: `invalid appId` }))
res.end()
return
}
const postId = req.params.postId
if (!uuidValidate(postId)) {
console.error("invalid postId")
res.status(400)
res.write(JSON.stringify({ error: `invalid postId` }))
res.end()
return
}
try {
await deletePost(appId, postId)
res.status(200)
res.write(JSON.stringify({ success: true }))
res.end()
} catch (err) {
console.error(err)
res.status(404)
res.write(JSON.stringify({ error: "couldn't delete this post" }))
res.end()
}
})
app.get("/", async (req, res) => {
// this is what users will see in the space - but no need to show something scary
res.status(200)
res.write(`<html><head></head><body>
Community is a micro-service used to manage community posts for my various spaces.
It is used by <a href="https://jbilcke-hf-panoremix.hf.space" target="_blank">Panoremix</a>, a generative panorama app.
</body></html>`)
res.end()
})
app.listen(port, () => { console.log(`Open http://localhost:${port}`) })