Skip to content

Latest commit

 

History

History
184 lines (143 loc) · 19.7 KB

File metadata and controls

184 lines (143 loc) · 19.7 KB
UnUseDomain

UnUseDomain

部署在 Cloudflare Workers 上的域名展示与管理系统

线上站点 Cloudflare Workers React TypeScript Hono

公开站点用于检索、筛选、速览和访问已上架域名;管理后台负责域名、分类、CSV、站点设置、到期提醒、通知、安全与审计日志。前端、Worker API、Cloudflare D1 与 R2 同源部署。

线上环境

  • 生产站点:https://unusedomain.com
  • 管理后台:https://unusedomain.com/admin
  • Worker:wanmi(为避免新建 Worker 后丢失现有 Secrets 与自定义域名绑定,保留此历史生产资源 ID)
  • D1:wanmi-db(绑定 DB,保留现有生产数据)
  • R2:wanmi-assets(绑定 UPLOADS,保留现有上传文件)
  • Cron:每天 01:00 UTC,即 Asia/Shanghai 09:00
  • 当前设计基线:Elegant Green Gold 雅致绿金主题(2026-07-20,由浅色暖金改版)——墨绿为主、暖白为底、香槟金点缀;域名简介由 0031 迁移一次性批量写入,后续为纯手动维护(站内 AI 生成能力已于 2026-07-17 移除)

权威业务源为 data/source/UnUseDomain.csv:859 条有效唯一记录。生产库还保留 3 条历史人工域名,因此线上合计 862 条;迁移与发布不得覆盖或归档这 3 条记录。

品牌与图标

  • 对外品牌、网页标题、管理后台、通知和下载文件名统一使用 UnUseDomain;生产域名统一为 unusedomain.com
  • 版权文字统一为 © 2026 UnUseDomain(0029 迁移去掉了原先的 . All rights reserved. 后缀)。
  • 品牌标志为「对分圆环」:圆环沿竖轴对分,左半石墨灰 #3B3E47、右半靛蓝 #233393,固定配色,不随 accent_color 变化。
  • public/ 下全部图标由同一张 1092×1092 透明原图确定性生成,分两组:unusedomain-logo.png(512)、favicon-16/32/48.png 与含 6 档条目的 favicon.ico透明底,供页内品牌标与浏览器标签使用;apple-touch-icon.png(180)、icon-192.pngicon-512.png白底,因为 iOS 会把透明合成为黑色、PWA purpose: "any maskable" 需要不透明底并把内容收进 80% 直径的安全区。
  • 站点设置通过品牌迁移指向这些静态资源;后台上传的 logo_url/favicon_url 优先级高于静态默认值。
  • Cookie、浏览器存储键、缓存标记与诊断 Header 使用 unusedomain 命名;品牌迁移会使旧管理会话失效一次,需要重新登录。

产品边界

当前产品明确不包含以下能力:

  • 求购表单、报价、线索存储及相关 API;
  • DNS 查询、缓存、写入和管理界面;
  • 注册商账户、凭据、同步及服务商适配器。

域名可保留 registrar_name 纯文字资料,用于记录该域名的注册商名称,但它不关联账户或外部 API。旧版相关端点均返回 404;0016_remove_registrar_dns_leads.sql 会删除历史表与关联字段,同时保留域名、分类、通知历史和注册商文字资料。

