// server/index.ts import express2 from "express"; // server/middleware/graphql.ts import { GraphQLClient } from "graphql-request"; function attachGraphQLClient(req, _res, next) { const endpoint2 = process.env.GO_GRAPHQL_URL || "http://localhost:8080/graphql"; const headers = {}; if (req.headers.authorization) { headers["authorization"] = req.headers.authorization; } req.gql = new GraphQLClient(endpoint2, { headers }); next(); } // server/routes/comment.ts import { Router } from "express"; // server/lib/graphqlClient.ts import { GraphQLClient as GraphQLClient2 } from "graphql-request"; var endpoint = process.env.GO_GRAPHQL_URL || "http://localhost:8080/graphql"; var graphqlClient = new GraphQLClient2(endpoint, { headers: { // Add any required headers here, e.g. authorization } }); // server/lib/error.ts function normalizeError(err) { if (!err) return { message: "Unknown error" }; if (typeof err === "string") return { message: err }; if (err instanceof Error) { return { message: err.message }; } return { message: "Unexpected error", details: err }; } function respondWithError(res, err, fallback = "Request failed") { const normalized = normalizeError(err); res.status(500).json({ message: fallback, error: normalized.message }); } // shared/generated/graphql.ts import gql from "graphql-tag"; var RegisterDocument = gql` mutation Register($input: RegisterInput!) { register(input: $input) { token user { id username email } } } `; var LoginDocument = gql` mutation Login($email: String!, $password: String!) { login(email: $email, password: $password) { token user { id username email } } } `; var LogoutDocument = gql` mutation Logout { logout } `; var RefreshTokenDocument = gql` mutation RefreshToken { refreshToken { token user { id username email } } } `; var GetAuthorDocument = gql` query GetAuthor($id: ID!) { author(id: $id) { id name biography createdAt updatedAt } } `; var AuthorsDocument = gql` query Authors($limit: Int, $offset: Int, $search: String, $countryId: ID) { authors(limit: $limit, offset: $offset, search: $search, countryId: $countryId) { id name createdAt } } `; var CreateAuthorDocument = gql` mutation CreateAuthor($input: AuthorInput!) { createAuthor(input: $input) { id name createdAt } } `; var GetBookmarkDocument = gql` query GetBookmark($id: ID!) { bookmark(id: $id) { id name createdAt user { id username } work { id name } } } `; var BookmarksDocument = gql` query Bookmarks($userId: ID) { bookmarks(userId: $userId) { id name createdAt work { id name } } } `; var CreateBookmarkDocument = gql` mutation CreateBookmark($input: BookmarkInput!) { createBookmark(input: $input) { id name createdAt work { id name } } } `; var DeleteBookmarkDocument = gql` mutation DeleteBookmark($id: ID!) { deleteBookmark(id: $id) } `; var GetCategoryDocument = gql` query GetCategory($id: ID!) { category(id: $id) { id name createdAt updatedAt works { id name } } } `; var CategoriesDocument = gql` query Categories($limit: Int, $offset: Int) { categories(limit: $limit, offset: $offset) { id name createdAt updatedAt } } `; var GetCollectionDocument = gql` query GetCollection($id: ID!) { collection(id: $id) { id name description createdAt updatedAt works { id name } user { id username } } } `; var CollectionsDocument = gql` query Collections($userId: ID, $limit: Int, $offset: Int) { collections(userId: $userId, limit: $limit, offset: $offset) { id name description createdAt updatedAt } } `; var CreateCollectionDocument = gql` mutation CreateCollection($input: CollectionInput!) { createCollection(input: $input) { id name description createdAt updatedAt } } `; var UpdateCollectionDocument = gql` mutation UpdateCollection($id: ID!, $input: CollectionInput!) { updateCollection(id: $id, input: $input) { id name description updatedAt } } `; var DeleteCollectionDocument = gql` mutation DeleteCollection($id: ID!) { deleteCollection(id: $id) } `; var AddWorkToCollectionDocument = gql` mutation AddWorkToCollection($collectionId: ID!, $workId: ID!) { addWorkToCollection(collectionId: $collectionId, workId: $workId) { id name works { id name } } } `; var RemoveWorkFromCollectionDocument = gql` mutation RemoveWorkFromCollection($collectionId: ID!, $workId: ID!) { removeWorkFromCollection(collectionId: $collectionId, workId: $workId) { id name works { id name } } } `; var GetCommentDocument = gql` query GetComment($id: ID!) { comment(id: $id) { id text createdAt updatedAt user { id username displayName } work { id name } translation { id name } parentComment { id } } } `; var CommentsDocument = gql` query Comments($workId: ID, $translationId: ID, $userId: ID, $limit: Int, $offset: Int) { comments( workId: $workId translationId: $translationId userId: $userId limit: $limit offset: $offset ) { id text createdAt user { id username displayName } } } `; var CreateCommentDocument = gql` mutation CreateComment($input: CommentInput!) { createComment(input: $input) { id text createdAt user { id username } } } `; var UpdateCommentDocument = gql` mutation UpdateComment($id: ID!, $input: CommentInput!) { updateComment(id: $id, input: $input) { id text updatedAt } } `; var DeleteCommentDocument = gql` mutation DeleteComment($id: ID!) { deleteComment(id: $id) } `; var GetContributionDocument = gql` query GetContribution($id: ID!) { contribution(id: $id) { id name status createdAt updatedAt user { id username } work { id name } translation { id name } } } `; var ContributionsDocument = gql` query Contributions($userId: ID, $workId: ID, $translationId: ID) { contributions(userId: $userId, workId: $workId, translationId: $translationId) { id name status createdAt updatedAt } } `; var CreateContributionDocument = gql` mutation CreateContribution($input: ContributionInput!) { createContribution(input: $input) { id name status createdAt } } `; var UpdateContributionDocument = gql` mutation UpdateContribution($id: ID!, $input: ContributionInput!) { updateContribution(id: $id, input: $input) { id name status updatedAt } } `; var DeleteContributionDocument = gql` mutation DeleteContribution($id: ID!) { deleteContribution(id: $id) } `; var ReviewContributionDocument = gql` mutation ReviewContribution($id: ID!, $status: ContributionStatus!, $feedback: String) { reviewContribution(id: $id, status: $status, feedback: $feedback) { id status updatedAt } } `; var GetLikeDocument = gql` query GetLike($id: ID!) { like(id: $id) { id createdAt user { id username } work { id name } translation { id name } comment { id } } } `; var LikesDocument = gql` query Likes($workId: ID, $translationId: ID, $commentId: ID) { likes(workId: $workId, translationId: $translationId, commentId: $commentId) { id user { id username } createdAt } } `; var CreateLikeDocument = gql` mutation CreateLike($input: LikeInput!) { createLike(input: $input) { id createdAt user { id username } } } `; var DeleteLikeDocument = gql` mutation DeleteLike($id: ID!) { deleteLike(id: $id) } `; var SearchDocument = gql` query Search($query: String!, $limit: Int, $offset: Int, $filters: SearchFilters) { search(query: $query, limit: $limit, offset: $offset, filters: $filters) { works { id name } authors { id name } translations { id name } total } } `; var WorkStatsDocument = gql` query WorkStats { works: works { id name } } `; var UserStatsDocument = gql` query UserStats { users: users { id username } } `; var BlogStatsDocument = gql` query BlogStats { blog: blog(id: "sample-id") { id title } } `; var CommentStatsDocument = gql` query CommentStats { comments: comments { id text } } `; var GetTagDocument = gql` query GetTag($id: ID!) { tag(id: $id) { id name createdAt } } `; var TagsDocument = gql` query Tags($limit: Int, $offset: Int) { tags(limit: $limit, offset: $offset) { id name createdAt } } `; var GetTranslationDocument = gql` query GetTranslation($id: ID!) { translation(id: $id) { id name language content work { id name } createdAt updatedAt } } `; var TranslationsDocument = gql` query Translations($workId: ID!, $language: String, $limit: Int, $offset: Int) { translations( workId: $workId language: $language limit: $limit offset: $offset ) { id name language createdAt } } `; var CreateTranslationDocument = gql` mutation CreateTranslation($input: TranslationInput!) { createTranslation(input: $input) { id name language createdAt } } `; var UpdateTranslationDocument = gql` mutation UpdateTranslation($id: ID!, $input: TranslationInput!) { updateTranslation(id: $id, input: $input) { id name language updatedAt } } `; var DeleteTranslationDocument = gql` mutation DeleteTranslation($id: ID!) { deleteTranslation(id: $id) } `; var GetUserDocument = gql` query GetUser($id: ID!) { user(id: $id) { id username email displayName role createdAt updatedAt } } `; var UsersDocument = gql` query Users($limit: Int, $offset: Int, $role: UserRole) { users(limit: $limit, offset: $offset, role: $role) { id username email displayName role createdAt updatedAt } } `; var UpdateUserDocument = gql` mutation UpdateUser($id: ID!, $input: UserInput!) { updateUser(id: $id, input: $input) { id username email displayName role createdAt updatedAt } } `; var DeleteUserDocument = gql` mutation DeleteUser($id: ID!) { deleteUser(id: $id) } `; var GetUserProfileDocument = gql` query GetUserProfile($userId: ID!) { userProfile(userId: $userId) { id userId phoneNumber website twitter facebook linkedIn github preferences settings createdAt updatedAt } } `; var GetWorkDocument = gql` query GetWork($id: ID!) { work(id: $id) { id name language createdAt updatedAt } } `; var WorksDocument = gql` query Works($limit: Int, $offset: Int, $language: String, $authorId: ID, $tagId: ID, $search: String) { works( limit: $limit offset: $offset language: $language authorId: $authorId tagId: $tagId search: $search ) { id name language createdAt } } `; var CreateWorkDocument = gql` mutation CreateWork($input: WorkInput!) { createWork(input: $input) { id name language createdAt } } `; // server/routes/comment.ts var router = Router(); router.get("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { comment } = await client.request( GetCommentDocument, { id: req.params.id } ); if (!comment) return res.status(404).json({ message: "Comment not found" }); res.json(comment); } catch (error) { respondWithError(res, error, "Failed to fetch comment"); } }); router.get("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { comments } = await client.request(CommentsDocument, { workId: req.query.workId, translationId: req.query.translationId, userId: req.query.userId, limit: req.query.limit ? Number(req.query.limit) : void 0, offset: req.query.offset ? Number(req.query.offset) : void 0 }); res.json(comments); } catch (error) { respondWithError(res, error, "Failed to fetch comments"); } }); router.post("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { createComment } = await client.request( CreateCommentDocument, { input: req.body } ); res.status(201).json(createComment); } catch (error) { respondWithError(res, error, "Failed to create comment"); } }); router.put("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { updateComment } = await client.request( UpdateCommentDocument, { id: req.params.id, input: req.body } ); res.json(updateComment); } catch (error) { respondWithError(res, error, "Failed to update comment"); } }); router.delete("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { deleteComment } = await client.request( DeleteCommentDocument, { id: req.params.id } ); res.json({ success: deleteComment }); } catch (error) { respondWithError(res, error, "Failed to delete comment"); } }); var comment_default = router; // server/routes/user.ts import { Router as Router2 } from "express"; var router2 = Router2(); router2.get("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { user } = await client.request(GetUserDocument, { id: req.params.id }); res.json(user); } catch (error) { respondWithError(res, error, "Failed to fetch user"); } }); router2.get("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { users } = await client.request(UsersDocument, { ...req.query }); res.json(users); } catch (error) { respondWithError(res, error, "Failed to fetch users"); } }); router2.put("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { updateUser } = await client.request( UpdateUserDocument, { id: req.params.id, input: req.body } ); res.json(updateUser); } catch (error) { respondWithError(res, error, "Failed to update user"); } }); router2.delete("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { deleteUser } = await client.request( DeleteUserDocument, { id: req.params.id } ); res.json({ success: deleteUser }); } catch (error) { respondWithError(res, error, "Failed to delete user"); } }); var user_default = router2; // server/routes/author.ts import { Router as Router3 } from "express"; var router3 = Router3(); router3.get("/", async (req, res) => { try { const variables = { limit: req.query.limit ? Number(req.query.limit) : void 0, offset: req.query.offset ? Number(req.query.offset) : void 0, search: req.query.search, countryId: req.query.countryId }; const client = req.gql || graphqlClient; const { authors } = await client.request( AuthorsDocument, variables ); res.json(authors); } catch (error) { respondWithError(res, error, "Failed to fetch authors"); } }); router3.get("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { author } = await client.request( GetAuthorDocument, { id: req.params.id } ); if (!author) return res.status(404).json({ message: "Author not found" }); res.json(author); } catch (error) { respondWithError(res, error, "Failed to fetch author"); } }); router3.post("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { createAuthor } = await client.request( CreateAuthorDocument, { input: req.body } ); res.status(201).json(createAuthor); } catch (error) { respondWithError(res, error, "Failed to create author"); } }); var author_default = router3; // server/routes/work.ts import { Router as Router4 } from "express"; var router4 = Router4(); router4.get("/", async (req, res) => { try { const variables = { limit: req.query.limit ? Number(req.query.limit) : void 0, offset: req.query.offset ? Number(req.query.offset) : void 0, language: req.query.language, authorId: req.query.authorId, tagId: req.query.tagId, search: req.query.search }; const client = req.gql || graphqlClient; const { works } = await client.request( WorksDocument, variables ); res.json(works); } catch (error) { respondWithError(res, error, "Failed to fetch works"); } }); router4.get("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { work } = await client.request(GetWorkDocument, { id: req.params.id }); if (!work) return res.status(404).json({ message: "Work not found" }); res.json(work); } catch (error) { respondWithError(res, error, "Failed to fetch work"); } }); router4.post("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { createWork } = await client.request( CreateWorkDocument, { input: req.body } ); res.status(201).json(createWork); } catch (error) { respondWithError(res, error, "Failed to create work"); } }); var work_default = router4; // server/routes/tag.ts import { Router as Router5 } from "express"; var router5 = Router5(); router5.get("/", async (req, res) => { try { const variables = { limit: req.query.limit ? Number(req.query.limit) : void 0, offset: req.query.offset ? Number(req.query.offset) : void 0 }; const client = req.gql || graphqlClient; const { tags } = await client.request(TagsDocument, variables); res.json(tags); } catch (error) { respondWithError(res, error, "Failed to fetch tags"); } }); router5.get("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { tag } = await client.request(GetTagDocument, { id: req.params.id }); if (!tag) return res.status(404).json({ message: "Tag not found" }); res.json(tag); } catch (error) { respondWithError(res, error, "Failed to fetch tag"); } }); var tag_default = router5; // server/routes/collection.ts import { Router as Router6 } from "express"; var router6 = Router6(); router6.get("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { collection } = await client.request( GetCollectionDocument, { id: req.params.id } ); if (!collection) return res.status(404).json({ message: "Collection not found" }); res.json(collection); } catch (error) { respondWithError(res, error, "Failed to fetch collection"); } }); router6.get("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { collections } = await client.request( CollectionsDocument, { userId: req.query.userId, limit: req.query.limit ? Number(req.query.limit) : void 0, offset: req.query.offset ? Number(req.query.offset) : void 0 } ); res.json(collections); } catch (error) { respondWithError(res, error, "Failed to fetch collections"); } }); router6.post("/", async (req, res) => { try { const client = req.gql || graphqlClient; const { createCollection } = await client.request( CreateCollectionDocument, { input: req.body } ); res.status(201).json(createCollection); } catch (error) { respondWithError(res, error, "Failed to create collection"); } }); router6.put("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { updateCollection } = await client.request( UpdateCollectionDocument, { id: req.params.id, input: req.body } ); res.json(updateCollection); } catch (error) { respondWithError(res, error, "Failed to update collection"); } }); router6.delete("/:id", async (req, res) => { try { const client = req.gql || graphqlClient; const { deleteCollection } = await client.request( DeleteCollectionDocument, { id: req.params.id } ); res.json({ success: deleteCollection }); } catch (error) { respondWithError(res, error, "Failed to delete collection"); } }); router6.post("/:id/works", async (req, res) => { try { const client = req.gql || graphqlClient; const { addWorkToCollection } = await client.request( AddWorkToCollectionDocument, { collectionId: req.params.id, workId: req.body.workId } ); res.status(201).json(addWorkToCollection); } catch (error) { respondWithError(res, error, "Failed to add work to collection"); } }); router6.delete("/:id/works/:workId", async (req, res) => { try { const client = req.gql || graphqlClient; const { removeWorkFromCollection } = await client.request( RemoveWorkFromCollectionDocument, { collectionId: req.params.id, workId: req.params.workId } ); res.json(removeWorkFromCollection); } catch (error) { respondWithError(res, error, "Failed to remove work from collection"); } }); var collection_default = router6; // server/routes/blog.ts import { Router as Router7 } from "express"; var router7 = Router7(); router7.get("/stats", async (req, res) => { try { const client = req.gql || graphqlClient; const data = await client.request(BlogStatsDocument, {}); res.json(data.blog); } catch (error) { respondWithError(res, error, "Failed to fetch blog stats"); } }); var blog_default = router7; // server/routes/stats.ts import { Router as Router8 } from "express"; var router8 = Router8(); router8.get("/work", async (req, res) => { try { const client = req.gql || graphqlClient; const data = await client.request( WorkStatsDocument, {} ); res.json(data.works); } catch (error) { respondWithError(res, error, "Failed to fetch work stats"); } }); router8.get("/user", async (req, res) => { try { const client = req.gql || graphqlClient; const data = await client.request( UserStatsDocument, {} ); res.json(data.users); } catch (error) { respondWithError(res, error, "Failed to fetch user stats"); } }); router8.get("/blog", async (req, res) => { try { const client = req.gql || graphqlClient; const data = await client.request( BlogStatsDocument, {} ); res.json(data.blog); } catch (error) { respondWithError(res, error, "Failed to fetch blog stats"); } }); router8.get("/comment", async (req, res) => { try { const client = req.gql || graphqlClient; const data = await client.request( CommentStatsDocument, {} ); res.json(data.comments); } catch (error) { respondWithError(res, error, "Failed to fetch comment stats"); } }); var stats_default = router8; // server/vite.ts import fs from "node:fs"; import path2 from "node:path"; import express from "express"; import { nanoid } from "nanoid"; import { createLogger, createServer as createViteServer } from "vite"; // vite.config.ts import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import path from "path"; var vite_config_default = defineConfig({ plugins: [react()], root: "client", resolve: { alias: { "@": path.resolve(__dirname, "./client/src"), "@shared": path.resolve(__dirname, "./shared") } }, build: { outDir: "../dist" } }); // server/vite.ts var viteLogger = createLogger(); function log(message, source = "express") { const formattedTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", second: "2-digit", hour12: true }); console.log(`${formattedTime} [${source}] ${message}`); } async function setupVite(app2, server) { const serverOptions = { middlewareMode: true, hmr: { server }, allowedHosts: ["localhost"] }; const vite = await createViteServer({ ...vite_config_default, configFile: false, customLogger: { ...viteLogger, error: (msg, options) => { viteLogger.error(msg, options); process.exit(1); } }, server: serverOptions, appType: "custom" }); app2.use(vite.middlewares); app2.use("*", async (req, res, next) => { const url = req.originalUrl; try { const clientTemplate = path2.resolve( import.meta.dirname, "..", "client", "index.html" ); let template = await fs.promises.readFile(clientTemplate, "utf-8"); template = template.replace( `src="/src/main.tsx"`, `src="/src/main.tsx?v=${nanoid()}"` ); const page = await vite.transformIndexHtml(url, template); res.status(200).set({ "Content-Type": "text/html" }).end(page); } catch (e) { vite.ssrFixStacktrace(e); next(e); } }); } function serveStatic(app2) { const distPath = path2.resolve(import.meta.dirname, "public"); if (!fs.existsSync(distPath)) { throw new Error( `Could not find the build directory: ${distPath}, make sure to build the client first` ); } app2.use(express.static(distPath)); app2.use("*", (_req, res) => { res.sendFile(path2.resolve(distPath, "index.html")); }); } // server/index.ts import { createServer } from "node:http"; var app = express2(); app.use(express2.json()); app.use(express2.urlencoded({ extended: false })); app.use(attachGraphQLClient); app.use((req, res, next) => { const start = Date.now(); const path3 = req.path; let capturedJsonResponse; const originalResJson = res.json; res.json = (bodyJson, ...args) => { capturedJsonResponse = bodyJson; return originalResJson.apply(res, [bodyJson, ...args]); }; res.on("finish", () => { const duration = Date.now() - start; if (path3.startsWith("/api")) { let logLine = `${req.method} ${path3} ${res.statusCode} in ${duration}ms`; if (capturedJsonResponse) { logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`; } if (logLine.length > 80) { logLine = `${logLine.slice(0, 79)}\u2026`; } log(logLine); } }); next(); }); (async () => { app.use("/api/users", user_default); app.use("/api/authors", author_default); app.use("/api/works", work_default); app.use("/api/tags", tag_default); app.use("/api/comments", comment_default); app.use("/api/collections", collection_default); app.use("/api/blog", blog_default); app.use("/api/stats", stats_default); const server = createServer(app); app.use((err, _req, res, _next) => { const fallbackStatus = 500; const status = typeof err === "object" && err ? err.status ?? err.statusCode ?? fallbackStatus : fallbackStatus; const message = err instanceof Error ? err.message : "Internal Server Error"; res.status(status).json({ message }); throw err; }); if (app.get("env") === "development") { await setupVite(app, server); } else { serveStatic(app); } const port = 5e3; server.listen(port, "0.0.0.0", () => { log(`serving on port ${port}`); }); })();