feat: 同步服务托管网页应用,daywk 单域名部署就绪

This commit is contained in:
root
2026-10-16 12:05:47 +08:00
parent acfb0cfdd2
commit b73d5ef2cc
16 changed files with 188 additions and 3 deletions
+5
View File
@@ -35,6 +35,11 @@ Caddy 会自动为 daywk.linyux.cn 申请并续期 HTTPS 证书(需 DNS 已生
应返回 {"ok":true}。
## 重要:该域名同时是应用入口
同步服务容器同时托管了网页应用本体(daywork 仓库中的构建产物)。手机与电脑浏览器直接打开
https://daywk.linyux.cn 就是应用(可添加到主屏幕作为 PWA),无需另外部署前端;/api 路径为数据接口。
## 五、客户端
应用 → 设置 → 多端数据同步:地址填 https://daywk.linyux.cn,注册云端账号后同步。
+2 -1
View File
@@ -1,6 +1,7 @@
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package.json index.js ./
COPY server/package.json server/index.js ./
COPY intern-log/dist ./dist
EXPOSE 3780
CMD ["node", "index.js"]
+1
View File
@@ -0,0 +1 @@
{"secret":"ab5dd60e3a42a471a54c8db1176d09ddd65b2d60f42629b8ba94274341ae9dc4"}
+3 -1
View File
@@ -1,6 +1,8 @@
services:
internlog-sync:
build: .
build:
context: ..
dockerfile: server/Dockerfile
container_name: internlog-sync
restart: unless-stopped
volumes:
+37
View File
@@ -122,6 +122,22 @@ function readBody(req, limit = 20 * 1024 * 1024) {
})
}
// ---------- 静态托管网页应用 ----------
// 容器内为 /app/dist;本机直跑时回退到仓库内的 intern-log/dist
const DIST_CANDIDATES = [path.join(__dirname, 'dist'), path.join(__dirname, '..', 'intern-log', 'dist')]
const DIST_DIR = process.env.DIST_DIR || DIST_CANDIDATES.find(p => fs.existsSync(path.join(p, 'index.html'))) || path.join(__dirname, 'dist')
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript',
'.css': 'text/css',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.json': 'application/json',
'.webmanifest': 'application/manifest+json',
'.ico': 'image/x-icon',
'.woff2': 'font/woff2'
}
// ---------- 路由 ----------
async function handle(req, res) {
const url = new URL(req.url, 'http://localhost')
@@ -129,6 +145,27 @@ async function handle(req, res) {
if (req.method === 'OPTIONS') { json(res, 204, {}); return }
// 非 API 的 GET 请求:返回网页应用(构建产物)
if (req.method === 'GET' && !route.startsWith('/api')) {
let rel = decodeURIComponent(url.pathname)
if (rel === '/' || rel === '') rel = '/index.html'
const file = path.normalize(path.join(DIST_DIR, rel))
if (!file.startsWith(DIST_DIR)) { json(res, 403, { error: 'forbidden' }); return }
fs.readFile(file, (err, data) => {
if (err) {
fs.readFile(path.join(DIST_DIR, 'index.html'), (e2, d2) => {
if (e2) { json(res, 404, { error: 'not found' }); return }
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
res.end(d2)
})
return
}
res.writeHead(200, { 'Content-Type': MIME[path.extname(file).toLowerCase()] || 'application/octet-stream' })
res.end(data)
})
return
}
if (req.method === 'GET' && route === '/api/health') { json(res, 200, { ok: true }); return }
// SSE 事件流:token 经查询参数传递(EventSource 无法自定义请求头)