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
|
FROM node:20-alpine AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install Hugo and su-exec (for entrypoint)
|
# Install specific Hugo version, git and su-exec
|
||||||
RUN apk add --no-cache hugo 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
|
# Create non-root user
|
||||||
RUN addgroup --system --gid 1001 nodejs && \
|
RUN addgroup --system --gid 1001 nodejs && \
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8016:8016"
|
- "8016:8016"
|
||||||
|
- "1313:1313"
|
||||||
volumes:
|
volumes:
|
||||||
# Hugo 博客目录(必须)- 映射宿主机博客根目录到容器内 /blog
|
# Hugo 博客目录(必须)- 映射宿主机博客根目录到容器内 /blog
|
||||||
- ..:/blog
|
- ..:/blog
|
||||||
|
|||||||
@@ -15,9 +15,16 @@ fi
|
|||||||
if [ -d "/blog/static" ]; then
|
if [ -d "/blog/static" ]; then
|
||||||
chown -R nextjs:nodejs /blog/static
|
chown -R nextjs:nodejs /blog/static
|
||||||
fi
|
fi
|
||||||
|
if [ -d "/blog/.git" ]; then
|
||||||
|
chown -R nextjs:nodejs /blog/.git
|
||||||
|
fi
|
||||||
if [ -f "/blog/hugo.toml" ]; then
|
if [ -f "/blog/hugo.toml" ]; then
|
||||||
chown nextjs:nodejs /blog/hugo.toml
|
chown nextjs:nodejs /blog/hugo.toml
|
||||||
fi
|
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 用户启动应用
|
# 以 nextjs 用户启动应用
|
||||||
exec su-exec nextjs "$@"
|
exec su-exec nextjs "$@"
|
||||||
@@ -95,10 +95,20 @@ document.getElementById('form').addEventListener('submit', async function(e) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 已登录则直接跳转
|
// 已登录则验证后跳转
|
||||||
if (localStorage.getItem('wauth')) {
|
(function(){
|
||||||
location.href = '/';
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -70,6 +70,9 @@ export async function startPolling(): Promise<void> {
|
|||||||
const msg = update.message as {
|
const msg = update.message as {
|
||||||
chat?: { id: number };
|
chat?: { id: number };
|
||||||
photo?: { file_id: string }[];
|
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;
|
text?: string;
|
||||||
caption?: string;
|
caption?: string;
|
||||||
reply_to_message?: { text?: string };
|
reply_to_message?: { text?: string };
|
||||||
@@ -78,11 +81,21 @@ export async function startPolling(): Promise<void> {
|
|||||||
const chatId = msg.chat.id;
|
const chatId = msg.chat.id;
|
||||||
if (!allowed(chatId)) continue;
|
if (!allowed(chatId)) continue;
|
||||||
|
|
||||||
|
// 处理图片
|
||||||
if (msg.photo && hasSession(chatId)) {
|
if (msg.photo && hasSession(chatId)) {
|
||||||
await handlePhoto(chatId, msg.photo, msg.caption);
|
await handlePhoto(chatId, msg.photo, msg.caption);
|
||||||
continue;
|
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;
|
if (!msg.text) continue;
|
||||||
const fullText = msg.text.trim();
|
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) {
|
async function handleCommand(chatId: number, cmd: string, arg?: string) {
|
||||||
try {
|
try {
|
||||||
if (cmd === "noop") return;
|
if (cmd === "noop") return;
|
||||||
|
|||||||
Reference in new issue
Block a user