设计系统

  • 提供浅色(默认)与深色两套「雅致绿金」主题。深色主背景 oklch(0.16 0.02 150),侧栏比页面更深以保持层次。
  • 主题在 index.html 的同步脚本里于首帧前定下:读 localStorage["unusedomain-theme"],没存过就跟随系统,然后把显式值写到 <html data-theme> 并同步 theme-color。放到 React 里读会先按浅色画一帧再翻深色,深色用户每次进站都闪一下白。
  • 根目录 tokens.css 是唯一设计令牌源;组件与运行时样式只引用 CSS 变量,不硬编码色值。色值统一用 oklch:感知均匀,派生 hover/bg/border 只需动 L 与 C,不会串色相。
  • 深色分支为 :root[data-theme="dark"],只覆盖语义色,不动结构/间距/圆角/动效。两处语义在深色下反转:--brand-primary 提亮(否则按钮糊进深底),--brand-primary-ink 由「最深的绿」变为「最亮的绿」(否则精品域名名看不见)。
  • --muted--sidebar-muted--radiusapp.css 的别名层定义,tokens.css 不重名定义,避免同名不同义。
  • 暖白背景 #f6f5f0、奶油白卡片 #fefdfa,品牌主色为深墨绿 #133429(hover #1e6b59),香槟金 #c89848 仅表达精品与重点资产(占比 ≤2%),浅底金色小字一律 --gold-text #79551f
  • 主、次、三级文字依次使用 #1f2a24#5d675f#7e877f;卡片阴影统一为绿倾向低透明度令牌,禁止纯黑阴影。
  • 站点设置 accent_colorapplyAccentColor 整套派生(主色/hover/bg/border/ring 同步),历史默认值视为未定制。
  • 动效常量单一来源:CSS 读 tokens.css--dur-*/--ease,JS 驱动的动画(KPI count-up、图表首绘)读 src/client/lib/motion.ts 的同名常量,两边数值一一对应。不引入 framer-motion——本项目的动画都能用 CSS transform/opacity 表达,为此多打 40KB gzip 不划算。
  • prefers-reduced-motion 除各处具名规则外,app.css 末尾另有一条覆盖全部元素与伪元素的兜底(animation-duration: 0.01ms 而非 none,保证动画终态仍被应用)。逐个补规则漏一个不会有任何报错,只有相关用户能感觉到。
  • 保留 prefers-reduced-transparency 退化;完整规范见 DESIGN.md

