## 1. ECS RAM 角色改为自动探测 原先要用户去阿里云控制台查「实例 RAM 角色名」再手填,纯属多余 —— 机器自己知道答案。 新增 fb_oss_detect_instance_role():读元数据 GET /latest/meta-data/ram/security-credentials/(末尾带斜杠)拿到角色名列表。 - 兼容单行 / 多行 / JSON 数组三种返回 - 未配置角色时 fb_oss_instance_creds() 自动探测,不再报「未配置」 - 后台改成只读展示「已绑定:xxx」,手填入口收进「高级」折叠里 - 保存时留空 = 自动探测后写入,不存空值 - 探测失败给出可操作原因(没绑角色 / 不在 ECS 上 / 元数据被禁用) ## 2. 邮件配置界面(原先完全缺失) lib/mailer.php 有完整 SMTP 实现,但后台一个输入框都没有, 只能改 config.php —— 用户装完根本找不到地方配。 根因有两层: - fb_setting_keys() 白名单里没有任何 smtp.* / notify.* 键, 就算加了表单,fb_set_setting() 也会把值全部丢弃 - 设置页只有「站点信息」「附件存储」两个区块 补上:白名单 15 个键 + 后台「邮件通知」区块 - SMTP 服务器/端口/加密方式(SSL/STARTTLS/不加密) - 登录账号/密码(留空不修改,避免每次重填) - 发件地址/显示名、超时、SSL 校验 - 通知规则:回复通知、每次回复都通知、新反馈通知管理员、发信冷却 - 「发送测试邮件」按钮,AJAX 发完就地显示成功或失败原因, 不用保存再靠猜 ## 3. 每个软件一个主题色 需求:从短链接进不同软件,颜色不同,一眼区分。 - software 表加 color 字段(幂等迁移) - 全站配色靠 --brand / --brand-600 / --brand-soft 三个变量, 按软件覆写这三个即可,不用为每个软件写一套 CSS - fb_color_variants() 由主色推出深一档(×0.85)与浅一档(往白混 88%) - 未配色的软件按 id 从 8 色调色板取默认色,保证同站不撞色 - 后台:颜色选择器(原生取色 + 8 个预设 + 用默认) - 前台:index / new / thread 都按当前软件注入主题色, meta theme-color 也跟着变 验证:三功能专项 30/30;新旧回归全过 (全新安装 33/33、老基线 25/25、附件 22/22、安装向导 60/60、约束检查 20/20)
This commit is contained in:
@@ -694,9 +694,98 @@
|
||||
for (var k = 0; k < wraps.length; k++) { wraps[k].classList.remove('has-error'); }
|
||||
}
|
||||
|
||||
/* ---------- 主题色选择器(后台软件编辑) ---------- */
|
||||
function bindColorPicker() {
|
||||
var rows = document.querySelectorAll('.color-row');
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
(function (row) {
|
||||
var input = row.querySelector('input[type=color]');
|
||||
var presets = row.querySelectorAll('.color-preset');
|
||||
var reset = row.querySelector('.color-reset');
|
||||
if (!input) { return; }
|
||||
|
||||
function syncOn(v) {
|
||||
for (var k = 0; k < presets.length; k++) {
|
||||
var same = (presets[k].getAttribute('data-color') || '').toLowerCase() === (v || '').toLowerCase();
|
||||
if (same) { presets[k].classList.add('on'); } else { presets[k].classList.remove('on'); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < presets.length; j++) {
|
||||
presets[j].addEventListener('click', function () {
|
||||
var c = this.getAttribute('data-color') || '';
|
||||
input.value = c;
|
||||
syncOn(c);
|
||||
});
|
||||
}
|
||||
input.addEventListener('input', function () { syncOn(this.value); });
|
||||
|
||||
if (reset) {
|
||||
reset.addEventListener('click', function () {
|
||||
// 清空 color 字段 = 保存后回落到按 id 取的默认色
|
||||
input.value = '#0057b8';
|
||||
syncOn('');
|
||||
var hidden = document.createElement('input');
|
||||
hidden.type = 'hidden';
|
||||
hidden.name = 'color';
|
||||
hidden.value = '';
|
||||
row.appendChild(hidden);
|
||||
input.removeAttribute('name'); // 提交时以隐藏字段为准
|
||||
});
|
||||
}
|
||||
})(rows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 邮件测试发送(后台设置页) ---------- */
|
||||
function bindMailTest() {
|
||||
var btn = document.getElementById('mailTestBtn');
|
||||
if (!btn) { return; }
|
||||
var out = document.getElementById('mailTestOut');
|
||||
var to = document.getElementById('mailTestTo');
|
||||
btn.addEventListener('click', function () {
|
||||
var addr = to ? (to.value || '').trim() : '';
|
||||
if (!addr) {
|
||||
if (out) { out.className = 'mail-test-out err'; out.textContent = '请填写收件邮箱。'; }
|
||||
return;
|
||||
}
|
||||
btn.disabled = true;
|
||||
var old = btn.textContent;
|
||||
btn.textContent = '发送中…';
|
||||
if (out) { out.className = 'mail-test-out'; out.textContent = '正在连接 SMTP 服务器…'; }
|
||||
|
||||
var fd = new FormData();
|
||||
fd.append('csrf', btn.getAttribute('data-csrf') || '');
|
||||
fd.append('act', 'mail_test');
|
||||
fd.append('to', addr);
|
||||
|
||||
fetch('admin.php', { method: 'POST', body: fd, credentials: 'same-origin' })
|
||||
.then(function (r) { return r.text(); })
|
||||
.then(function (html) {
|
||||
// 服务端是整页渲染,从返回的 HTML 里取出提示文字
|
||||
var doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
var box = doc.querySelector('.flash');
|
||||
var txt = box ? (box.textContent || '').trim() : '已提交,请回页面顶部查看结果。';
|
||||
if (out) {
|
||||
out.className = 'mail-test-out ' + (/失败/.test(txt) ? 'err' : 'ok');
|
||||
out.textContent = txt;
|
||||
}
|
||||
})
|
||||
.catch(function (e) {
|
||||
if (out) { out.className = 'mail-test-out err'; out.textContent = '请求失败:' + e; }
|
||||
})
|
||||
.then(function () {
|
||||
btn.disabled = false;
|
||||
btn.textContent = old;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function boot() {
|
||||
bindCaptchaRefresh();
|
||||
bindAdminToggle();
|
||||
bindColorPicker();
|
||||
bindMailTest();
|
||||
bindInlineValidate(); // ★ 必须在 bindNoDoubleSubmit 之前
|
||||
bindNoDoubleSubmit();
|
||||
bindUploaders();
|
||||
|
||||
Reference in New Issue
Block a user