2358
This commit is contained in:
1 parent
424355da7c
commit
7b3173e89d
5 files changed
+86
-6
No files matched your search
@@ -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 && \
|
||||
|
||||
@@ -12,6 +12,7 @@ services:
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8016:8016"
|
||||
- "1313:1313"
|
||||
volumes:
|
||||
# Hugo 博客目录(必须)- 映射宿主机博客根目录到容器内 /blog
|
||||
- ..:/blog
|
||||
|
||||
@@ -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 "$@"
|
||||
@@ -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');
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -70,6 +70,9 @@ export async function startPolling(): Promise<void> {
|
||||
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<void> {
|
||||
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\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;
|
||||
|
||||
Reference in new issue
Block a user