公开站点

  • 搜索与筛选:关键词、分类(下拉含全部/精品/各分类及计数)、后缀、位数(全部、1–9 位与 10 位以上,驱动 minLength/maxLength),状态同步到 URL;高级筛选面板已移除,contains/excludes/kind 与旧链接的 group=two/three 仍可通过 URL 直传兼容。
  • 默认排序(publicDefaultOrderSql):精品优先,且两组各按各的规则——精品组只按位数升序(短的在前),普通组只按后缀优先级(com/cn/net/org/io/is/do/其他)。两个排序键都用 CASE WHEN is_featured 兜住,不属于本组的行取 NULL;因为 is_featured DESC 已把两组彻底分开,各自的 NULL 不会干扰另一组的组内次序。
  • 首页无 Hero:页首(品牌 Logo + 右侧域名总数与「后台」入口)之下直接是搜索与域名列表;无独立精选区块、无分类 pills 行、无「全部资产」标题行;精品域名以主列表卡片上的精品标记区分。页脚不再有管理入口。
  • 页脚为三栏:左侧友情链接、中间品牌 Logo 与版权、右侧联系方式小图标(原先在页首右上角,已整体下移)。三栏的列号写死为 1/2/3,左右两列同为 1fr,因此没有友情链接时中栏仍落在页脚正中、联系方式仍靠右,不会因为少一个子元素而顺次前移。窄屏(≤900px)沿用同一套列模板,靠压缩内容让位:带 LOGO 的友链收成纯图标、中间只留品牌 Logo 不显示版权文字,联系图标保持一排。
  • 搜索按钮固定为右侧 72px 紧凑按钮,不再拉伸占据工具栏。
  • 电脑版域名卡片徽章行右侧仅保留复制、速览两枚图标;无可见中文按钮文字,也没有“我想要”。
  • 前台界面文案全部为英文(搜索、筛选、分页、速览弹窗、精品详情页与 SSR、页面标题与 OG 描述);分类值仍以中文存库、后台照常中文管理,前台由 src/client/lib/category-label.ts 做展示层映射(纯字母→Letters、三拼→3 Pinyin…),未覆盖的新分类原样透出。拼音含义模块保留汉字释义——那是内容而非界面语言。
  • 站名(页首品牌名与页脚版权)走文字流光:background-clip: text + 墨绿→香槟金→墨绿的渐变横向循环,prefers-reduced-motion 下停到静止渐变。域名卡片不做流光。
  • 卡片高约 150px:简介为空时整段不渲染(不再留占位行),内边距与行距同步收紧;徽章行为「精品星标 + 后缀 + 域龄」,分类徽章已移除,精品仅由星标表达、域名文字用深墨绿加重(不再用金色,在浅底上更稳)。域龄由 registered_at 折算整年,显示为 17 Years(数字与单位之间留一个空格,不带 Age 前缀),满 10 年转金色;剩余有效期显示为 N Days
  • 手机(≤720px)走紧凑列表,一条域名条只有域名、单行简介和查看按钮:后缀徽章、剩余天数与复制按钮全部收起,查看按钮贴右侧并与域名齐平。简介为空时不撑第二行、也不留占位。实现上靠徽章行 display: contents 把查看按钮直接提到域名同一行,第二行只在 :has(.domain-description) 命中时才出现。
  • 手机列表一行放两条域名条,含后缀超过 15 字符的域名(DomainCard 打上 full-row)占满整行。靠 grid-auto-flow: row dense 让后面的短域名回填长域名跳过的半格,不必在 JS 里重排列表——实测不会留下任何孤零零的半格。半格只有约 110px 放域名,15 字符在 14px 字号下最宽 106px 刚好一行读完,这个字号是被阈值定死的;同时要关掉 strong.domain-tld 各自带的 white-space: normaloverflow-wrap: anywhere,否则后缀会被甩到第二行。整行的那条仍用 20px 与 34px 按钮。
  • 手机筛选栏四项并成一行。.catalogue-toolbar 是 flex,.toolbar-controls 不给 flex: 1 就只有内容宽(约 280px),四项挤不进去。
  • 点击域名名称直接在新标签页访问对应域名;站内保留原生 dialog 速览,精品域名速览额外提供独立详情页入口。
  • 最近搜索仅保存在当前浏览器的版本化 localStorage 中;收藏功能已下线。
  • 单一雅致绿金主题、随机发现、相似域名、桌面/平板/手机响应式布局与无障碍标签。
  • Worker 为首页注入 canonical、Open Graph 和真实域名 ItemList JSON-LD。
  • 87 个精品域名拥有 Worker SSR 独立页 /d/:domain,包含完整资料、同后缀/同长度推荐、Open Graph 与 Product JSON-LD;普通域名访问该路径时重定向到 /domains?q=域名
  • /api/public/og/:domain 为精品域名生成 1200×630 墨绿底香槟金字 PNG;sitemap.xml 从 D1 动态输出首页和全部 87 个精品详情页。

