From fc3a94164789119b2dca955c9ccbb3df981d6af6 Mon Sep 17 00:00:00 2001 From: mukimovd <41473651-mukimovd@users.noreply.replit.com> Date: Thu, 8 May 2025 00:20:04 +0000 Subject: [PATCH] Enable analysis and annotation features for literary works on the platform Implements new API routes in `server/routes.ts` for fetching, creating, and retrieving analysis results and annotations, including user enrichment. Replit-Commit-Author: Agent Replit-Commit-Session-Id: cbacfb18-842a-4116-a907-18c0105ad8ec Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/39b5c689-6e8a-4d5a-9792-69cc81a56534/15578bf6-749f-46f9-89a4-2849cb59c0ac.jpg --- server/routes.ts | 145 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/server/routes.ts b/server/routes.ts index 602b59d..887dd2d 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -600,6 +600,151 @@ export async function registerRoutes(app: Express): Promise { res.json(works); }); + // Analysis results routes + app.get("/api/works/:slug/analysis", async (req: Request, res: Response) => { + try { + const work = await storage.getWorkBySlug(req.params.slug); + if (!work) { + return res.status(404).json({ message: "Work not found" }); + } + + const results = await storage.getAnalysisResultsByWorkId(work.id); + res.json(results); + } catch (error) { + console.error("Error fetching analysis results:", error); + res.status(500).json({ message: "Failed to fetch analysis results" }); + } + }); + + app.get("/api/works/:slug/analysis/:type", async (req: Request, res: Response) => { + try { + const work = await storage.getWorkBySlug(req.params.slug); + if (!work) { + return res.status(404).json({ message: "Work not found" }); + } + + const result = await storage.getAnalysisResultByWorkAndType(work.id, req.params.type); + if (!result) { + return res.status(404).json({ message: "Analysis result not found" }); + } + + res.json(result); + } catch (error) { + console.error("Error fetching analysis result:", error); + res.status(500).json({ message: "Failed to fetch analysis result" }); + } + }); + + app.post("/api/analysis", async (req: Request, res: Response) => { + try { + // Validate request body using zod + const analysisData = z.object({ + workId: z.number(), + type: z.string(), + data: z.any() + }).parse(req.body); + + const result = await storage.createAnalysisResult(analysisData); + res.status(201).json(result); + } catch (error) { + if (error instanceof z.ZodError) { + return res.status(400).json({ message: error.errors }); + } + console.error("Error creating analysis result:", error); + res.status(500).json({ message: "Failed to create analysis result" }); + } + }); + + // Annotations routes + app.get("/api/works/:slug/annotations", async (req: Request, res: Response) => { + try { + const work = await storage.getWorkBySlug(req.params.slug); + if (!work) { + return res.status(404).json({ message: "Work not found" }); + } + + const annotations = await storage.getAnnotationsByWorkId(work.id); + + // Enrich annotations with user information + const enrichedAnnotations = await Promise.all(annotations.map(async (annotation) => { + const user = await storage.getUser(annotation.userId); + if (!user) return annotation; + + const { password, ...safeUser } = user; + return { ...annotation, user: safeUser }; + })); + + res.json(enrichedAnnotations); + } catch (error) { + console.error("Error fetching annotations:", error); + res.status(500).json({ message: "Failed to fetch annotations" }); + } + }); + + app.get("/api/works/:slug/line/:lineNumber/annotations", async (req: Request, res: Response) => { + try { + const work = await storage.getWorkBySlug(req.params.slug); + if (!work) { + return res.status(404).json({ message: "Work not found" }); + } + + const lineNumber = Number(req.params.lineNumber); + const translationId = req.query.translationId ? Number(req.query.translationId) : undefined; + + const annotations = await storage.getAnnotationsByLine( + work.id, + lineNumber, + translationId + ); + + // Enrich annotations with user information + const enrichedAnnotations = await Promise.all(annotations.map(async (annotation) => { + const user = await storage.getUser(annotation.userId); + if (!user) return annotation; + + const { password, ...safeUser } = user; + return { ...annotation, user: safeUser }; + })); + + res.json(enrichedAnnotations); + } catch (error) { + console.error("Error fetching line annotations:", error); + res.status(500).json({ message: "Failed to fetch line annotations" }); + } + }); + + app.post("/api/annotations", async (req: Request, res: Response) => { + try { + // Validate request body using zod + const annotationData = z.object({ + workId: z.number(), + translationId: z.number().nullable().optional(), + lineNumber: z.number().nullable().optional(), + content: z.string(), + type: z.string(), + userId: z.number(), + isOfficial: z.boolean() + }).parse(req.body); + + const annotation = await storage.createAnnotation(annotationData); + + // Get user details + const user = await storage.getUser(annotation.userId); + if (user) { + const { password, ...safeUser } = user; + res.status(201).json({ ...annotation, user: safeUser }); + } else { + res.status(201).json(annotation); + } + } catch (error) { + if (error instanceof z.ZodError) { + return res.status(400).json({ message: error.errors }); + } + console.error("Error creating annotation:", error); + res.status(500).json({ message: "Failed to create annotation" }); + } + }); + const httpServer = createServer(app); return httpServer; }