diff --git a/README.md b/README.md
index 79d7ada..a1caf23 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,9 @@ go_blog/
│ └── upload_validator.go # 上传文件校验
├── i18n/
│ └── i18n.go # 中英文翻译映射 + Accept-Language 检测
+├── static/
+│ ├── css/markdown.css # Markdown 排版样式(go:embed 编入二进制)
+│ └── js/markdown.js # 前端 Markdown 渲染器(BlogMD)
├── templates/
│ ├── layouts/base.html # 公共布局(导航栏 + 头像下拉菜单 + 页脚)
│ ├── pages/
diff --git a/install_linux.sh b/install_linux.sh
index cc2d4d1..f11dc5e 100755
--- a/install_linux.sh
+++ b/install_linux.sh
@@ -40,15 +40,11 @@ install -m 0755 -o root -g root "${SCRIPT_DIR}/${BINARY_NAME}" "${INSTALL_DIR}/$
rm -rf "${INSTALL_DIR}/templates"
cp -a "${SCRIPT_DIR}/templates" "${INSTALL_DIR}/templates"
chown -R root:root "${INSTALL_DIR}/templates"
-rm -rf "${INSTALL_DIR}/static"
-cp -a "${SCRIPT_DIR}/static" "${INSTALL_DIR}/static"
-chown -R root:root "${INSTALL_DIR}/static"
+# static 资源已通过 go:embed 编入二进制,无需单独拷贝
chown "${SERVICE_USER}:${SERVICE_USER}" "${INSTALL_DIR}"
chmod 0755 "${INSTALL_DIR}"
find "${INSTALL_DIR}/templates" -type d -exec chmod 0755 {} \;
find "${INSTALL_DIR}/templates" -type f -exec chmod 0644 {} \;
-find "${INSTALL_DIR}/static" -type d -exec chmod 0755 {} \;
-find "${INSTALL_DIR}/static" -type f -exec chmod 0644 {} \;
SOCKET_PATH="${SOCKET_DIR}/web.sock"
diff --git a/main.go b/main.go
index e76f56e..4c5466a 100644
--- a/main.go
+++ b/main.go
@@ -1,10 +1,13 @@
package main
import (
+ "embed"
"flag"
"fmt"
+ "io/fs"
"log"
"net"
+ "net/http"
"os"
"github.com/gin-contrib/sessions"
@@ -17,6 +20,13 @@ import (
"go_blog/models"
)
+// staticFiles embeds the static assets (Markdown CSS/JS) into the binary so a
+// deployment only needs to replace the executable — no separate static
+// directory has to be copied to the server.
+//
+//go:embed static
+var staticFiles embed.FS
+
func main() {
// 0. Parse command-line flags.
configFlag := flag.String("config", "", "path to config file (default: OS-aware path)")
@@ -49,8 +59,12 @@ func main() {
// 6. Serve uploaded files (avatars etc.) from the storage path.
router.Static("/uploads", cfg.Path)
- // 6b. Serve bundled static assets (CSS/JS for Markdown rendering).
- router.Static("/static", "./static")
+ // 6b. Serve bundled static assets (embedded into the binary).
+ staticFS, err := fs.Sub(staticFiles, "static")
+ if err != nil {
+ log.Fatalf("Failed to open embedded static assets: %v", err)
+ }
+ router.StaticFS("/static", http.FS(staticFS))
// 6. Global session middleware.
router.Use(sessions.Sessions("blog_session", store))
diff --git a/templates/admin/comment_list.html b/templates/admin/comment_list.html
index ff78adb..e2ad00b 100644
--- a/templates/admin/comment_list.html
+++ b/templates/admin/comment_list.html
@@ -83,7 +83,12 @@
diff --git a/templates/pages/article.html b/templates/pages/article.html
index ddb05b3..38a4e38 100644
--- a/templates/pages/article.html
+++ b/templates/pages/article.html
@@ -144,12 +144,27 @@