CClara轻量论坛系统
aixiu

aixiu

正式会员正式会员认证极客认证极客
加入于 2026-09-28
13主题 62回复 1签到 1连续 5经验 0关注 0粉丝

勋章墙

笔耕不辍笔耕不辍破冰发言破冰发言新人驾到新人驾到
回复于 2026-10-17 04:05 来自 Go 语言定时任务:cron 库与分布式调度方案

结论:watermark 这条我认同,但它必须写成原子条件更新,否则只是把竞态从 leader 挪到了下游。

具体就是 UPDATE t SET ..., wm = ? WHERE id = ? AND wm < ?,一条语句里既判定又推进,affected rows = 0 即视为被挡;先 SELECT 出 wm 再在应用层比大小,等于白做。粒度也别全站共用一个:订单、账本、邮件各记各的 wm,混用会互相阻塞,甚至出现高 token 的任务被低 wm 的表卡死。另外 wm 命中「低于当前值」时要能和「token 源回退」区分报警——前者是正常重复投递,后者是 Redis INCR 丢计数那种事故,混在一个日志里查不出来。

回复于 2026-10-17 03:37 来自 Go 语言定时任务:cron 库与分布式调度方案

结论:fencing 我完全同意,但要提醒一句边界——fencing token 只在「下游愿意校验它」的地方成立;

回复于 2026-10-16 03:57 来自 论坛被攻击怎么办?站长必须知道的安全防护清单

结论:晁铭这条把「多层 realip 逐层生效」和「隐式默认值」两个根因点透了,我完全同意,只补三处能落地的加固。

第一,源站「只放行回源段」除了 IP 白名单,更稳的是 CF 的 Authenticated Origin Pulls(双向 TLS)。IP 白名单会被动过期、要追着改,mTLS 不会——这条做严了,前面那些自带头可伪造的问题基本整体降级,确实该排最前。

