feat(评论): 新增功能开关管理及评论点赞功能优化
- 在管理后台添加功能开关页面,支持控制评论点赞和文章点赞功能的开启/关闭 - 优化评论点赞功能,支持取消点赞和本地存储点赞状态 - 更新点赞UI样式,提升用户体验 - 添加相关API接口和文档说明
This commit is contained in:
38
cwd-api/src/api/admin/featureSettings.ts
Normal file
38
cwd-api/src/api/admin/featureSettings.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Context } from 'hono';
|
||||
import { Bindings } from '../../bindings';
|
||||
import {
|
||||
loadFeatureSettings,
|
||||
saveFeatureSettings
|
||||
} from '../../utils/featureSettings';
|
||||
|
||||
export const getFeatureSettings = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
try {
|
||||
const settings = await loadFeatureSettings(c.env);
|
||||
return c.json(settings);
|
||||
} catch (e: any) {
|
||||
return c.json({ message: e.message || 'Failed to load feature settings' }, 500);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateFeatureSettings = async (c: Context<{ Bindings: Bindings }>) => {
|
||||
try {
|
||||
const body = await c.req.json();
|
||||
const enableCommentLike =
|
||||
typeof body.enableCommentLike === 'boolean'
|
||||
? body.enableCommentLike
|
||||
: undefined;
|
||||
const enableArticleLike =
|
||||
typeof body.enableArticleLike === 'boolean'
|
||||
? body.enableArticleLike
|
||||
: undefined;
|
||||
|
||||
await saveFeatureSettings(c.env, {
|
||||
enableCommentLike,
|
||||
enableArticleLike
|
||||
});
|
||||
|
||||
return c.json({ message: 'Saved successfully' });
|
||||
} catch (e: any) {
|
||||
return c.json({ message: e.message || 'Failed to save feature settings' }, 500);
|
||||
}
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
loadEmailNotificationSettings,
|
||||
saveEmailNotificationSettings
|
||||
} from './utils/email';
|
||||
import { loadFeatureSettings } from './utils/featureSettings';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
import { getComments } from './api/public/getComments';
|
||||
@@ -30,6 +31,10 @@ import { getLikeStatus, likePage } from './api/public/like';
|
||||
import { likeComment } from './api/public/likeComment';
|
||||
import { listLikes } from './api/admin/listLikes';
|
||||
import { getLikeStats } from './api/admin/likeStats';
|
||||
import {
|
||||
getFeatureSettings,
|
||||
updateFeatureSettings
|
||||
} from './api/admin/featureSettings';
|
||||
|
||||
const app = new Hono<{ Bindings: Bindings }>();
|
||||
const VERSION = `v${packageJson.version}`;
|
||||
@@ -228,6 +233,7 @@ app.post('/api/comments/like', likeComment);
|
||||
app.get('/api/config/comments', async (c) => {
|
||||
try {
|
||||
const settings = await loadCommentSettings(c.env);
|
||||
const featureSettings = await loadFeatureSettings(c.env);
|
||||
const {
|
||||
adminKey,
|
||||
adminKeySet,
|
||||
@@ -236,7 +242,7 @@ app.get('/api/config/comments', async (c) => {
|
||||
...publicSettings
|
||||
} = settings as any;
|
||||
|
||||
return c.json(publicSettings);
|
||||
return c.json({ ...publicSettings, ...featureSettings });
|
||||
} catch (e: any) {
|
||||
return c.json({ message: e.message || '加载评论配置失败' }, 500);
|
||||
}
|
||||
@@ -256,6 +262,8 @@ app.get('/admin/analytics/overview', getVisitOverview);
|
||||
app.get('/admin/analytics/pages', getVisitPages);
|
||||
app.get('/admin/likes/list', listLikes);
|
||||
app.get('/admin/likes/stats', getLikeStats);
|
||||
app.get('/admin/settings/features', getFeatureSettings);
|
||||
app.put('/admin/settings/features', updateFeatureSettings);
|
||||
app.get('/admin/settings/email', getAdminEmail);
|
||||
app.put('/admin/settings/email', setAdminEmail);
|
||||
app.get('/admin/settings/email-notify', async (c) => {
|
||||
|
||||
92
cwd-api/src/utils/featureSettings.ts
Normal file
92
cwd-api/src/utils/featureSettings.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Bindings } from '../bindings';
|
||||
|
||||
export const FEATURE_COMMENT_LIKE_KEY = 'comment_feature_comment_like';
|
||||
export const FEATURE_ARTICLE_LIKE_KEY = 'comment_feature_article_like';
|
||||
|
||||
export type FeatureSettings = {
|
||||
enableCommentLike: boolean;
|
||||
enableArticleLike: boolean;
|
||||
};
|
||||
|
||||
export async function loadFeatureSettings(env: Bindings): Promise<FeatureSettings> {
|
||||
await env.CWD_DB.prepare(
|
||||
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
|
||||
).run();
|
||||
|
||||
const keys = [FEATURE_COMMENT_LIKE_KEY, FEATURE_ARTICLE_LIKE_KEY];
|
||||
const { results } = await env.CWD_DB.prepare(
|
||||
'SELECT key, value FROM Settings WHERE key IN (?, ?)'
|
||||
)
|
||||
.bind(...keys)
|
||||
.all<{ key: string; value: string }>();
|
||||
|
||||
const map = new Map<string, string>();
|
||||
for (const row of results) {
|
||||
if (row && row.key) {
|
||||
map.set(row.key, row.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Default to true if not set, or false?
|
||||
// Usually features might be enabled by default.
|
||||
// But let's check the user requirement. "New settings... whether to enable..."
|
||||
// If I default to false, existing users might lose features if they were implicit.
|
||||
// But these are "new" settings.
|
||||
// "comment likes" and "article likes" existed before?
|
||||
// The code shows `like.ts` and `likeComment.ts`.
|
||||
// `likePage` handler in `index.ts`.
|
||||
// So the features exist. To avoid breaking changes, I should probably default to TRUE.
|
||||
// But wait, if I default to true, then the user has to manually turn them off.
|
||||
// If I default to false, they disappear.
|
||||
// Given "whether to enable" implies they might be optional now.
|
||||
// I'll default to TRUE to maintain backward compatibility (features visible by default).
|
||||
|
||||
const enableCommentLikeRaw = map.get(FEATURE_COMMENT_LIKE_KEY);
|
||||
const enableCommentLike = enableCommentLikeRaw !== '0'; // Default to true if missing or '1'
|
||||
|
||||
const enableArticleLikeRaw = map.get(FEATURE_ARTICLE_LIKE_KEY);
|
||||
const enableArticleLike = enableArticleLikeRaw !== '0'; // Default to true if missing or '1'
|
||||
|
||||
return {
|
||||
enableCommentLike,
|
||||
enableArticleLike
|
||||
};
|
||||
}
|
||||
|
||||
export async function saveFeatureSettings(
|
||||
env: Bindings,
|
||||
settings: Partial<FeatureSettings>
|
||||
) {
|
||||
await env.CWD_DB.prepare(
|
||||
'CREATE TABLE IF NOT EXISTS Settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)'
|
||||
).run();
|
||||
|
||||
const entries: { key: string; value: string | undefined }[] = [
|
||||
{
|
||||
key: FEATURE_COMMENT_LIKE_KEY,
|
||||
value:
|
||||
typeof settings.enableCommentLike === 'boolean'
|
||||
? settings.enableCommentLike
|
||||
? '1'
|
||||
: '0'
|
||||
: undefined
|
||||
},
|
||||
{
|
||||
key: FEATURE_ARTICLE_LIKE_KEY,
|
||||
value:
|
||||
typeof settings.enableArticleLike === 'boolean'
|
||||
? settings.enableArticleLike
|
||||
? '1'
|
||||
: '0'
|
||||
: undefined
|
||||
}
|
||||
];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.value !== undefined) {
|
||||
await env.CWD_DB.prepare('REPLACE INTO Settings (key, value) VALUES (?, ?)')
|
||||
.bind(entry.key, entry.value)
|
||||
.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user