feat: add admin accounts and image templates
This commit is contained in:
104
lib/server/image-template-input.ts
Normal file
104
lib/server/image-template-input.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { ImageTemplate } from "@/lib/types";
|
||||
|
||||
type ImageTemplateBody = {
|
||||
name?: unknown;
|
||||
description?: unknown;
|
||||
prompt?: unknown;
|
||||
previewImageUrl?: unknown;
|
||||
settings?: unknown;
|
||||
sortOrder?: unknown;
|
||||
};
|
||||
|
||||
type ImageTemplatePatch = Partial<Omit<ImageTemplate, "id" | "ownerId" | "createdAt" | "updatedAt">>;
|
||||
|
||||
export function normalizeImageTemplateCreate(body: ImageTemplateBody): Omit<ImageTemplate, "id" | "ownerId" | "createdAt" | "updatedAt"> {
|
||||
return {
|
||||
name: requiredText(body.name, "模板名称", 80),
|
||||
description: optionalText(body.description, 240),
|
||||
prompt: requiredText(body.prompt, "预设提示词", 4000),
|
||||
previewImageUrl: optionalPreviewUrl(body.previewImageUrl),
|
||||
settings: normalizeTemplateSettings(body.settings),
|
||||
sortOrder: optionalNumber(body.sortOrder) ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeImageTemplateUpdate(body: ImageTemplateBody): ImageTemplatePatch {
|
||||
const patch: ImageTemplatePatch = {};
|
||||
if ("name" in body) patch.name = requiredText(body.name, "模板名称", 80);
|
||||
if ("description" in body) patch.description = optionalText(body.description, 240);
|
||||
if ("prompt" in body) patch.prompt = requiredText(body.prompt, "预设提示词", 4000);
|
||||
if ("previewImageUrl" in body) patch.previewImageUrl = optionalPreviewUrl(body.previewImageUrl);
|
||||
if ("settings" in body) patch.settings = normalizeTemplateSettings(body.settings);
|
||||
if ("sortOrder" in body) patch.sortOrder = optionalNumber(body.sortOrder) ?? 0;
|
||||
return patch;
|
||||
}
|
||||
|
||||
function requiredText(value: unknown, label: string, maxLength: number): string {
|
||||
const text = optionalText(value, maxLength);
|
||||
if (!text) throw new Error(`${label}不能为空`);
|
||||
return text;
|
||||
}
|
||||
|
||||
function optionalText(value: unknown, maxLength: number): string | undefined {
|
||||
if (value === undefined || value === null) return undefined;
|
||||
const text = String(value).trim();
|
||||
if (!text) return undefined;
|
||||
return text.slice(0, maxLength);
|
||||
}
|
||||
|
||||
function optionalPreviewUrl(value: unknown): string | undefined {
|
||||
const url = optionalText(value, 1000);
|
||||
if (!url) return undefined;
|
||||
if (url.startsWith("/") || /^https?:\/\//i.test(url)) return url;
|
||||
throw new Error("效果预览图地址必须是 http(s) 或站内路径");
|
||||
}
|
||||
|
||||
function normalizeTemplateSettings(value: unknown): ImageTemplate["settings"] {
|
||||
if (!isRecord(value)) return {};
|
||||
const width = optionalPositiveInteger(value.width);
|
||||
const height = optionalPositiveInteger(value.height);
|
||||
const engine = normalizeImageEngine(value.engine);
|
||||
const scale = optionalScale(value.scale);
|
||||
const quality = normalizeQuality(value.quality);
|
||||
return {
|
||||
...(engine ? { engine } : {}),
|
||||
...(width && height ? { width, height } : {}),
|
||||
...(typeof value.forceSingle === "boolean" ? { forceSingle: value.forceSingle } : {}),
|
||||
...(scale !== undefined ? { scale } : {}),
|
||||
...(quality ? { quality } : {})
|
||||
};
|
||||
}
|
||||
|
||||
function optionalNumber(value: unknown): number | undefined {
|
||||
if (value === undefined || value === null || value === "") return undefined;
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? Math.trunc(parsed) : undefined;
|
||||
}
|
||||
|
||||
function optionalPositiveInteger(value: unknown): number | undefined {
|
||||
const parsed = optionalNumber(value);
|
||||
if (!parsed || parsed < 1) return undefined;
|
||||
return Math.min(parsed, 8192);
|
||||
}
|
||||
|
||||
function optionalScale(value: unknown): number | undefined {
|
||||
const parsed = optionalNumber(value);
|
||||
if (parsed === undefined) return undefined;
|
||||
return Math.max(1, Math.min(100, parsed));
|
||||
}
|
||||
|
||||
function normalizeImageEngine(value: unknown): ImageTemplate["settings"]["engine"] | undefined {
|
||||
const text = optionalText(value, 20);
|
||||
if (text === "jimeng" || text === "evolink") return text;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function normalizeQuality(value: unknown): ImageTemplate["settings"]["quality"] | undefined {
|
||||
const text = optionalText(value, 20);
|
||||
if (text === "low" || text === "medium" || text === "high") return text;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
Reference in New Issue
Block a user