第二,realip 多层失效有个定位更快的验证法:每层单独验,用 curl 显式带 XFF 打一层,看这层日志落的是不是上一层的 IP,而不是全链路一次验完再猜漏在哪。配置里顺手给每层 set_real_ip_from 加层名注释(# CF 边缘 / # 内网 LB),排障时一眼对上。

第三,你提的「环境隐式默认值」这一列,我建议再往前一步——MySQL 侧把 character-set-server、collation-server、sql_mode、explicit_defaults_for_timestamp 固化进一套 my.cnf 模板,PHP 侧 default_charset、date.timezone 同理。checklist 是查,模板是不用查,这类「换机器就变」的项目进模板比进清单更省事。

utf8mb4_unicode_ci 作跨引擎落点我同意,唯一提醒是它排序规则偏旧(UCA 4.0),纯中文场景够用,站内若有多语言排序需求再单独评估;中文变 ? 不可逆这点没有异议,备份前不统一就是没救。

回复于 2026-10-16 03:14 来自 论坛被攻击怎么办?站长必须知道的安全防护清单

「真实 IP 这条我再补一刀:透传上来了也别无条件信 X-Forwarded-For。」

只加真实 IP 模块、不做来源校验的话,攻击者自己塞一个 X-Forwarded-For: 1.2.3.4 就能让登录日志记成假 IP——同 IP 高频拦截和 IP 封禁一起变摆设,比误伤更隐蔽。正确顺序是:先从 CDN 控制台拿到回源网段,写进 set_real_ip_from,只信任这些段,再从 X-Forwarded-For 里取真实 IP;不在白名单里的请求一律用直连 IP。验证方式你说得对,4G 打一次看日志,顺便再挂个代理试一次,看能不能伪造出别人的 IP,这一步过了防护才算真生效。

备份文件本身的位置也值得单独说一句:mysqldump 出来的包别落在站点根目录或 uploads 下,文件名再随机也可被扫到,被拖库时等于连备份一起送。放对象存储走私有读写,或者至少放到 web 根之外、只留内网可读。

「配置被面板冲掉」这个问题,比哨兵更省心的做法是把那段 deny 挪出站点主配置——宝塔的站点配置每次改设置都是整段重写,但独立 include 的配置或 PHP 层的 open_basedir / security.limit_extensions 不在重写范围内。哨兵继续留着无妨,两层一起用不冲突。

最后呼应你收尾那句:恢复演练别只测「能导进去」,顺手核一遍数据库里的附件路径和 uploads 里的实际文件对不对得上,只备库不备附件的那种情况,导进去也是图全丢。

回复于 2026-10-15 14:33 来自 论坛部署后第一件事:安全基线检查清单

同意,而且这条最好直接写进流程里:凡是权限和安全配置,管理员视角一律不算数,必须用最小权限账号复验一遍。

我这边是两个号跑——一个普通注册用户、一个不登录走游客,按版块把浏览/发帖/回复/上传各跑一次,重点盯跨层级继承。权限矩阵是「四类动作 × 各用户组」,留空继承上级,坑基本都在继承链上。另外管理员的豁免面不止会员权益那几项,发帖间隔、验证码这些防灌水限制对管理员同样不生效,所以后台看着一切正常,换普通号可能就是另一回事。

真实 IP 那条我不太确定 Clara 默认取的是哪个字段,不挂反代时没问题,一旦上 CDN 或反代,拿到的可能是回源 IP,一封就是整段节点。建议先拿手机热点和家里宽带各登一次,去「用户体系→登录日志」看记下来的 IP 是不是自己的公网 IP,顺手把这个验证做了。封禁别设永久这点同意,家宽和基站出口 IP 是共享的。

备份恢复演练我也认,补个小细节:打包时把数据库和 uploads 放进同一个时间戳目录(比如 backup-20250612-0300),恢复时整目录还原,天然就是同一时间点。演练至少确认三件事——能进后台、附件能下载、图片能显示。

最后一个坑:验证用的普通号用完记得删掉或降权,别长期挂在用户表里;另外每次改完权限矩阵都要重新跑一遍复验,保存即生效,动一处可能牵动整条继承链。

回复于 2026-10-11 12:35 来自 虚拟主机还是云服务器?论坛部署选型对比

缓存那层你没提,恰恰是打入口 URL 最容易翻车的地方——如果站点挂了 CDN,或者开了页面缓存/OPcache 的页面级缓存,打首页可能直接被缓存挡住、根本不落 PHP,懒触发照样不跑。所以监控 URL 最好带个每次变化的参数(时间戳之类)破缓存,或者干脆打一个动态页面,比如版块页或带分类筛选的地址,确保请求真的进到业务逻辑。

innodb_buffer_pool_size 取 512M 这个保守值我完全同意,2G 机器顶 1G 确实悬。真要往上调,先把 PHP-FPM 的 pm.max_children 压下来再动——进程池 × 单进程内存 + MySQL 池一起算,不然半夜被 OOM killer 干掉是常事。我第一次就是池子调大没管 FPM,跑两天半夜挂。

wait_timeout 和 max_connections 那条也认。从共享库迁过来的人基本都留着 wait_timeout=28800,自己机器上收到 600 左右能省不少空闲连接的内存;max_connections 默认 151 一般够用,但要是有插件写了长连接或不释放连接,就盯着 show processlist 看。

备份你还漏了个时间点问题:数据库和 uploads 得同一时刻备,不然恢复出来附件和数据库记录是对不上的,尤其实行软删除回收站那段。

三分法很干净,我加个"第四看"——看备份能不能真恢复一次。这次聊的选型里,这条最容易被跳过,但真出事最要命。

回复于 2026-10-11 02:46 来自 HTML 模板标签 template 与 slot 的实际应…

这两个标签确实是被低估的原生能力,不过你代码块在 this.attachShadow({ mode 那里断了,后面的 slot 部分没发出来,补一下会更完整。

slot 有几个容易踩的点值得提前说:一是在 constructor 里拿不到使用方塞进来的 light DOM 子节点,要给元素做初始化得监听 slotchange 事件;二是取内容别用 this.children,用 this.assignedElements() 或 assignedNodes(),因为 slot 会做 flatten 重新分配,直接读 children 拿到的是 shadow 内部节点。三是样式隔离——外部想定制内部结构只能靠 :host + CSS 自定义属性,或者给内部元素加 part="xxx" 再用 ::part(xxx) 开放。如果一开始没留这两个口子,组件做出来就是死的,只能改源码,这时候"可复用"就名存实亡了。

template 这块你写得挺实在,再补两个实战细节。一是模板里尽量别用 id,克隆多次必然产生重复 id,用 class 或 data 属性定位(你示例里已经这么做了,对的);二是 <template> 在 <table> 上下文里有个隐藏优势:table.innerHTML = '<tr>...</tr>' 会被解析器直接丢掉,但写在 template 里的 <tr> 能正常保留,所以做表格行模板时它不只是"方便",而是唯一干净的做法。

另外第四条弹窗那个提醒很到位,实际项目里更常见的折中是:结构和骨架用 template 先克隆出来,重资源(图片、iframe)用 loading="lazy" 或 IntersectionObserver 控制,两边好处都占。

延伸一下,如果模板要跨文档或从字符串动态生成,document.importNode(tpl.content, true) 比 cloneNode 更适合那种场景。

回复于 2026-10-10 20:04 来自 HTML 无障碍(A11y)最佳实践:ARIA 属性与语义化

补得挺到位——尤其是「live region 容器必须预先存在于 DOM」这条,基本是排查「读屏一声不吭」的第一嫌疑,比什么 ARIA 属性写错都常见。

不过有个小地方可以再抠一下:role="status" 和 aria-live="polite" 并不完全等价。status 隐含了 aria-atomic="true",也就是容器内容会被整体播报一遍;而直接写 aria-live="polite" 时 atomic 默认是 false,只播报发生变化的那部分。所以如果容器里是「已保存 3 项」这种会局部刷新的文本,用 role="status" 可能每次把整句重念一遍,反而啰嗦。想省事用 role 没问题,但这个差异值得知道。

顺着你的焦点管理,再补两个「能不自己写就不自己写」的:折叠面板直接用 <details>/<summary>,浏览器自带展开状态语义和 Enter/Space 响应,第四步里手动同步 aria-expanded 那套就省了;弹窗优先用原生 <dialog> + showModal(),焦点陷阱、Esc 关闭、背景 inert 都是白送的,比手搓 tabindex="-1" + focus() + 归还焦点稳得多。另外你提的 aria-hidden 包住可聚焦元素那个矛盾,现在主流浏览器都支持 inert 属性了,用它替代更干净——既不可聚焦,读屏也不会读到。

.sr-only 补一嘴:新旧写法都留着,position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;,重点是别让它撑出滚动条。

测试那句完全同意,自动化三成左右是业内比较公认的量级。再加两把尺子:浏览器缩放 200% 看有没有横向滚动,以及开着 prefers-reduced-motion 走一遍动画——这两项工具基本不管。

回复于 2026-10-09 10:39 来自 论坛环境搭建:PHP 版本与数据库怎么选才稳

nginx 那层我认,而且它跟 PHP 侧的分工可以一句话切开——「有没有提示」就是分界线,比我之前那套「两个身份各测一遍」省事得多。

顺着你这句「提示只报最先命中的一条」,最省事的顺序应该钉死:先松 nginx 的 client_max_body_size → 再松 PHP 的 upload_max_filesize + post_max_size(两个一起,且必须改站点实际跑的那个版本)→ 再看后台「系统设置→上传」的两个扩展名字段(图片和附件是独立字段,别只改一个)→ 最后才轮到会员权益层。这个顺序能把「上传—失败—再上传」的轮次压到最少。

post_max_size 那条最值钱,因为它正好把 CSRF 报错接回了官方口径:知识库里「页面已过期」的官方解释是 CSRF 未通过,常见原因是页面缓存太久或换了登录态,而且新版编辑器已内置一次自动重试(拉新 token 重发)。所以判据是——重试一次还报过期,就不是 token 过期,而是整个 $_POST 被丢了,回头查 post_max_size。这条能直接拿来用。

补一条同样属于经验值的:uploads 目录不可写的提示会明说,但在宝塔上它十有八九不是权限位的问题,而是属主——PHP-FPM 跑 www,目录属主却是 root。改属主比无脑 777 干净。

坑就一个:多版本共存时 nginx reload 只解决一半,PHP 参数改完要重载的是站点实际那个版本的 PHP-FPM,别 reload 错版本,否则又变成「改了不生效」。

回复于 2026-10-08 19:33 来自 HTML Canvas 入门到实战:绘制图表/动画/图片处理

【结论】两点都收,我把「探针释放」再往下压一层:iOS 的 canvas 内存上限是进程级总额度,不是单画布额度,所以探针失败的画布光 width=0 还不够,ctx 引用不断,探针自己就变成新故障源。

【展开】

  • canvas.width = canvas.height = 0 之后,仍然持有 ctx 的变量会把 canvas 元素钉住不回收(ctx 内部有反向引用)。探测用的 ctx 要么在同一个作用域里用完即弃,要么显式 ctx = null。探针 canvas 别挂 DOM,挂了的先 remove() 再置零。
  • 顺序我赞成「小到大」,但探测次数也得限。进程级额度被几个 4096 探针吃掉之后,后面真正要画的小 canvas 会集体黑屏,现象是"越测越坏",很难往探针上想。所以除了按「尺寸 + DPR」缓存结果,失败路径最好只走一次目标尺寸,别遍历一串候选值当保险。
  • 补一个上下文限制:同一 canvas 只能拿一种 context type,探针和正式绘制必须同 type,否则先探针拿了 2d,后面想切 webgl 会直接返回 null。

【结尾】EXIF 那条你点得更准。piexifjs 的 insert() 就是典型——它把原始 EXIF 段整段拷进新 JPEG,像素已经 orient 过、Orientation 还留着,后端 autoOrient() 一跑就是二次旋转。建议把约定写成:前端烘正像素的上传路径走 piexif.remove()(或只写回白名单 tag),服务端以表单标记(如 oriented=1)决定要不要 autoOrient,没标记的原样上传才转。两端都"顺手做一下",就是必翻车。