Import source: app/backend/src/routes/wiki-proposals
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
import type { FastifyPluginAsync } from 'fastify';
|
||||
import { z } from 'zod';
|
||||
import { prisma } from '../db.js';
|
||||
import { requireAuth } from '../plugins/session.js';
|
||||
|
||||
const submitWikiSchema = z.object({
|
||||
articleId: z.string(),
|
||||
title: z.string().min(1).max(200),
|
||||
summary: z.string().min(1).max(500),
|
||||
content: z.string().min(1).max(500_000),
|
||||
});
|
||||
|
||||
const submitGlossarySchema = z.object({
|
||||
term: z.string().min(1).max(100),
|
||||
definition: z.string().min(1).max(50_000),
|
||||
sameAs: z.array(z.string().url()).default([]),
|
||||
});
|
||||
|
||||
const reviewProposalSchema = z.object({
|
||||
status: z.enum(['approved', 'rejected']),
|
||||
rejectionReason: z.string().optional(),
|
||||
});
|
||||
|
||||
export const wikiProposalRoutes: FastifyPluginAsync = async (app) => {
|
||||
app.addHook('preHandler', requireAuth());
|
||||
|
||||
// Submit a Wiki Edit Proposal
|
||||
app.post('/api/v1/wiki/proposals', async (req, reply) => {
|
||||
const parsed = submitWikiSchema.safeParse(req.body);
|
||||
if (!parsed.success) return reply.code(400).send({ error: 'invalid_input', details: parsed.error.issues });
|
||||
|
||||
const article = await prisma.article.findUnique({ where: { id: parsed.data.articleId } });
|
||||
if (!article) return reply.code(404).send({ error: 'article_not_found' });
|
||||
|
||||
const proposal = await prisma.wikiProposal.create({
|
||||
data: {
|
||||
articleId: article.id,
|
||||
title: parsed.data.title,
|
||||
summary: parsed.data.summary,
|
||||
content: parsed.data.content,
|
||||
suggestedById: req.session!.userId,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, proposal };
|
||||
});
|
||||
|
||||
// Submit a Glossary Proposal
|
||||
app.post('/api/v1/glossary/proposals', async (req, reply) => {
|
||||
const parsed = submitGlossarySchema.safeParse(req.body);
|
||||
if (!parsed.success) return reply.code(400).send({ error: 'invalid_input', details: parsed.error.issues });
|
||||
|
||||
const proposal = await prisma.glossaryProposal.create({
|
||||
data: {
|
||||
term: parsed.data.term,
|
||||
definition: parsed.data.definition,
|
||||
sameAs: parsed.data.sameAs,
|
||||
suggestedById: req.session!.userId,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, proposal };
|
||||
});
|
||||
|
||||
// Admin routes for reviewing proposals
|
||||
app.register(async (adminApp) => {
|
||||
adminApp.addHook('preHandler', async (req, reply) => {
|
||||
if (req.session?.role !== 'admin' && req.session?.role !== 'editor') {
|
||||
return reply.code(403).send({ error: 'forbidden' });
|
||||
}
|
||||
});
|
||||
|
||||
adminApp.get('/api/v1/admin/wiki/proposals', async (req) => {
|
||||
const proposals = await prisma.wikiProposal.findMany({
|
||||
where: { status: 'pending' },
|
||||
include: { article: { select: { title: true, slug: true } }, suggestedBy: { select: { name: true, username: true } } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
return { proposals };
|
||||
});
|
||||
|
||||
adminApp.patch('/api/v1/admin/wiki/proposals/:id', async (req, reply) => {
|
||||
const { id } = req.params as { id: string };
|
||||
const parsed = reviewProposalSchema.safeParse(req.body);
|
||||
if (!parsed.success) return reply.code(400).send({ error: 'invalid_input' });
|
||||
|
||||
const proposal = await prisma.wikiProposal.findUnique({ where: { id } });
|
||||
if (!proposal) return reply.code(404).send({ error: 'not_found' });
|
||||
|
||||
const updated = await prisma.wikiProposal.update({
|
||||
where: { id },
|
||||
data: {
|
||||
status: parsed.data.status,
|
||||
rejectionReason: parsed.data.rejectionReason,
|
||||
reviewedById: req.session!.userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (parsed.data.status === 'approved') {
|
||||
// Merge text and record in ArticleRevision
|
||||
await prisma.articleRevision.create({
|
||||
data: {
|
||||
articleId: proposal.articleId,
|
||||
savedById: req.session!.userId,
|
||||
title: proposal.title,
|
||||
summary: proposal.summary,
|
||||
content: proposal.content,
|
||||
status: 'wiki_merge',
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.article.update({
|
||||
where: { id: proposal.articleId },
|
||||
data: {
|
||||
title: proposal.title,
|
||||
summary: proposal.summary,
|
||||
content: proposal.content,
|
||||
dateModified: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true, proposal: updated };
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user