Your Name b4698be089
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: add external image generation api
2026-05-18 15:56:08 +08:00

105 lines
2.8 KiB
TypeScript

export const EXTERNAL_IMAGE_API_CONFIG_KEY = "external_image_api_config";
export type ExternalImageApiAccessKey = {
id: string;
name: string;
apiKey: string;
enabled: boolean;
notes: string;
};
export type ExternalImageApiConfig = {
enabled: boolean;
defaultOwnerEmail: string;
apiKeys: ExternalImageApiAccessKey[];
};
export const DEFAULT_EXTERNAL_IMAGE_API_OWNER_EMAIL =
"external-image-api@system.local";
export const defaultExternalImageApiConfig: ExternalImageApiConfig = {
enabled: false,
defaultOwnerEmail: DEFAULT_EXTERNAL_IMAGE_API_OWNER_EMAIL,
apiKeys: [],
};
function normalizeText(value: unknown) {
return String(value || "").replace(/[\u0000-\u001F\u007F]/g, "").trim();
}
function normalizeEmail(value: unknown) {
const normalized = normalizeText(value).toLowerCase();
if (!normalized) return DEFAULT_EXTERNAL_IMAGE_API_OWNER_EMAIL;
return normalized;
}
function normalizeKeyId(value: unknown, fallbackIndex: number) {
const normalized = normalizeText(value)
.toLowerCase()
.replace(/[^a-z0-9_-]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
return normalized || `external-key-${fallbackIndex + 1}`;
}
function normalizeAccessKey(
value: unknown,
index: number,
): ExternalImageApiAccessKey | null {
if (!value || typeof value !== "object") return null;
const raw = value as Partial<ExternalImageApiAccessKey>;
const apiKey = normalizeText(raw.apiKey);
if (!apiKey) return null;
return {
id: normalizeKeyId(raw.id, index),
name: normalizeText(raw.name) || `第三方调用 Key ${index + 1}`,
apiKey,
enabled: raw.enabled !== false,
notes: normalizeText(raw.notes),
};
}
export function normalizeExternalImageApiConfig(
value: unknown,
): ExternalImageApiConfig {
if (!value || typeof value !== "object") {
return defaultExternalImageApiConfig;
}
const raw = value as Partial<ExternalImageApiConfig> & {
apiKeys?: unknown[];
};
const seenIds = new Set<string>();
const apiKeys = Array.isArray(raw.apiKeys)
? raw.apiKeys
.map((item, index) => normalizeAccessKey(item, index))
.filter((item): item is ExternalImageApiAccessKey => Boolean(item))
.map((item, index) => {
let nextId = item.id || `external-key-${index + 1}`;
while (seenIds.has(nextId)) {
nextId = `${nextId}-${seenIds.size + 1}`;
}
seenIds.add(nextId);
return {
...item,
id: nextId,
};
})
: [];
return {
enabled: raw.enabled === true,
defaultOwnerEmail: normalizeEmail(raw.defaultOwnerEmail),
apiKeys,
};
}
export function maskExternalImageApiKey(apiKey: string) {
const trimmed = normalizeText(apiKey);
if (trimmed.length <= 8) return trimmed ? "********" : "";
return `${trimmed.slice(0, 4)}********${trimmed.slice(-4)}`;
}