import re import json from pathlib import Path from datetime import datetime, timezone from flask import render_template from werkzeug.utils import secure_filename import markdown from bs4 import BeautifulSoup import config def allowed_file(filename: str) -> bool: """检查文件扩展名是否允许""" return ( "." in filename and filename.rsplit(".", 1)[1].lower() in config.ALLOWED_EXTENSIONS ) def load_index() -> list: """加载文章索引""" with open(config.INDEX_FILE, "r", encoding="utf-8") as f: return json.load(f) def save_index(index: list): """保存文章索引""" with open(config.INDEX_FILE, "w", encoding="utf-8") as f: json.dump(index, f, ensure_ascii=False, indent=2) def fix_image_paths(md_content: str, post_id: str) -> str: """将 Markdown 中的本地图片路径替换为可访问的 URL""" pattern = r'!\[([^\]]*)\]\(([^)]+)\)' def repl(match): alt, src = match.groups() # 仅当 src 不含 '/' 或 'http' 且扩展名为图片时视为本地文件 if ( not src.startswith(("http://", "https://", "/", "#")) and "." in src ): new_src = f"/static/uploads/posts/{post_id}/{src}" return f'![{alt}]({new_src})' return match.group(0) return re.sub(pattern, repl, md_content) def extract_summary(html_body: str, max_chars: int = 200) -> str: """从 HTML 中提取纯文本摘要(返回完整文本)""" soup = BeautifulSoup(html_body, "html.parser") text = soup.get_text() # 去除多余空白 text = re.sub(r"\s+", " ", text).strip() return text def extract_thumbnail(html_body: str) -> str: """从 HTML 中提取第一张图片的 URL""" soup = BeautifulSoup(html_body, "html.parser") img = soup.find("img") if img and img.get("src"): return img["src"] return "" def render_markdown(md_content: str) -> str: """将 Markdown 渲染为 HTML""" md = markdown.Markdown( extensions=[ "extra", "codehilite", "tables", "fenced_code", ] ) return md.convert(md_content) def generate_static_page(post_id: str, title: str, html_body: str, date: str, thumbnail: str, logged_in: bool = False): """生成独立的静态 HTML 文件""" rendered = render_template( "post_template.html", title=title, content=html_body, date=date, thumbnail=thumbnail, post_id=post_id, logged_in=logged_in, ) output_path = config.GENERATED_FOLDER / f"{post_id}.html" with open(output_path, "w", encoding="utf-8") as f: f.write(rendered)