1
0

2 Sitoutukset 979e2621d5 ... efe38e4d00

Tekijä SHA1 Viesti Päivämäärä
  Your Name efe38e4d00 feat: 添加删除文章入口 3 päivää sitten
  Your Name a0b49b3de9 feat: 上传大图自动缩放,减小图片体积和带宽消耗 3 päivää sitten
3 muutettua tiedostoa jossa 96 lisäystä ja 12 poistoa
  1. 88 11
      app.py
  2. 3 1
      helpers.py
  3. 5 0
      templates/post_template.html

+ 88 - 11
app.py

@@ -51,6 +51,38 @@ if not config.INDEX_FILE.exists():
         json.dump([], f, ensure_ascii=False, indent=2)
 
 
+# ---------- 图片缩放辅助函数 ----------
+def resize_image_if_large(filepath: str, max_width: int = 1200):
+    """如果图片宽度超过 max_width,则等比缩放并覆盖原文件。
+    不处理 GIF 文件(保留动画),若 Pillow 未安装则静默跳过。"""
+    try:
+        from PIL import Image
+    except ImportError:
+        return   # 不做任何处理,避免依赖问题
+
+    try:
+        img = Image.open(filepath)
+        w, h = img.size
+        if w <= max_width:
+            return
+        # 跳过 GIF(避免丢失动画)
+        if img.format == "GIF":
+            return
+        new_h = int(h * max_width / w)
+        img = img.resize((max_width, new_h), Image.LANCZOS)
+        # 保存时尽量保留原格式和质量
+        save_kwargs = {}
+        if img.format == "JPEG":
+            save_kwargs["quality"] = 85
+            save_kwargs["optimize"] = True
+        elif img.format == "PNG":
+            save_kwargs["optimize"] = True
+        img.save(filepath, format=img.format, **save_kwargs)
+    except Exception:
+        # 任何异常都静默跳过,不影响上传流程
+        pass
+
+
 @app.context_processor
 def inject_user_status():
     """向所有模板注入登录状态"""
@@ -180,6 +212,9 @@ def editor_upload_image():
     file_path = editor_uploads_dir / new_name
     file.save(str(file_path))
 
+    # ---------- 对上传的图片进行缩放 ----------
+    resize_image_if_large(str(file_path))
+
     # 生成可访问 URL 和 Markdown 片段
     image_url = f"/static/uploads/posts/editor_uploads/{new_name}"
     markdown_code = f"![{safe_name}]({image_url})"
@@ -281,7 +316,10 @@ def upload():
         if img and img.filename:
             filename = secure_filename(img.filename)
             if filename:
-                img.save(str(upload_dir / filename))
+                file_path = upload_dir / filename
+                img.save(str(file_path))
+                # ---------- 对上传的图片进行缩放 ----------
+                resize_image_if_large(str(file_path))
 
     # 读取 Markdown 内容
     with open(md_path, "r", encoding="utf-8") as f:
@@ -325,19 +363,53 @@ def upload():
 
 @app.route("/post/<post_id>")
 def view_post(post_id: str):
-    """查看文章详情"""
-    # 安全验证:只允许字母数字和下划线
+    """查看文章详情,支持登录后显示删除按钮"""
     if not re.match(r"^[a-zA-Z0-9_]+$", post_id):
         abort(404)
 
-    try:
-        return send_from_directory(
-            config.GENERATED_FOLDER,
-            f"{post_id}.html",
-        )
-    except FileNotFoundError:
+    posts = load_index()
+    entry = next((p for p in posts if p["id"] == post_id), None)
+    if not entry:
         abort(404)
 
+    title = entry.get("title", "")
+    date = entry.get("date", "")
+    thumbnail = entry.get("thumbnail", "")
+
+    content = ""
+    md_path = config.POSTS_DATA_FOLDER / post_id / "content.md"
+    if md_path.exists():
+        try:
+            with open(md_path, "r", encoding="utf-8") as f:
+                md_content = f.read()
+            fixed_md = fix_image_paths(md_content, post_id)
+            html_body = render_markdown(fixed_md)
+            content = html_body
+        except Exception:
+            content = ""
+
+    if not content:
+        generated_path = config.GENERATED_FOLDER / f"{post_id}.html"
+        if generated_path.exists():
+            try:
+                with open(generated_path, "r", encoding="utf-8") as f:
+                    html_content = f.read()
+                soup = BeautifulSoup(html_content, "html.parser")
+                card_summary = soup.find("div", class_="card-summary")
+                if card_summary:
+                    content = card_summary.decode_contents()
+            except Exception:
+                content = ""
+
+    return render_template(
+        "post_template.html",
+        title=title,
+        content=content,
+        date=date,
+        thumbnail=thumbnail,
+        post_id=post_id,
+    )
+
 
 @app.route("/admin")
 def admin():
@@ -348,7 +420,11 @@ def admin():
 
 @app.route("/admin/delete/<post_id>", methods=["POST"])
 def delete_post(post_id: str):
-    """删除文章"""
+    """删除文章(需要登录)"""
+    if "user" not in session:
+        flash("请先登录", "warning")
+        return redirect(url_for("login"))
+
     # 安全验证
     if not re.match(r"^[a-zA-Z0-9_]+$", post_id):
         abort(404)
@@ -373,7 +449,8 @@ def delete_post(post_id: str):
     index = [p for p in index if p["id"] != post_id]
     save_index(index)
 
-    return redirect(url_for("admin"))
+    flash("文章已删除", "success")
+    return redirect(url_for("index"))
 
 
 @app.errorhandler(404)

+ 3 - 1
helpers.py

@@ -68,7 +68,7 @@ def render_markdown(md_content: str) -> str:
     )
     return md.convert(md_content)
 
-def generate_static_page(post_id: str, title: str, html_body: str, date: str, thumbnail: str):
+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",
@@ -76,6 +76,8 @@ def generate_static_page(post_id: str, title: str, html_body: str, date: str, th
         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:

+ 5 - 0
templates/post_template.html

@@ -15,6 +15,11 @@
         <div class="card-summary">
             {{ content | safe }}
         </div>
+        {% if logged_in and post_id %}
+        <form method="POST" action="{{ url_for('delete_post', post_id=post_id) }}" onsubmit="return confirm('确定要删除「{{ title }}」吗?此操作不可恢复。');" style="margin-top:1.5rem;">
+            <button type="submit" class="btn btn-danger">🗑️ 删除文章</button>
+        </form>
+        {% endif %}
     </div>
 </article>
 {% endblock %}