105 lines
2.8 KiB
TypeScript
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)}`;
|
|
}
|