diff --git a/.gitignore b/.gitignore index cdb99a5..10e60b6 100644 --- a/.gitignore +++ b/.gitignore @@ -174,4 +174,8 @@ wrangler.jsonc .claude .trae -.npmrc \ No newline at end of file +.npmrc + +# idea + +.idea \ No newline at end of file diff --git a/cwd-api/src/api/public/getComments.ts b/cwd-api/src/api/public/getComments.ts index 452187f..836de90 100644 --- a/cwd-api/src/api/public/getComments.ts +++ b/cwd-api/src/api/public/getComments.ts @@ -1,10 +1,11 @@ import { Context } from 'hono' import { Bindings } from '../../bindings' import { getCravatar } from '../../utils/getAvatar' +import { decodePostSlug, getAllSlugFormats, escapeLikePattern } from '../../utils/decodePostSlug' export const getComments = async (c: Context<{ Bindings: Bindings }>) => { const rawPostSlug = c.req.query('post_slug') || '' - const postSlug = rawPostSlug.trim() + const postSlug = decodePostSlug(rawPostSlug) const page = parseInt(c.req.query('page') || '1') const limit = Math.min(parseInt(c.req.query('limit') || '20'), 50) const nested = c.req.query('nested') !== 'false' @@ -42,13 +43,14 @@ export const getComments = async (c: Context<{ Bindings: Bindings }>) => { } try { - const equalSlugs = Array.from(new Set(slugList)) - const likePatternsSet = new Set() - for (const s of equalSlugs) { - likePatternsSet.add(`${s}#%`) - likePatternsSet.add(`${s}?%`) + const allSlugFormats = new Set() + for (const s of slugList) { + for (const format of getAllSlugFormats(s)) { + allSlugFormats.add(format) + } } - const likePatterns = Array.from(likePatternsSet) + const equalSlugs = Array.from(allSlugFormats) + const whereParts: string[] = [] if (equalSlugs.length === 1) { whereParts.push('post_slug = ?') @@ -56,16 +58,13 @@ export const getComments = async (c: Context<{ Bindings: Bindings }>) => { const placeholders = equalSlugs.map(() => '?').join(', ') whereParts.push(`post_slug IN (${placeholders})`) } - for (let i = 0; i < likePatterns.length; i += 1) { - whereParts.push('post_slug LIKE ?') - } const whereClause = whereParts.length > 0 - ? `status = "approved" AND (${whereParts.join(' OR ')})` - : 'status = "approved"' + ? `(${whereParts.join(' OR ')})` + : '1=1' - let finalWhereClause = whereClause - const bindParams: unknown[] = [...equalSlugs, ...likePatterns] + let finalWhereClause = `status = "approved" AND ${whereClause}` + const bindParams: unknown[] = [...equalSlugs] if (siteId) { finalWhereClause += ' AND site_id = ?' diff --git a/cwd-api/src/api/public/getPagePv.ts b/cwd-api/src/api/public/getPagePv.ts index cbf99e2..7c8e13d 100644 --- a/cwd-api/src/api/public/getPagePv.ts +++ b/cwd-api/src/api/public/getPagePv.ts @@ -1,10 +1,11 @@ import type { Context } from 'hono'; import type { Bindings } from '../../bindings'; +import { decodePostSlug } from '../../utils/decodePostSlug'; export const getPagePv = async (c: Context<{ Bindings: Bindings }>) => { try { const rawPostSlug = c.req.query('post_slug') || ''; - const postSlug = rawPostSlug.trim(); + const postSlug = decodePostSlug(rawPostSlug); const rawSiteId = c.req.query('siteId') || ''; const siteId = rawSiteId && rawSiteId !== 'default' ? rawSiteId : ''; diff --git a/cwd-api/src/api/public/like.ts b/cwd-api/src/api/public/like.ts index 94ed60e..7c0bc3d 100644 --- a/cwd-api/src/api/public/like.ts +++ b/cwd-api/src/api/public/like.ts @@ -1,5 +1,6 @@ import type { Context } from 'hono'; import type { Bindings } from '../../bindings'; +import { decodePostSlug } from '../../utils/decodePostSlug'; type LikeStatusResponse = { liked: boolean; @@ -36,7 +37,7 @@ export const getLikeStatus = async ( ): Promise => { try { const rawPostSlug = c.req.query('post_slug') || ''; - const postSlug = rawPostSlug.trim(); + const postSlug = decodePostSlug(rawPostSlug); const siteId = c.req.query('siteId') || ''; if (!postSlug) { @@ -92,13 +93,14 @@ export const likePage = async ( const rawPostSlug = typeof body.postSlug === 'string' ? body.postSlug.trim() : ''; + const postSlug = decodePostSlug(rawPostSlug); const rawPostTitle = typeof body.postTitle === 'string' ? body.postTitle.trim() : ''; const rawPostUrl = typeof body.postUrl === 'string' ? body.postUrl.trim() : ''; const siteId = typeof body.siteId === 'string' ? body.siteId.trim() : ''; - if (!rawPostSlug) { + if (!postSlug) { return c.json({ message: 'postSlug is required' }, 400); } @@ -109,7 +111,7 @@ export const likePage = async ( const existingLike = await c.env.CWD_DB.prepare( 'SELECT id FROM Likes WHERE page_slug = ? AND user_id = ? AND site_id = ?' ) - .bind(rawPostSlug, userId, siteId) + .bind(postSlug, userId, siteId) .first<{ id: number }>(); let alreadyLiked = false; @@ -118,7 +120,7 @@ export const likePage = async ( await c.env.CWD_DB.prepare( 'INSERT INTO Likes (page_slug, user_id, created_at, site_id) VALUES (?, ?, ?, ?)' ) - .bind(rawPostSlug, userId, now, siteId) + .bind(postSlug, userId, now, siteId) .run(); } else { alreadyLiked = true; @@ -127,7 +129,7 @@ export const likePage = async ( const pageStatsRow = await c.env.CWD_DB.prepare( 'SELECT id FROM page_stats WHERE post_slug = ? AND site_id = ?' ) - .bind(rawPostSlug, siteId) + .bind(postSlug, siteId) .first<{ id: number }>(); if (!pageStatsRow) { @@ -135,7 +137,7 @@ export const likePage = async ( 'INSERT INTO page_stats (post_slug, post_title, post_url, pv, last_visit_at, created_at, updated_at, site_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)' ) .bind( - rawPostSlug, + postSlug, rawPostTitle || null, rawPostUrl || null, 0, @@ -161,7 +163,7 @@ export const likePage = async ( const totalRow = await c.env.CWD_DB.prepare( 'SELECT COUNT(*) AS count FROM Likes WHERE page_slug = ? AND site_id = ?' ) - .bind(rawPostSlug, siteId) + .bind(postSlug, siteId) .first<{ count: number }>(); const totalLikes = totalRow?.count || 0; diff --git a/cwd-api/src/api/public/likeComment.ts b/cwd-api/src/api/public/likeComment.ts index d4dd849..df47dc2 100644 --- a/cwd-api/src/api/public/likeComment.ts +++ b/cwd-api/src/api/public/likeComment.ts @@ -27,6 +27,7 @@ export const likeComment = async (c: Context<{ Bindings: Bindings }>) => { } const id = parsed; + const method = c.req.method; try { const existing = await c.env.CWD_DB.prepare( @@ -39,26 +40,20 @@ export const likeComment = async (c: Context<{ Bindings: Bindings }>) => { return c.json({ message: 'Comment not found' }, 404); } + const delta = method === 'DELETE' ? -1 : 1; + const currentLikes = typeof existing.likes === 'number' && Number.isFinite(existing.likes) && existing.likes >= 0 + ? existing.likes + : 0; + const newLikes = Math.max(0, currentLikes + delta); + await c.env.CWD_DB.prepare( - 'UPDATE Comment SET likes = COALESCE(likes, 0) + 1 WHERE id = ?' + 'UPDATE Comment SET likes = ? WHERE id = ?' ) - .bind(id) + .bind(newLikes, id) .run(); - const updated = await c.env.CWD_DB.prepare( - 'SELECT COALESCE(likes, 0) as likes FROM Comment WHERE id = ?' - ) - .bind(id) - .first<{ likes?: number }>(); - - const likes = - updated && typeof updated.likes === 'number' && Number.isFinite(updated.likes) && updated.likes >= 0 - ? updated.likes - : ((existing.likes || 0) + 1); - - return c.json({ id, likes }); + return c.json({ id, likes: newLikes }); } catch (e: any) { - return c.json({ message: e?.message || '点赞失败' }, 500); + return c.json({ message: e?.message || '操作失败' }, 500); } }; - diff --git a/cwd-api/src/api/public/postComment.ts b/cwd-api/src/api/public/postComment.ts index 3668461..f06079e 100644 --- a/cwd-api/src/api/public/postComment.ts +++ b/cwd-api/src/api/public/postComment.ts @@ -12,8 +12,8 @@ import { EmailNotificationSettings } from '../../utils/email'; import { loadTelegramSettings, sendTelegramMessage } from '../../utils/telegram'; +import { decodePostSlug } from '../../utils/decodePostSlug'; -// 检查内容,将