feat: add book category functionality, fix XSS protection and validation issues, update film category mapping
This commit is contained in:
@@ -21,6 +21,112 @@ export function escapeHtml(str: string): string {
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证图片 URL 是否安全
|
||||
* 只允许 http:// 和 https:// 协议,防止 javascript: 等恶意协议
|
||||
* @param url 图片 URL
|
||||
* @returns 是否安全
|
||||
*/
|
||||
export function isValidImageUrl(url: string): boolean {
|
||||
if (!url || typeof url !== 'string') {
|
||||
return false;
|
||||
}
|
||||
// 只允许 http:// 和 https:// 协议
|
||||
return url.startsWith('http://') || url.startsWith('https://');
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤图片 URL 数组,移除不安全的 URL
|
||||
* @param urls 图片 URL 数组
|
||||
* @returns 安全的 URL 数组
|
||||
*/
|
||||
export function filterImageUrls(urls: string[] | any): string[] {
|
||||
if (!Array.isArray(urls)) {
|
||||
return [];
|
||||
}
|
||||
return urls.filter(url => isValidImageUrl(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* 截断字符串到指定长度,防止超出数据库字段长度限制
|
||||
* @param str 输入字符串
|
||||
* @param maxLength 最大长度
|
||||
* @returns 截断后的字符串
|
||||
*/
|
||||
export function truncateString(str: string, maxLength: number): string {
|
||||
if (!str || typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
return str.length > maxLength ? str.substring(0, maxLength) : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并规范化年份字段
|
||||
* 只允许 1800-当前年份+10 范围内的合理年份
|
||||
* @param year 输入年份
|
||||
* @returns 有效的年份数字或 null
|
||||
*/
|
||||
export function validateYear(year: any): number | null {
|
||||
if (year === null || year === undefined || year === '') {
|
||||
return null;
|
||||
}
|
||||
const num = parseInt(String(year), 10);
|
||||
if (isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
const currentYear = new Date().getFullYear();
|
||||
if (num < 1800 || num > currentYear + 10) {
|
||||
return null;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证评分字段(豆瓣评分、TMDB评分)
|
||||
* 只允许 0-10 范围内的数字,精度到小数点后1位
|
||||
* @param rating 输入评分
|
||||
* @returns 有效的评分数字或 null
|
||||
*/
|
||||
export function validateRating(rating: any): number | null {
|
||||
if (rating === null || rating === undefined || rating === '') {
|
||||
return null;
|
||||
}
|
||||
// 解析字符串格式的评分,如 "TMDB 7.4" / "豆瓣 8.0"
|
||||
if (typeof rating === 'string') {
|
||||
const match = rating.match(/(\d+\.?\d*)/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
rating = parseFloat(match[1]);
|
||||
}
|
||||
const num = parseFloat(String(rating));
|
||||
if (isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
if (num < 0 || num > 10) {
|
||||
return null;
|
||||
}
|
||||
return Math.round(num * 10) / 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证分类ID
|
||||
* 必须是正整数
|
||||
* @param categoryId 输入分类ID
|
||||
* @param defaultValue 默认值
|
||||
* @returns 有效的分类ID
|
||||
*/
|
||||
export function validateCategoryId(categoryId: any, defaultValue: number | null = null): number | null {
|
||||
if (categoryId === null || categoryId === undefined || categoryId === '') {
|
||||
return defaultValue;
|
||||
}
|
||||
const num = parseInt(String(categoryId), 10);
|
||||
if (isNaN(num) || num <= 0) {
|
||||
return defaultValue;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归转义对象中的所有字符串字段
|
||||
* @param obj 输入对象
|
||||
|
||||
Reference in New Issue
Block a user