性能策略

  • 样式按域拆分:app.css 为前台与共享层,admin.css 为后台专属层,随已懒加载的 AdminApp 按需加载,不进前台首屏(前台 CSS 135.93KB → 108.66KB,gzip 23.54 → 19.19KB)。
  • 全部字体自托管,Google Fonts 在中国大陆不可达,外链会让国内用户完全拿不到。分两组:
    • src/client/styles/fonts.csspnpm fonts:fetch 生成(Manrope、IBM Plex Mono 等 OFL 字体的 latin/latin-ext 子集),不要手改
    • src/client/styles/brand-fonts.css 只放英文 Averia Serif Libre Light(OFL,37KB),随前台加载。
    • 中文仓耳华新体(仓耳字库商业授权,站点所有者已确认取得网页嵌入授权;原始 TTF 不入库)的 @font-face 放在 admin.css只随后台加载。两份都由 pnpm fonts:brand -- --cjk <中文TTF> --latin <拉丁TTF> 生成。
  • 中文字体必须子集化:全字库 28565 字、17MB,按「源码里出现的汉字 + GB2312 一级字库 3755 常用字 + 常用标点」裁到约 815KB。
  • 中文字体不进前台:前台文案与库内简介均已英文化(0030_english_descriptions.sql 翻掉了仅有的两条中文简介),前台不再出现汉字,815KB 的中文子集只随 admin.css 进后台。此前它会因为一条中文简介被整体拉下来,字体换上时整屏文字跳一次——即用户可见的"刷新闪动"。若日后前台又出现中文(例如友情链接名),按 tokens.css 的系统中文字体栈渲染,不会再触发下载。
  • 边缘缓存键除 public_data_version 外还带构建标识 __bvvite.config.tsdefine 注入,CI 用提交 SHA):数据版本只在数据改动时自增,改排序、序列化这类纯代码逻辑不会碰它,没有构建标识时部署后仍会命中旧响应直到 s-maxage 到期。
  • 拆分依据是真实页面的 DOM 探测而非命名前缀:只有「前台完全不匹配、仅后台匹配」的选择器才进 admin.css。实测前后台共享选择器仅 19 个(html/body/button/.brand/.secondary-button/.pagination 等),全部留在 app.css;两个文件选择器不重叠,因此 admin.css 的加载顺序不影响前台层叠。注意 .admin-link 是前台页脚的管理入口,不可按前缀误判为后台样式。
  • 后台域名列表每页 100 条服务端分页,滚动到底自动累积下一页;桌面按视口虚拟化渲染,行高由首行实测得出。
  • 720px 以下表格切换为卡片式布局、行高不定,此时关闭虚拟化直接整段渲染;宽表格只在自身容器内横向滚动,页面本身不横向滚动。
  • 后台搜索输入去抖 300ms,避免逐字符重置累积列表。
  • 公开列表默认每页 36 条,服务端分页;不预取下一页。
  • 最近请求缓存最多 20 项,失败请求自动移出缓存。
  • 数据版本检查从 8 秒降为 60 秒,并且只在页面可见时执行;后台修改可通过刷新立即读取。
  • 卡片使用 React.memocontent-visibility 与布局隔离;关闭卡片入场动画、重阴影、位移悬停和移动底栏模糊。
  • 管理后台按路由懒加载,不进入公开首页首包。

管理后台

后台固定为七个模块:概览、域名、分类、站点设置、到期提醒、账户安全、操作日志。

  • PBKDF2 密码、HMAC 会话、CSRF、防暴力登录、会话撤销与改密;
  • 域名 CRUD、服务端筛选/排序、可选简介(纯手动填写)、生命周期文字资料、精品、上架状态和批量操作;
  • 批量精品、上架、隐藏和删除走统一确认弹窗,显示选中数量与即将执行的动作;批量设置分类可选现有分类或新建;选中数超过后端 500 个 id 的上限时前端自动分批提交;切换筛选条件会清空选择,避免误伤当前看不见的域名;
  • CSV 导入先 dry-run 预览:展示将新增、已存在、字段冲突、无效与文件内重复的条数,并按字段列出会被改写的新旧值;dry-run 不写数据库,确认后才执行真实导入;
  • 操作日志顶部提供近 7 天操作趋势折线图,并按创建/更新/删除/批量分组计数;
  • CSV 预览、跳过/更新冲突及导入错误下载;
  • 站点资料和 R2 图片;
  • 站点设置页内的友情链接管理:增删多条,每条可改名称、地址、LOGO 地址、排序,以及显示形式(LOGO + 文字 / 仅 LOGO / 仅文字),保存后即时反映到前台页脚左栏;
  • Email、Telegram、Bark、Server酱、企业微信、飞书、Discord 通知;
  • 操作日志筛选、分页与 CSV 导出。

技术栈

React 19、TypeScript 6、Vite 8、Cloudflare Vite Plugin、Hono、Workers Static Assets、D1、R2、Cron Triggers、Zod、Vitest 和 Playwright。

本地开发

要求 Node.js 22+ 与 pnpm 10+。

pnpm install
cp .dev.vars.example .dev.vars
pnpm db:migrate:local
pnpm domains:validate
pnpm domains:import:local -- --dry-run
pnpm domains:import:local
pnpm dev --host 127.0.0.1

