From ed14361e7b5629f3178c8a81ba001a503d1f1a94 Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Sun, 21 Jun 2026 23:58:29 +0800 Subject: [PATCH] 2358 --- write-server/Dockerfile | 9 +++-- write-server/docker-compose.yml | 1 + write-server/entrypoint.sh | 7 ++++ write-server/nginx/login.html | 18 +++++++--- write-server/src/lib/bot/poll.ts | 57 ++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/write-server/Dockerfile b/write-server/Dockerfile index d4406aab..67b9bd61 100644 --- a/write-server/Dockerfile +++ b/write-server/Dockerfile @@ -35,8 +35,13 @@ RUN npm run build FROM node:20-alpine AS runner WORKDIR /app -# Install Hugo and su-exec (for entrypoint) -RUN apk add --no-cache hugo su-exec +# Install specific Hugo version, git and su-exec +ARG HUGO_VERSION=0.128.2 +RUN wget -q https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz -O /tmp/hugo.tar.gz && \ + tar -xzf /tmp/hugo.tar.gz -C /tmp && \ + mv /tmp/hugo /usr/local/bin/hugo && \ + rm /tmp/hugo.tar.gz && \ + apk add --no-cache git su-exec # Create non-root user RUN addgroup --system --gid 1001 nodejs && \ diff --git a/write-server/docker-compose.yml b/write-server/docker-compose.yml index 271871d3..cde41113 100644 --- a/write-server/docker-compose.yml +++ b/write-server/docker-compose.yml @@ -12,6 +12,7 @@ services: restart: unless-stopped ports: - "8016:8016" + - "1313:1313" volumes: # Hugo 博客目录(必须)- 映射宿主机博客根目录到容器内 /blog - ..:/blog diff --git a/write-server/entrypoint.sh b/write-server/entrypoint.sh index 614940f8..39f82270 100644 --- a/write-server/entrypoint.sh +++ b/write-server/entrypoint.sh @@ -15,9 +15,16 @@ fi if [ -d "/blog/static" ]; then chown -R nextjs:nodejs /blog/static fi +if [ -d "/blog/.git" ]; then + chown -R nextjs:nodejs /blog/.git +fi if [ -f "/blog/hugo.toml" ]; then chown nextjs:nodejs /blog/hugo.toml fi +# 配置 git(容器内 nextjs 用户) +su-exec nextjs git config --global user.name "Write Server" 2>/dev/null +su-exec nextjs git config --global user.email "write@usj.cc" 2>/dev/null + # 以 nextjs 用户启动应用 exec su-exec nextjs "$@" diff --git a/write-server/nginx/login.html b/write-server/nginx/login.html index cdfcb8c8..16fada33 100644 --- a/write-server/nginx/login.html +++ b/write-server/nginx/login.html @@ -95,10 +95,20 @@ document.getElementById('form').addEventListener('submit', async function(e) { } }); -// 已登录则直接跳转 -if (localStorage.getItem('wauth')) { - location.href = '/'; -} +// 已登录则验证后跳转 +(function(){ + var tok = localStorage.getItem('wauth'); + if (!tok) return; + fetch('/auth/check', { headers: { 'Authorization': 'Basic ' + tok } }).then(function(r) { + if (r.ok) { + location.href = '/'; + } else { + localStorage.removeItem('wauth'); + } + }).catch(function() { + localStorage.removeItem('wauth'); + }); +})(); diff --git a/write-server/src/lib/bot/poll.ts b/write-server/src/lib/bot/poll.ts index 455d1ad4..203b333c 100644 --- a/write-server/src/lib/bot/poll.ts +++ b/write-server/src/lib/bot/poll.ts @@ -70,6 +70,9 @@ export async function startPolling(): Promise { const msg = update.message as { chat?: { id: number }; photo?: { file_id: string }[]; + document?: { file_id: string; file_name?: string; mime_type?: string }; + animation?: { file_id: string; file_name?: string; mime_type?: string }; + video?: { file_id: string; file_name?: string }; text?: string; caption?: string; reply_to_message?: { text?: string }; @@ -78,11 +81,21 @@ export async function startPolling(): Promise { const chatId = msg.chat.id; if (!allowed(chatId)) continue; + // 处理图片 if (msg.photo && hasSession(chatId)) { await handlePhoto(chatId, msg.photo, msg.caption); continue; } + // 处理 GIF / 文档 / 视频 + if ((msg.animation || msg.document || msg.video) && hasSession(chatId)) { + const file = msg.animation || msg.document || msg.video; + if (file) { + await handleDocument(chatId, file.file_id, file.file_name, msg.caption); + } + continue; + } + if (!msg.text) continue; const fullText = msg.text.trim(); @@ -204,6 +217,50 @@ async function handlePhoto(chatId: number, photos: { file_id: string }[], captio } } +async function handleDocument(chatId: number, fileId: string, fileName?: string, caption?: string) { + try { + const fs = await import("fs"); + const path = await import("path"); + const { getSession } = await import("./sessions"); + + const s = getSession(chatId); + if (!s.dirPath) { + await sendMessage(chatId, "❌ 请先发 /new 或 /edit 开始写作"); + return; + } + + const fileRes = await tg<{ file_path?: string }>("getFile", { file_id: fileId }); + if (!fileRes.ok || !fileRes.result?.file_path) { + await sendMessage(chatId, "❌ 文件获取失败(可能文件太大,Telegram 限制 20MB)"); + return; + } + + const filePath = fileRes.result.file_path; + const fileUrl = "https://api.telegram.org/file/bot" + TG_TOKEN + "/" + filePath; + const fileBuffer = await fetch(fileUrl).then((r) => r.arrayBuffer()); + + const articleDir = s.dirPath; + if (!fs.existsSync(articleDir)) fs.mkdirSync(articleDir, { recursive: true }); + + const ext = fileName?.split(".").pop() || filePath.split(".").pop() || "gif"; + const filename = Date.now() + "-" + fileId.slice(0, 8) + "." + ext; + fs.writeFileSync(path.join(articleDir, filename), Buffer.from(fileBuffer)); + + // GIF 用图片语法,其他用链接 + const isImage = ["gif", "webp", "png", "jpg", "jpeg"].includes(ext.toLowerCase()); + const fileCaption = caption || fileName || "file"; + if (isImage) { + appendContent(chatId, `\n![${fileCaption}](${filename})\n`); + } else { + appendContent(chatId, `\n[${fileCaption}](${filename})\n`); + } + await sendMessage(chatId, `📎 文件已保存:${filename}`); + } catch (e) { + console.error("[bot:document]", e instanceof Error ? e.message : String(e)); + await sendMessage(chatId, "❌ 文件保存失败"); + } +} + async function handleCommand(chatId: number, cmd: string, arg?: string) { try { if (cmd === "noop") return;