初始化兜爸心选 App 官网项目(构建与发布)

- src/:从线上 shop.douba.top/public/app 拉取的页面源码(v0.3.8)
- 版本号、更新日期、安装包大小改为模板变量,统一由 site.config.json 管理,
  安装包大小在构建时从 APK 实际字节数计算
- scripts/build.mjs:构建到 dist/,校验 APK、模板变量与资源引用
- scripts/deploy.mjs:与线上比对差异 → 上传 → 备份 → 原子切换 → 线上验证
- scripts/rollback.mjs、scripts/preview.mjs:回滚与本地预览
- AGENTS.md:项目说明、发版流程与服务器/nginx 约定

由 AI 助手协助完成。
This commit is contained in:
douba
2026-10-29 06:20:35 +00:00
commit 72648b4914
29 changed files with 1899 additions and 0 deletions

28
scripts/preview.mjs Normal file
View File

@@ -0,0 +1,28 @@
// 本地预览 dist/:按线上路径挂在 /app 下(/app 和 /app/ 都返回首页)
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const DIST = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist');
const PORT = Number(process.env.PORT) || 8080;
const TYPES = {
'.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8',
'.svg': 'image/svg+xml', '.webp': 'image/webp', '.png': 'image/png', '.jpg': 'image/jpeg',
'.apk': 'application/vnd.android.package-archive',
};
http.createServer((req, res) => {
const url = decodeURIComponent(req.url.split('?')[0]);
if (url === '/') { res.writeHead(302, { Location: '/app' }); return res.end(); }
if (url !== '/app' && !url.startsWith('/app/')) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end(`本地预览只包含 /app,${url} 属于线上商城`);
}
let file = path.join(DIST, url.slice(4));
if (!file.startsWith(DIST)) { res.writeHead(403); return res.end(); }
if (fs.existsSync(file) && fs.statSync(file).isDirectory()) file = path.join(file, 'index.html');
if (!fs.existsSync(file)) { res.writeHead(404); return res.end('Not Found'); }
res.writeHead(200, { 'Content-Type': TYPES[path.extname(file)] || 'application/octet-stream' });
fs.createReadStream(file).pipe(res);
}).listen(PORT, () => console.log(`本地预览:http://localhost:${PORT}/app`));