perf: rewrite agent workflow preview urls at 230px
This commit is contained in:
parent
4d86b15781
commit
38cc12da8e
@ -34,6 +34,7 @@
|
||||
"smoke:routes": "node scripts/smoke-routes.mjs",
|
||||
"verify:release": "node scripts/verify-release.mjs",
|
||||
"backfill:agent-preview-images": "pnpm exec tsx scripts/backfill-agent-workflow-preview-images.ts",
|
||||
"rewrite:agent-preview-image-urls": "pnpm exec tsx scripts/rewrite-agent-workflow-preview-image-urls.ts",
|
||||
"format": "pnpm exec ultracite fix",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "npx tsx lib/db/migrate.ts",
|
||||
|
||||
191
scripts/rewrite-agent-workflow-preview-image-urls.ts
Normal file
191
scripts/rewrite-agent-workflow-preview-image-urls.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import { config as loadEnv } from "dotenv";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import Module from "node:module";
|
||||
import path from "node:path";
|
||||
|
||||
loadEnv({ path: ".env.local" });
|
||||
|
||||
const moduleLoader = Module as typeof Module & {
|
||||
_load: (request: string, parent: NodeModule | null, isMain: boolean) => unknown;
|
||||
};
|
||||
const originalLoad = moduleLoader._load;
|
||||
moduleLoader._load = function patchedServerOnlyLoad(
|
||||
this: typeof moduleLoader,
|
||||
request,
|
||||
parent,
|
||||
isMain
|
||||
) {
|
||||
if (request === "server-only") {
|
||||
return {};
|
||||
}
|
||||
return originalLoad.call(this, request, parent, isMain);
|
||||
};
|
||||
|
||||
const PREVIEW_WIDTH = 230;
|
||||
const PREVIEW_QUALITY = 76;
|
||||
const dryRun = process.argv.includes("--dry-run");
|
||||
|
||||
function normalizeUrl(value: unknown): string {
|
||||
return String(value || "").trim();
|
||||
}
|
||||
|
||||
function isAbsoluteHttpUrl(value: string) {
|
||||
return /^https?:\/\//i.test(value);
|
||||
}
|
||||
|
||||
function isUnsupportedPreviewSource(value: string) {
|
||||
return !value || value.startsWith("data:") || value.startsWith("blob:");
|
||||
}
|
||||
|
||||
function withoutKnownImageProcess(url: URL) {
|
||||
const next = new URL(url.toString());
|
||||
next.searchParams.delete("x-oss-process");
|
||||
const rawSearch = next.search.replace(/^\?/, "");
|
||||
if (/^imageView2\//i.test(rawSearch)) {
|
||||
next.search = "";
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
function buildOptimizedPreviewUrl(source: string): string | null {
|
||||
const normalized = normalizeUrl(source);
|
||||
if (!isAbsoluteHttpUrl(normalized) || isUnsupportedPreviewSource(normalized)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(normalized);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
const host = url.hostname.toLowerCase();
|
||||
const sourceUrl = withoutKnownImageProcess(url);
|
||||
|
||||
if (host.includes("aliyuncs.com") || host.includes("oss-")) {
|
||||
sourceUrl.searchParams.set(
|
||||
"x-oss-process",
|
||||
`image/resize,w_${PREVIEW_WIDTH}/quality,q_${PREVIEW_QUALITY}/format,webp`
|
||||
);
|
||||
return sourceUrl.toString();
|
||||
}
|
||||
|
||||
if (host.includes("qiniu") || host.includes("clouddn") || host.includes("qiniucdn")) {
|
||||
const separator = sourceUrl.search ? "&" : "?";
|
||||
return `${sourceUrl.toString()}${separator}imageView2/2/w/${PREVIEW_WIDTH}/q/${PREVIEW_QUALITY}/format/webp`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function visitOptions(options: any[], visitor: (option: any) => void) {
|
||||
for (const option of options || []) {
|
||||
visitor(option);
|
||||
visitOptions(option.children || [], visitor);
|
||||
}
|
||||
}
|
||||
|
||||
function visitSteps(steps: any[], visitor: (option: any) => void) {
|
||||
for (const step of steps || []) {
|
||||
visitOptions(step.options || [], visitor);
|
||||
visitSteps(step.children || [], visitor);
|
||||
}
|
||||
}
|
||||
|
||||
function createBackupPath() {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
return path.join(
|
||||
process.cwd(),
|
||||
"backups",
|
||||
`agent-workflow-ui-config-before-url-rewrite-${timestamp}.json`
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const dbModule = await import("../lib/db");
|
||||
const schemaModule = await import("../lib/db/schema");
|
||||
const workflowConfig = await import("../lib/agents/workflow-ui-config");
|
||||
const { db } = dbModule;
|
||||
const { systemSettings } = schemaModule;
|
||||
|
||||
const [setting] = await db
|
||||
.select({ value: systemSettings.value })
|
||||
.from(systemSettings)
|
||||
.where(eq(systemSettings.key, workflowConfig.AGENT_WORKFLOW_UI_CONFIG_KEY))
|
||||
.limit(1);
|
||||
|
||||
if (!setting?.value) {
|
||||
console.log("No agent workflow UI config found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = workflowConfig.normalizeAgentWorkflowUiConfig(setting.value);
|
||||
const nextConfig = structuredClone(normalized);
|
||||
const summary = {
|
||||
total: 0,
|
||||
rewritten: 0,
|
||||
skipped: 0,
|
||||
unsupported: 0,
|
||||
};
|
||||
|
||||
visitSteps(nextConfig.steps || [], (option) => {
|
||||
summary.total += 1;
|
||||
const currentPreview = normalizeUrl(option.previewImageUrl);
|
||||
const source = normalizeUrl(option.previewImageSourceUrl) || currentPreview;
|
||||
const optimized = buildOptimizedPreviewUrl(source);
|
||||
if (!currentPreview || !source) {
|
||||
summary.skipped += 1;
|
||||
return;
|
||||
}
|
||||
if (!optimized) {
|
||||
summary.unsupported += 1;
|
||||
return;
|
||||
}
|
||||
if (currentPreview === optimized && option.previewImageSourceUrl === source) {
|
||||
summary.skipped += 1;
|
||||
return;
|
||||
}
|
||||
option.previewImageUrl = optimized;
|
||||
option.previewImageSourceUrl = source;
|
||||
summary.rewritten += 1;
|
||||
});
|
||||
|
||||
console.log(JSON.stringify({ dryRun, summary }, null, 2));
|
||||
|
||||
if (dryRun || summary.rewritten === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const backupPath = createBackupPath();
|
||||
await mkdir(path.dirname(backupPath), { recursive: true });
|
||||
await writeFile(
|
||||
backupPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
key: workflowConfig.AGENT_WORKFLOW_UI_CONFIG_KEY,
|
||||
backedUpAt: new Date().toISOString(),
|
||||
value: setting.value,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
console.log(`Backup written: ${backupPath}`);
|
||||
|
||||
await db
|
||||
.update(systemSettings)
|
||||
.set({
|
||||
value: nextConfig,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(systemSettings.key, workflowConfig.AGENT_WORKFLOW_UI_CONFIG_KEY));
|
||||
console.log(`Rewritten ${summary.rewritten} agent workflow preview image URLs.`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user