diff --git a/templates/MetaLab-2026/html/messages/index.html b/templates/MetaLab-2026/html/messages/index.html
index 86dd26b..31ad093 100644
--- a/templates/MetaLab-2026/html/messages/index.html
+++ b/templates/MetaLab-2026/html/messages/index.html
@@ -133,12 +133,10 @@
if (link) {
// 有链接的通知:使用 fetch keepalive 确保页面跳转前标记已读不会丢失
e.preventDefault();
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
fetch('/api/messages/' + id + '/read', {
method: 'POST',
keepalive: true,
- headers: { 'X-CSRF-Token': csrfToken }
+ headers: { 'X-CSRF-Token': getCSRFToken() }
});
card.classList.remove('unread');
var badge = card.querySelector('.msg-badge');
diff --git a/templates/MetaLab-2026/html/posts/show.html b/templates/MetaLab-2026/html/posts/show.html
index 14ff0b5..0a739e7 100644
--- a/templates/MetaLab-2026/html/posts/show.html
+++ b/templates/MetaLab-2026/html/posts/show.html
@@ -187,12 +187,11 @@
if (!btn) return;
btn.addEventListener('click', function() {
if (!confirm('确认提交审核?审核通过后将公开发布。')) return;
- var csrf = document.querySelector('meta[name="csrf-token"]');
fetch('/api/posts/' + btn.dataset.id + '/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'X-CSRF-Token': csrf ? csrf.getAttribute('content') : ''
+ 'X-CSRF-Token': getCSRFToken()
}
})
.then(function(r) { return r.json(); })
@@ -217,8 +216,6 @@
if (!btn || !overlay) return;
var postId = btn.dataset.id;
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
function showOverlay() {
overlay.style.display = 'flex';
@@ -256,7 +253,7 @@
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'X-CSRF-Token': csrfToken
+ 'X-CSRF-Token': getCSRFToken()
},
body: JSON.stringify({ post_id: parseInt(postId), amount: amount })
})
@@ -297,8 +294,6 @@
if (!container || !likeBtn || !dislikeBtn) return;
var postId = container.dataset.postId;
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
// 页面加载时获取当前反应状态
fetch('/api/posts/' + postId + '/reaction')
@@ -321,7 +316,7 @@
likeBtn.disabled = true;
fetch('/api/posts/' + postId + '/like', {
method: 'POST',
- headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCSRFToken() }
})
.then(function(r) {
if (r.status === 401) { window.location.href = '/auth/login?redirect=/posts/' + postId; return null; }
@@ -343,7 +338,7 @@
dislikeBtn.disabled = true;
fetch('/api/posts/' + postId + '/dislike', {
method: 'POST',
- headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCSRFToken() }
})
.then(function(r) {
if (r.status === 401) { window.location.href = '/auth/login?redirect=/posts/' + postId; return null; }
@@ -377,15 +372,14 @@
if (!favBtn || !container) return;
var postId = container.dataset.postId;
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
var isFavorited = false;
var currentFolderId = null;
var loading = false;
function api(method, url, data) {
var opts = { method: method, headers: { 'Content-Type': 'application/json' } };
- if (csrfToken) opts.headers['X-CSRF-Token'] = csrfToken;
+ var token = getCSRFToken();
+ if (token) opts.headers['X-CSRF-Token'] = token;
if (data) opts.body = JSON.stringify(data);
return fetch(url, opts).then(function(r) {
if (r.status === 401) { window.location.href = '/auth/login?redirect=/posts/' + postId; throw new Error('unauthorized'); }
@@ -469,7 +463,7 @@
// 无弹窗回退(未登录跳转)
function handleNoModal() {
- if (!csrfToken) {
+ if (!getCSRFToken()) {
window.location.href = '/auth/login?redirect=/posts/' + postId;
return;
}
@@ -571,7 +565,7 @@
// 事件:点击收藏按钮 → 打开弹窗
favBtn.addEventListener('click', function() {
- if (!csrfToken) { window.location.href = '/auth/login?redirect=/posts/' + postId; return; }
+ if (!getCSRFToken()) { window.location.href = '/auth/login?redirect=/posts/' + postId; return; }
showModal();
});
@@ -627,15 +621,14 @@
var commentInput = document.getElementById('commentInput');
var commentSubmitBtn = document.getElementById('commentSubmitBtn');
var mentionDropdown = document.getElementById('mentionDropdown');
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
var currentPage = 1;
var totalPages = 1;
// ====== 工具函数 ======
function api(method, url, data) {
var opts = { method: method, headers: { 'Content-Type': 'application/json' } };
- if (csrfToken) opts.headers['X-CSRF-Token'] = csrfToken;
+ var token = getCSRFToken();
+ if (token) opts.headers['X-CSRF-Token'] = token;
if (data) opts.body = JSON.stringify(data);
return fetch(url, opts).then(function(r) { return r.json(); });
}
@@ -1031,7 +1024,7 @@
fetch('/api/comments/upload-image', {
method: 'POST',
- headers: { 'X-CSRF-Token': csrfToken },
+ headers: { 'X-CSRF-Token': getCSRFToken() },
body: formData
})
.then(function(r) { return r.json(); })
diff --git a/templates/MetaLab-2026/html/settings/index.html b/templates/MetaLab-2026/html/settings/index.html
index f6f65af..30055dd 100644
--- a/templates/MetaLab-2026/html/settings/index.html
+++ b/templates/MetaLab-2026/html/settings/index.html
@@ -1032,9 +1032,6 @@
var createBtn = document.getElementById('createFolderBtn');
if (!listEl) return;
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
-
function esc(s) {
var d = document.createElement('div');
d.textContent = s || '';
@@ -1043,7 +1040,8 @@
function api(method, url, data) {
var opts = { method: method, headers: { 'Content-Type': 'application/json' } };
- if (csrfToken) opts.headers['X-CSRF-Token'] = csrfToken;
+ var token = getCSRFToken();
+ if (token) opts.headers['X-CSRF-Token'] = token;
if (data) opts.body = JSON.stringify(data);
return fetch(url, opts).then(function (r) { return r.json(); });
}
diff --git a/templates/MetaLab-2026/html/studio/drafts.html b/templates/MetaLab-2026/html/studio/drafts.html
index 4d8d1e7..df6d666 100644
--- a/templates/MetaLab-2026/html/studio/drafts.html
+++ b/templates/MetaLab-2026/html/studio/drafts.html
@@ -65,7 +65,7 @@ function submitPost(id) {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'X-CSRF-Token': '{{.CSRFToken}}'
+ 'X-CSRF-Token': getCSRFToken()
}
})
.then(r => r.json())
@@ -85,7 +85,7 @@ function deletePost(id) {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
- 'X-CSRF-Token': '{{.CSRFToken}}'
+ 'X-CSRF-Token': getCSRFToken()
}
})
.then(r => r.json())
diff --git a/templates/MetaLab-2026/html/studio/posts.html b/templates/MetaLab-2026/html/studio/posts.html
index 4f1f1cc..618924d 100644
--- a/templates/MetaLab-2026/html/studio/posts.html
+++ b/templates/MetaLab-2026/html/studio/posts.html
@@ -86,7 +86,7 @@ function deletePost(id) {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
- 'X-CSRF-Token': '{{.CSRFToken}}'
+ 'X-CSRF-Token': getCSRFToken()
}
})
.then(r => r.json())
diff --git a/templates/MetaLab-2026/static/js/studio-editor.js b/templates/MetaLab-2026/static/js/studio-editor.js
index 86c061b..8a2f95f 100644
--- a/templates/MetaLab-2026/static/js/studio-editor.js
+++ b/templates/MetaLab-2026/static/js/studio-editor.js
@@ -1,17 +1,13 @@
/**
* Studio Editor — 创作中心 Vditor 编辑器
* 基于 editor.js,使用 /api/studio/posts 端点
+ * CSRF token 由 shared/utils.js 的 getCSRFToken() 统一提供
*/
let vditor = null
let draftTimer = null
let submitting = false
-function getCsrfToken() {
- var match = document.cookie.match(/(?:^|;\s*)mlb_csrf=([^;]*)/)
- return match ? match[1] : ''
-}
-
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('vditor')
if (!container) return
@@ -186,7 +182,7 @@ async function handleSubmit(e) {
method,
headers: {
'Content-Type': 'application/json',
- 'X-CSRF-Token': getCsrfToken(),
+ 'X-CSRF-Token': getCSRFToken(),
},
body: JSON.stringify({ title, body }),
})
diff --git a/templates/admin/static/js/comments.js b/templates/admin/static/js/comments.js
index 011d193..4bbc047 100644
--- a/templates/admin/static/js/comments.js
+++ b/templates/admin/static/js/comments.js
@@ -3,8 +3,6 @@
var currentPage = 1;
var currentKeyword = '';
var totalPages = 1;
- var csrfMeta = document.querySelector('meta[name="csrf-token"]');
- var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : '';
var searchInput = document.getElementById('commentSearch');
var searchBtn = document.getElementById('commentSearchBtn');
diff --git a/templates/admin/static/js/posts.js b/templates/admin/static/js/posts.js
index 1f9f53c..0a43de3 100644
--- a/templates/admin/static/js/posts.js
+++ b/templates/admin/static/js/posts.js
@@ -1,8 +1,5 @@
(function() {
- function getCSRFToken() {
- var meta = document.querySelector('meta[name="csrf-token"]');
- return meta ? meta.getAttribute('content') : '';
- }
+ // CSRF token 由 shared/utils.js 的 getCSRFToken() 统一提供
function api(url, method, body) {
return fetch(url, {
diff --git a/templates/shared/static/js/utils.js b/templates/shared/static/js/utils.js
index 09ef3ac..77353a8 100644
--- a/templates/shared/static/js/utils.js
+++ b/templates/shared/static/js/utils.js
@@ -4,11 +4,13 @@
'use strict';
/**
- * 从 读取 CSRF 令牌
- * 由服务端 CSRF 中间件注入到页面模板的 标签中
+ * 获取 CSRF 令牌 — 优先从 cookie 读取(与服务端验证源一致,token 刷新后保持最新),
+ * meta 标签作为后备(部分页面可能未设置 cookie)
* @returns {string} CSRF 令牌,未找到时返回空字符串
*/
function getCSRFToken() {
+ var match = document.cookie.match(/(?:^|;\s*)mlb_csrf=([^;]*)/);
+ if (match && match[1]) return match[1];
var meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.getAttribute('content') : '';
}