UNPKG

simple-message-board

Version:

简易留言板应用,支持markdown格式

159 lines (126 loc) 4.93 kB
const url = require('url'); const querystring = require('querystring'); const { sendHtml, sendJson, redirect, notFound, serveStatic } = require('./utils/http'); const { readBody } = require('./utils/body'); const { listMessages, createMessage, deleteMessage, fetchMessagesSince, getTotalCount, getFilteredCount } = require('./services/messageService'); const { getAllTags } = require('./services/tagService'); const { createReply, deleteReply } = require('./services/replyService'); const { renderHomePage } = require('./templates/homePage'); const { buildListPath } = require('./utils/paths'); const { MAX_PAGES, PAGE_SIZE } = require('./config'); async function routeRequest(req, res) { const parsedUrl = url.parse(req.url, true); const { pathname, query } = parsedUrl; if (pathname.startsWith('/static/')) { const served = serveStatic(req, res, pathname); if (served) { return; } } if (req.method === 'GET' && pathname === '/') { await handleHome(res, query); return; } if (req.method === 'POST' && pathname === '/submit') { await handleSubmit(req, res); return; } if (req.method === 'POST' && pathname === '/delete') { await handleDelete(req, res); return; } if (req.method === 'GET' && pathname === '/api/messages') { await handleApiMessages(res, query); return; } if (req.method === 'GET' && pathname === '/api/tags') { await handleApiTags(res); return; } if (req.method === 'POST' && pathname === '/reply') { await handleReply(req, res); return; } if (req.method === 'POST' && pathname === '/delete-reply') { await handleDeleteReply(req, res); return; } notFound(res); } async function handleHome(res, query) { const searchRaw = typeof query?.q === 'string' ? query.q : ''; const pageRaw = query?.page; const tagFilter = query?.tag; const data = await listMessages(searchRaw, pageRaw, tagFilter); const allTags = await getAllTags(); const html = renderHomePage({ ...data, allTags }); sendHtml(res, html); } async function handleSubmit(req, res) { const body = await readBody(req); const { message, tags } = querystring.parse(body); // 解析标签:支持逗号分隔或空格分隔 let tagArray = []; if (tags && typeof tags === 'string') { tagArray = tags.split(/[,,\s]+/) .map(t => t.trim()) .filter(Boolean); } await createMessage(message, tagArray); redirect(res, '/'); } async function handleDelete(req, res) { const body = await readBody(req); const { id, page, q, tag } = querystring.parse(body); const searchTerm = typeof q === 'string' ? q.trim() : ''; const tagFilter = typeof tag === 'string' ? tag.trim() : ''; await deleteMessage(id); // Use filtered count instead of global count to compute correct total pages const totalMessages = await getFilteredCount(searchTerm, tagFilter); const totalPages = Math.max(1, Math.min(MAX_PAGES, Math.ceil(totalMessages / PAGE_SIZE))); let targetPage = Number.parseInt(page, 10); if (Number.isNaN(targetPage) || targetPage < 1) { targetPage = 1; } if (targetPage > totalPages) { targetPage = totalPages; } redirect(res, buildListPath(targetPage, searchTerm, tagFilter)); } async function handleApiMessages(res, query) { const messages = await fetchMessagesSince(query?.since_id, query?.limit); sendJson(res, { messages }, 200, { 'Cache-Control': 'no-cache, no-store, must-revalidate' }); } async function handleApiTags(res) { const tags = await getAllTags(); sendJson(res, { tags }, 200, { 'Cache-Control': 'no-cache, no-store, must-revalidate' }); } async function handleReply(req, res) { const body = await readBody(req); const { message_id, content, page, q, tag } = querystring.parse(body); await createReply(message_id, content); const searchTerm = typeof q === 'string' ? q.trim() : ''; const tagFilter = typeof tag === 'string' ? tag.trim() : ''; let targetPage = Number.parseInt(page, 10); if (Number.isNaN(targetPage) || targetPage < 1) { targetPage = 1; } redirect(res, buildListPath(targetPage, searchTerm, tagFilter)); } async function handleDeleteReply(req, res) { const body = await readBody(req); const { id, page, q, tag } = querystring.parse(body); await deleteReply(id); const searchTerm = typeof q === 'string' ? q.trim() : ''; const tagFilter = typeof tag === 'string' ? tag.trim() : ''; let targetPage = Number.parseInt(page, 10); if (Number.isNaN(targetPage) || targetPage < 1) { targetPage = 1; } redirect(res, buildListPath(targetPage, searchTerm, tagFilter)); } module.exports = { routeRequest };