feat(assets): 更新 Logo 与图片资源,添加数据层服务模块

- 更新 logo.svg/logo-light.svg/logo-white.svg 品牌标识
- 新增 logo-calligraphy.svg 书法体 Logo 变体
- 新增新闻、二维码、微信业务等图片资源
- 删除旧版 JPEG 测试图片
- 新增 CMS 数据层服务模块 (admin-api, auth, cms, crypto, db)
- 新增 cases/cross-references/methodology/team 常量数据
- 新增 site-config 站点配置
This commit is contained in:
张翔
2026-07-07 06:54:47 +08:00
parent 636bc4ecde
commit 49dbced0cb
32 changed files with 4219 additions and 23 deletions
+84
View File
@@ -0,0 +1,84 @@
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import { type NextRequest } from 'next/server';
const JWT_SECRET = process.env.JWT_SECRET || 'novalon-cms-secret-key-2024';
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET || 'novalon-cms-refresh-secret-2024';
const TOKEN_EXPIRY = '24h';
const REFRESH_TOKEN_EXPIRY = '7d';
export interface JwtPayload {
userId: string;
username: string;
role: string;
}
// ============ 密码工具 ============
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10);
}
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
// ============ Token 工具 ============
export function generateTokens(payload: JwtPayload): { accessToken: string; refreshToken: string } {
const accessToken = jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY });
const refreshToken = jwt.sign(payload, JWT_REFRESH_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRY });
return { accessToken, refreshToken };
}
export function verifyAccessToken(token: string): JwtPayload {
return jwt.verify(token, JWT_SECRET) as JwtPayload;
}
export function verifyRefreshToken(token: string): JwtPayload {
return jwt.verify(token, JWT_REFRESH_SECRET) as JwtPayload;
}
// ============ 请求认证 ============
export function getTokenFromRequest(request: NextRequest): string | null {
const authHeader = request.headers.get('authorization');
if (authHeader?.startsWith('Bearer ')) {
return authHeader.slice(7);
}
return null;
}
export function authenticateRequest(request: NextRequest): JwtPayload | null {
const token = getTokenFromRequest(request);
if (!token) return null;
try {
return verifyAccessToken(token);
} catch {
return null;
}
}
// ============ Cookie 工具 ============
export function setTokenCookie(response: Response, accessToken: string, refreshToken: string): void {
response.headers.set(
'Set-Cookie',
`novalon_token=${accessToken}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400`
);
response.headers.append(
'Set-Cookie',
`novalon_refresh=${refreshToken}; Path=/; HttpOnly; SameSite=Lax; Max-Age=604800`
);
}
export function clearTokenCookie(response: Response): void {
response.headers.set(
'Set-Cookie',
`novalon_token=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`
);
response.headers.append(
'Set-Cookie',
`novalon_refresh=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`
);
}