Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5218f35a1 | ||
|
|
bd3a875403 | ||
|
|
5f21c73044 |
@@ -120,6 +120,9 @@ go_blog/
|
|||||||
│ └── upload_validator.go # 上传文件校验
|
│ └── upload_validator.go # 上传文件校验
|
||||||
├── i18n/
|
├── i18n/
|
||||||
│ └── i18n.go # 中英文翻译映射 + Accept-Language 检测
|
│ └── i18n.go # 中英文翻译映射 + Accept-Language 检测
|
||||||
|
├── static/
|
||||||
|
│ ├── css/markdown.css # Markdown 排版样式(go:embed 编入二进制)
|
||||||
|
│ └── js/markdown.js # 前端 Markdown 渲染器(BlogMD)
|
||||||
├── templates/
|
├── templates/
|
||||||
│ ├── layouts/base.html # 公共布局(导航栏 + 头像下拉菜单 + 页脚)
|
│ ├── layouts/base.html # 公共布局(导航栏 + 头像下拉菜单 + 页脚)
|
||||||
│ ├── pages/
|
│ ├── pages/
|
||||||
|
|||||||
+1
-5
@@ -40,15 +40,11 @@ install -m 0755 -o root -g root "${SCRIPT_DIR}/${BINARY_NAME}" "${INSTALL_DIR}/$
|
|||||||
rm -rf "${INSTALL_DIR}/templates"
|
rm -rf "${INSTALL_DIR}/templates"
|
||||||
cp -a "${SCRIPT_DIR}/templates" "${INSTALL_DIR}/templates"
|
cp -a "${SCRIPT_DIR}/templates" "${INSTALL_DIR}/templates"
|
||||||
chown -R root:root "${INSTALL_DIR}/templates"
|
chown -R root:root "${INSTALL_DIR}/templates"
|
||||||
rm -rf "${INSTALL_DIR}/static"
|
# static 资源已通过 go:embed 编入二进制,无需单独拷贝
|
||||||
cp -a "${SCRIPT_DIR}/static" "${INSTALL_DIR}/static"
|
|
||||||
chown -R root:root "${INSTALL_DIR}/static"
|
|
||||||
chown "${SERVICE_USER}:${SERVICE_USER}" "${INSTALL_DIR}"
|
chown "${SERVICE_USER}:${SERVICE_USER}" "${INSTALL_DIR}"
|
||||||
chmod 0755 "${INSTALL_DIR}"
|
chmod 0755 "${INSTALL_DIR}"
|
||||||
find "${INSTALL_DIR}/templates" -type d -exec chmod 0755 {} \;
|
find "${INSTALL_DIR}/templates" -type d -exec chmod 0755 {} \;
|
||||||
find "${INSTALL_DIR}/templates" -type f -exec chmod 0644 {} \;
|
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"
|
SOCKET_PATH="${SOCKET_DIR}/web.sock"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
@@ -17,6 +20,13 @@ import (
|
|||||||
"go_blog/models"
|
"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() {
|
func main() {
|
||||||
// 0. Parse command-line flags.
|
// 0. Parse command-line flags.
|
||||||
configFlag := flag.String("config", "", "path to config file (default: OS-aware path)")
|
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.
|
// 6. Serve uploaded files (avatars etc.) from the storage path.
|
||||||
router.Static("/uploads", cfg.Path)
|
router.Static("/uploads", cfg.Path)
|
||||||
|
|
||||||
// 6b. Serve bundled static assets (CSS/JS for Markdown rendering).
|
// 6b. Serve bundled static assets (embedded into the binary).
|
||||||
router.Static("/static", "./static")
|
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.
|
// 6. Global session middleware.
|
||||||
router.Use(sessions.Sessions("blog_session", store))
|
router.Use(sessions.Sessions("blog_session", store))
|
||||||
|
|||||||
@@ -83,7 +83,12 @@
|
|||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
document.querySelectorAll('.comment-body[data-md]').forEach(function (el) {
|
document.querySelectorAll('.comment-body[data-md]').forEach(function (el) {
|
||||||
BlogMD.renderInto(el, el.getAttribute('data-md') || '');
|
var raw = el.getAttribute('data-md') || '';
|
||||||
|
if (window.BlogMD) {
|
||||||
|
BlogMD.renderInto(el, raw);
|
||||||
|
} else {
|
||||||
|
el.textContent = raw;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -111,13 +111,16 @@
|
|||||||
{{define "markdown_assets"}}
|
{{define "markdown_assets"}}
|
||||||
{{/* Markdown rendering assets: marked (pinned UMD build), DOMPurify,
|
{{/* Markdown rendering assets: marked (pinned UMD build), DOMPurify,
|
||||||
highlight.js and the shared renderer + styles. Include on any page that
|
highlight.js and the shared renderer + styles. Include on any page that
|
||||||
renders Markdown (articles, comments, editor previews). */}}
|
renders Markdown (articles, comments, editor previews).
|
||||||
|
Local static files carry a ?v= cache-buster: bump it whenever
|
||||||
|
static/js/markdown.js or static/css/markdown.css changes, otherwise
|
||||||
|
browsers may keep serving stale cached copies. */}}
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/styles/github.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/styles/github.min.css">
|
||||||
<link rel="stylesheet" href="/static/css/markdown.css">
|
<link rel="stylesheet" href="/static/css/markdown.css?v=2">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.12/marked.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.4.13/dist/purify.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.4.13/dist/purify.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/highlight.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/highlight.min.js"></script>
|
||||||
<script src="/static/js/markdown.js"></script>
|
<script src="/static/js/markdown.js?v=2"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "footer"}}
|
{{define "footer"}}
|
||||||
|
|||||||
@@ -144,12 +144,27 @@
|
|||||||
<script>
|
<script>
|
||||||
// Article body: render Markdown client-side through the shared BlogMD
|
// Article body: render Markdown client-side through the shared BlogMD
|
||||||
// pipeline (marked + DOMPurify + highlight.js, see /static/js/markdown.js).
|
// pipeline (marked + DOMPurify + highlight.js, see /static/js/markdown.js).
|
||||||
BlogMD.renderInto(document.getElementById('articleBody'), {{.Article.Content}});
|
// If the renderer failed to load, fall back to plain text so the article
|
||||||
|
// is never blank.
|
||||||
|
(function () {
|
||||||
|
var body = document.getElementById('articleBody');
|
||||||
|
var content = {{.Article.Content}};
|
||||||
|
if (window.BlogMD) {
|
||||||
|
BlogMD.renderInto(body, content);
|
||||||
|
} else {
|
||||||
|
body.textContent = content;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// Comments are rendered from the escaped data-md attribute.
|
// Comments are rendered from the escaped data-md attribute.
|
||||||
document.querySelectorAll('.comment-body[data-md]').forEach(function (el) {
|
document.querySelectorAll('.comment-body[data-md]').forEach(function (el) {
|
||||||
if (el.dataset.rendered) return;
|
if (el.dataset.rendered) return;
|
||||||
BlogMD.renderInto(el, el.getAttribute('data-md') || '');
|
var raw = el.getAttribute('data-md') || '';
|
||||||
|
if (window.BlogMD) {
|
||||||
|
BlogMD.renderInto(el, raw);
|
||||||
|
} else {
|
||||||
|
el.textContent = raw;
|
||||||
|
}
|
||||||
el.dataset.rendered = '1';
|
el.dataset.rendered = '1';
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -191,7 +206,11 @@
|
|||||||
var previewing = previewBox.classList.toggle('hidden') === false;
|
var previewing = previewBox.classList.toggle('hidden') === false;
|
||||||
textarea.classList.toggle('hidden', previewing);
|
textarea.classList.toggle('hidden', previewing);
|
||||||
if (previewing) {
|
if (previewing) {
|
||||||
previewBox.innerHTML = BlogMD.render(textarea.value);
|
if (window.BlogMD) {
|
||||||
|
previewBox.innerHTML = BlogMD.render(textarea.value);
|
||||||
|
} else {
|
||||||
|
previewBox.textContent = textarea.value;
|
||||||
|
}
|
||||||
togglePreview.textContent = '{{index .Tr "comments_edit"}}';
|
togglePreview.textContent = '{{index .Tr "comments_edit"}}';
|
||||||
} else {
|
} else {
|
||||||
togglePreview.textContent = '{{index .Tr "comments_preview"}}';
|
togglePreview.textContent = '{{index .Tr "comments_preview"}}';
|
||||||
|
|||||||
Reference in New Issue
Block a user