1
0
Files
DiAL/scripts/build.ts
lanyuanxiaoyao 548b44d28e feat: 重构 Dashboard 为卡片式分组布局
表格布局替换为按分组展示的卡片式布局,新增 group 字段配置和 TargetBoard/TargetCard 等组件。模态框详情页支持时间范围筛选和分页,SummaryCards 减为 3 个。API 端点变更:trend/history 改用 from/to 参数,history 支持分页。recentSampleCount 硬编码为 30。
2026-05-11 08:54:21 +08:00

142 lines
4.4 KiB
TypeScript

import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
import { dirname, relative, sep } from "node:path";
import { fileURLToPath } from "node:url";
import { $ } from "bun";
const buildDir = fileURLToPath(new URL("../.build/", import.meta.url));
const webDistDir = fileURLToPath(new URL("../dist/web/", import.meta.url));
const executablePath = fileURLToPath(new URL("../dist/gateway-checker", import.meta.url));
const generatedAssetsPath = fileURLToPath(new URL("../.build/static-assets.ts", import.meta.url));
const generatedEntryPath = fileURLToPath(new URL("../.build/server-entry.ts", import.meta.url));
await rm(buildDir, { recursive: true, force: true });
await rm(executablePath, { force: true });
await mkdir(buildDir, { recursive: true });
await $`bunx --bun vite build`;
const files = await listFiles(webDistDir);
const indexPath = files.find((file) => normalize(relative(webDistDir, file)) === "index.html");
if (!indexPath) {
throw new Error("Vite build 未生成 dist/web/index.html");
}
const assetFiles = files.filter((file) => file !== indexPath);
await writeGeneratedAssets(indexPath, assetFiles);
await writeGeneratedEntry();
const target = process.env.BUN_TARGET ?? process.env.BUILD_TARGET;
const result = await Bun.build({
entrypoints: [generatedEntryPath],
compile: target
? {
target: target as Bun.Build.CompileTarget,
outfile: executablePath,
autoloadDotenv: true,
autoloadBunfig: true,
}
: {
outfile: executablePath,
autoloadDotenv: true,
autoloadBunfig: true,
},
minify: true,
sourcemap: "linked",
});
if (!result.success) {
await rm(executablePath, { force: true });
throw new Error("Bun executable 构建失败");
}
console.log(`Built executable: ${executablePath}`);
await rm(buildDir, { recursive: true, force: true });
async function listFiles(directory: string): Promise<string[]> {
const entries = await readdir(directory, { withFileTypes: true });
const files = await Promise.all(
entries.map(async (entry) => {
const path = `${directory.replace(/\/$/, "")}/${entry.name}`;
if (entry.isDirectory()) {
return listFiles(path);
}
return [path];
}),
);
return files.flat().sort((left, right) => normalize(left).localeCompare(normalize(right)));
}
async function writeGeneratedAssets(indexPath: string, assetFiles: string[]) {
const imports = [
`import type { StaticAssets } from "../src/server/app";`,
`import indexPath from "${toImportPath(indexPath)}" with { type: "file" };`,
...assetFiles.map((file, index) => `import asset${index}Path from "${toImportPath(file)}" with { type: "file" };`),
];
const assetEntries = assetFiles.map((file, index) => {
const urlPath = `/${normalize(relative(webDistDir, file))}`;
return ` ${JSON.stringify(urlPath)}: Bun.file(asset${index}Path),`;
});
const source = `${imports.join("\n")}
export const staticAssets: StaticAssets = {
indexHtml: Bun.file(indexPath),
files: {
${assetEntries.join("\n")}
},
};
`;
await mkdir(dirname(generatedAssetsPath), { recursive: true });
await writeFile(generatedAssetsPath, source);
}
async function writeGeneratedEntry() {
await writeFile(
generatedEntryPath,
`import { loadConfig } from "../src/server/checker/config-loader";
import { ProbeStore } from "../src/server/checker/store";
import { ProbeEngine } from "../src/server/checker/engine";
import { startServer } from "../src/server/server";
import { readRuntimeConfig } from "../src/server/config";
import { staticAssets } from "./static-assets";
async function main() {
const { configPath } = readRuntimeConfig();
const config = await loadConfig(configPath);
const store = new ProbeStore(config.dataDir + "/probe.db");
store.syncTargets(config.targets);
const engine = new ProbeEngine(store, config.targets, config.maxConcurrentChecks);
engine.start();
startServer({
config: { host: config.host, port: config.port },
mode: "production",
staticAssets,
store,
});
}
void main().catch((error) => {
console.error("启动失败:", error instanceof Error ? error.message : error);
process.exit(1);
});
`,
);
}
function toImportPath(path: string): string {
const rel = normalize(relative(buildDir, path));
return rel.startsWith(".") ? rel : `./${rel}`;
}
function normalize(path: string): string {
return path.split(sep).join("/");
}