feat: 服务器迁移后的全量演进(依赖升级/安全审计/设计统一/截图/SEO 底座化)
在原仓库历史之上同步本服务器上的全部演进: - 迁移至新服务器并重建 PG 数据库 + ddnc 开发域名绑定 - 全部依赖升级至最新(Next 16.3.5 / React 19.3 / better-auth 1.7.5 / Drizzle 0.45 / TS 7), overrides 消除已知 CVE,安全审计 19 项发现全部修复闭环 - 设计统一:各站宽度 token(--container + site-container)、classic hero 重设计、 头部换行修复、页面过渡与 View Transitions - 截图功能:api.adx9.com 截图服务接入(ow=800 本地化 webp、cf=1、去重/限流/占位图) - SEO 底座化:canonical/hreflang/sitemap/JSON-LD/robots 全部收进 core,新站自动继承 - 加载体验:五站五形态路由骨架(data-theme 切换、shimmer 微光、延迟淡入)+ 过滤 useTransition 交叉淡入淡出(card-enter 错峰入场) - 构建:experimental.cpus 限流防 PG 连接打满;清理死组件链
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* # 脚本 capture-screenshots
|
||||
* 说明:批量预热工具网站截图 —— 调用截图 API 并本地化到
|
||||
* SCREENSHOT_STORAGE_DIR(默认 <cwd>/.screenshots)。
|
||||
* 工具详情页的 <img src="/api/screenshots/<slug>"> 命中时即可直接回本地文件。
|
||||
* 用法:bun scripts/capture-screenshots.ts [--force] [--limit N]
|
||||
* --force 忽略已有文件强制重截
|
||||
* --limit 最多处理 N 个工具(默认全部)
|
||||
*/
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import dotenv from "dotenv";
|
||||
import { resolve } from "path";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { tool } from "../src/lib/db/schema";
|
||||
import { ensureScreenshot, isValidSlug } from "../src/lib/screenshot";
|
||||
|
||||
dotenv.config({ path: resolve(process.cwd(), ".env.local") });
|
||||
|
||||
const force = process.argv.includes("--force");
|
||||
const limitIdx = process.argv.indexOf("--limit");
|
||||
const limit = limitIdx > -1 ? Number(process.argv[limitIdx + 1]) : Infinity;
|
||||
|
||||
const client = postgres(process.env.DATABASE_URL!, { prepare: false });
|
||||
const db = drizzle(client);
|
||||
|
||||
const tools = await db
|
||||
.select({ slug: tool.slug, name: tool.nameEn, websiteUrl: tool.websiteUrl })
|
||||
.from(tool)
|
||||
.where(eq(tool.status, "published"));
|
||||
|
||||
let ok = 0;
|
||||
let skip = 0;
|
||||
let fail = 0;
|
||||
let n = 0;
|
||||
|
||||
for (const t of tools) {
|
||||
if (n >= limit) break;
|
||||
n++;
|
||||
if (!t.websiteUrl) {
|
||||
skip++;
|
||||
console.log(`○ ${t.slug}: 无 websiteUrl,跳过`);
|
||||
continue;
|
||||
}
|
||||
if (!isValidSlug(t.slug)) {
|
||||
skip++;
|
||||
console.log(`○ ${t.slug}: slug 不合法,跳过`);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const started = Date.now();
|
||||
await ensureScreenshot(t.slug, t.websiteUrl, { force });
|
||||
ok++;
|
||||
console.log(`✓ ${t.slug} (${Date.now() - started}ms)`);
|
||||
} catch (err) {
|
||||
fail++;
|
||||
console.error(`✗ ${t.slug}:`, err instanceof Error ? err.message : err);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n完成:成功 ${ok} / 跳过 ${skip} / 失败 ${fail},共 ${n}`);
|
||||
client.end();
|
||||
process.exit(fail > 0 ? 1 : 0);
|
||||
Reference in New Issue
Block a user