本地 Vite 默认关闭 Cloudflare 远程 binding 代理,避免开发与 CI 依赖线上凭据。

.dev.vars 必须提供 ADMIN_EMAILBOOTSTRAP_ADMIN_PASSWORDSESSION_SECRETCREDENTIALS_ENCRYPTION_KEY,且已被 Git 忽略。首次使用某个管理员邮箱登录时才会创建该账号;部署不会重置已有密码。

验证与部署

pnpm check
pnpm test:e2e

pnpm exec wrangler whoami
pnpm db:backup
pnpm db:migrate:remote
pnpm run deploy
pnpm verify:production

pnpm check 包含类型检查、ESLint、单元/集成测试、生产构建、859 条本地域名验证和生产文件扫描。生产变更顺序固定为:完整 D1 备份 → 检查待执行迁移 → 远程迁移 → 部署 → API、桌面、手机和后台验收。

本轮关键迁移:

  • 0015_restore_domain_management_schema.sql:仅作为历史列名兼容桥,为全新安装准备临时文字列并回填权威 CSV;不会恢复被移除的业务表。
  • 0016_remove_registrar_dns_leads.sql:兼容历史线上和全新安装两种 schema,规范化 registrar_name,重建导入暂存表,删除注册商账户、DNS 和求购线索表,并保护分类与通知历史。
  • 0017_domain_keywords_field.sql:为域名和导入暂存记录增加逗号分隔关键词,按中英文逗号与顿号迁移有效简介,同时完整保留原 description
  • 0026_unusedomain_rebrand.sql:统一生产站点名称、Slogan、版权、Logo 与 Favicon,同时保留联系方式、主题色、展示密度和其他管理员设置。
  • 0028_friend_links.sql:新建 friend_links 表承载页脚友情链接(名称、地址、LOGO、显示形式、排序),并写入首条「大佬论坛」;只新增表,不触碰既有数据。
  • 0029_footer_copyright_trim.sql:把页脚版权的 . All rights reserved. 后缀去掉,只保留 © 2026 UnUseDomain;用 REPLACE 而非整句覆盖,后台改过前半段时不会被抹掉。
  • 0030_english_descriptions.sql:把全站仅有的两条中文简介(mx.ooonamesale.cn)翻成英文,使前台彻底无汉字;以当前值为条件,后台改过则跳过,不覆盖人工编辑。
  • 0031_domain_descriptions.sql:为其余 865 条域名批量写入英文简介(初版整句,已由 0032 压短)。一律英文——中文简介会把 815KB 字体子集拉回前台首屏,正是之前专门优化掉的问题。每条 UPDATE 都带 description 为空的条件,后台已填写的整句跳过;domains 上的 AFTER UPDATE 触发器自增 public_data_version,边缘缓存随即换键。简介内容按域名类型分组撰写:数字米讲数字构成与中文彩头,拼音米点出对应汉义,字母米说明缩写来源,英文米直述词义。
  • 0032_short_domain_descriptions.sql:把上一条的整句简介压到 10–20 字符的标签式短语。卡片与手机域名条都是单行截断,电脑卡片一行只容得下约 42 字符,整句版本仅 14% 能显示完整;改成短语后全部一行显示完,平均 17 字符。以 0031 的原值为条件,后台改过则跳过。

所有远程 Token 只能通过环境变量、CI Secret 或交互式输入提供。不得写入 README、.dev.vars.example、Wrangler 配置、构建产物、Issue 或日志;聊天中暴露过的长期凭据应在发布后轮换。

目录

src/client/            React 公开站点与管理后台
src/worker/            Hono Worker、认证、API 与通知服务
src/shared/            CSV、导入计划、Schema 和共享类型
scripts/               数据导入、验证、备份与安全扫描
migrations/            D1 migrations
data/source/           权威 CSV
tests/                 单元、集成与 Playwright E2E(含 22 张视觉基线截图)
docs/                  部署、导入与安全文档

详细说明见 Cloudflare 部署域名导入安全设计设计规范HANDOFF