Commit Graph
65 Commits
Author SHA1 Message Date
dsh 52ca450ddd fix: SECURITY_TODO #28 评论提交按 IP 限流(5 条/分钟)防灌水刷屏
评论端点未认证即可提交(默认 AllowGuest=true 且即时公开),配合默认
GuestRequireApproval=false 开箱即用状态可被灌水机刷屏。PostComment
复用 #27 的 WindowRateLimiter 按 IP 限流 5 条/分钟,超限 429 +
i18n comments_locked(中英)。

- handlers/comment.go: PostComment(db, limiter),键前缀区分
- main.go / security_test.go: 评论限流器接线
- 测试: TestCommentRateLimited(同 IP 5 次成功、第 6 次 429、
  其他 IP 不受影响)

注: 可选项“新部署默认 GuestRequireApproval=true”为产品决策,未随本项实施。
2026-08-27 21:39:28 +08:00
dsh 4c32267b47 fix: SECURITY_TODO #27 注册接口按 IP 限流(10 次/小时)防批量注册
register/api 未认证端点可被无限批量注册:bcrypt cost 12 与用户名唯一
检查不构成成本屏障。新增 WindowRateLimiter(固定窗口、有界 map、与
#10 相同的淘汰策略),Register 按 IP 限流 10 次/小时,超限 429 +
i18n register_locked(中英)。

- handlers/rate_limit.go: 通用固定窗口限流器(可注入时钟便于测试)
- handlers/auth.go: Register(db, limiter),键前缀与登录限流器区分
- main.go: 注册限流器接线;测试环境同步
- 测试: TestWindowLimiterFixedWindow / TestRegisterRateLimited
  (同 IP 10 次成功、第 11 次 429、其他 IP 不受影响)

注: #28 评论限流将复用同一限流器,在下一个 commit 实施。
2026-08-27 21:38:49 +08:00
kevin 2e3888d5b1 fix: 完成 P0 安全修复 #26——请求体大小限制防未认证内存耗尽 DoS
- 新增 middleware/bodylimit.go:BodyLimit 中间件,非 multipart 统一
  4 MiB(覆盖文章正文上限);multipart 按平台上传策略派生(启用类型
  限制与全局默认取最大 +1 MiB 开销,下限不低于编译期默认)。
  Content-Length 已知且超限读体前直接 413,其余经 MaxBytesReader 截断
- handlers/api.go:bindJSON 识别 *http.MaxBytesError → 413 +
  request_too_large(i18n 中英新增)
- main.go:中间件顺序调整为 SetUserContext → BodyLimit → CSRF——
  必须先于 CSRF(其解析 multipart 会读取整个请求体),SetUserContext
  提前使 413 文案可按请求语言翻译;测试环境链同步(security_test.go)
- middleware/auth.go:apiAuthError 更名 apiError(BodyLimit 复用)
- 新增 handlers/bodylimit_test.go:超限 JSON 两种形态(已知长度/
  chunked)413、正常体放行至认证层、3MB multipart 拒绝且附件表零写入、
  GET 不受影响
- SECURITY_TODO.md:#26 勾选完成并记录验证;API 化复审新增 #26–#32
  待办清单(P1 注册/评论限流、P2 favicon 魔数校验/最后管理员竞态/
  置顶权限、P3 零碎项)与执行顺序

go build / vet / test -race ./... 全绿
2026-08-27 21:23:19 +08:00
kevin 0f1dc7969d feat: footer 显示 Git 提交版本与编译时间(-ldflags 注入 buildinfo) 2026-08-27 20:14:28 +08:00
kevin 443eccb8a6 docs: TODO_API_REFACTOR 全部 22 项勾选完成 2026-08-27 20:08:19 +08:00
kevin 7d12167380 docs: README 路由表同步 API 化(页面/API 分离 + API 约定章节) 2026-08-27 20:08:11 +08:00
kevin 7f0beb5b0c test: 新增 api_test.go——/api 认证/越权/文章/评论/注册端到端断言
- TestAPIAuthRequiredReturnsJSON:未登录打 /api 路由返回 401 JSON
  (携带合法 CSRF 模拟真实前端顺序)
- TestAPIAdminRequiredReturnsJSON:非管理员 403 api_forbidden
- TestAPICSRFHeaderRequired:缺 CSRF 头 403
- TestAPILoginRoleRedirect:登录成功按角色返回 redirect(/admin | /)
- TestAPIArticleCRUD:admin 创建(slug 自动生成)/更新/校验 400/软删除
- TestAPIMyArticlesOwnership:跨作者 update 404 / delete 无效 / 创建成功
- TestAPICommentValidationCodes:评论校验 code + 成功 redirect 锚点
- TestAPIRegisterConflictAndMismatch:409 用户名冲突 / 400 密码不一致
- 测试环境补齐:env 路由增 /api/admin/articles 与 /api/my/articles CRUD;
  attachments URL 迁 /api(upload/delete 改 DELETE+CSRF 头),
  p3 附件用例 URL 同步
- 新增 anonSession/csrfTokenFrom/loginRequest/itoa/deleteAttachment 辅助
- go build/vet/test ./... 全绿
2026-08-27 19:59:25 +08:00
kevin 76b744d5b5 feat: 个人资料接口 JSON 化——POST /api/profile
- profile.go:UpdateProfile 改 JSON 绑定(profileRequest),移除内嵌
  头像文件分支(头像统一走 /api/profile/avatar,XSS 链校验保留在
  UploadAvatar);错误改 APIError(400 profile_wrong_password/
  profile_password_short/profile_email_invalid、404 user_not_found、
  500),成功 {ok,redirect:/profile?saved=1}
- main.go:/profile 组旧 POST 移除,/api/profile[/avatar] 分组收敛
- profile.html:主表单改 blogAPI(头像 cropper 流程独立不变),
  新增 profileError 错误区
- 测试:TestProfilePasswordMinLength/TestProfileEmailValidation 改
  JSON(400+code);TestUpdateProfileAvatarRejectsNonImage 改打
  /api/profile/avatar;env 路由同步 /api/profile
- go build/vet/test 全绿
2026-08-27 19:56:03 +08:00
kevin efc30156b9 feat: 站点设置接口 JSON 化——/api/admin/settings/*
- settings.go:5 组 save handler 改 JSON 绑定(siteSettingsRequest/
  uploadSettingsRequest/downloadSettingsRequest/commentSettingsRequest/
  navLinkSettingsRequest);request struct 变更子函数签名(不再触碰
  c.PostForm),upload/navlink/download 的 action 分发保留,未知
  action 返回 400;enabled 用 *bool(nil 沿用旧默认启用语义)
- favicon/logo 上传拆出:POST /api/admin/settings/site/favicon|logo
  (multipart,图片类别校验 + 旧本地文件替换),SiteSettingsSave 只
  处理文本/URL/clear(存储路径校验保留 illegal_dir 400)
- main.go:设置旧 POST 路由移除,新 /api/admin/settings 分组注册
- 模板:base.html 新增 blogSettingsForm 委托(data-api-url/data-action/
  data-confirm → POST + redirect/alert);settings_site 主表单 JSON +
  logo/favicon 选择即上传;navlinks/upload/download/comments 页全部
  小表单改委托(约 14 个)
- blogForm:剔除 file 字段(文件走 multipart)
- 测试:TestStorageDirTraversalRejected、TestAddUploadFileTypeRejectsDangerousExtensions
  更新 JSON 断言;env 路由补 /api/admin/settings
- main_test 冒烟补设置 API 断言;go build/vet/test 全绿
2026-08-27 19:52:07 +08:00
kevin 856d414295 feat: 用户管理接口 JSON 化——/api/admin/users CRUD
- admin_user.go:userForm 加 json tags,parseUserFormJSON(绑定+去白,
  ID 取路由参数);UserCreate/Update/Delete 校验错误改 APIError
  (400 校验、409 user_username_exists、403 自防/最后管理员、404
  user_not_found、500),成功 {ok,redirect:?saved=1&msg=...}
- i18n:新增 user_not_found(中英)
- main.go:旧 POST /admin/users/new|:id/edit|:id/delete 移除,
  改 POST/PUT/DELETE /api/admin/users[/:id](自防屏障保留)
- user_form.html:表单改 blogAPI(FormID 分流 POST/PUT),错误内联
- user_list.html:删除改 blogDelete 委托
- p2/security 测试:环境路由同步 + 用户校验/注入用例改 JSON 断言
  (TestAdminUserPasswordAndEmailEnforcement、TestAdminUserRoutesRejectNonNumericIDs)
- main_test 冒烟补用户 API 断言;go build/vet/test 全绿
2026-08-27 19:44:09 +08:00
kevin 5b227b71ef feat: 文章 CRUD 接口 JSON 化——/api/admin|my/articles
- article.go:articleForm 加 json tags,新增 parseArticleFormJSON(绑定+
  去白);ArticleCreate/ArticleUpdate/ArticleDelete 增加 redirectPath 参数,
  校验失败改 APIError 400、DB 错误 500,成功 {ok,redirect}(原 renderForm
  分支移除);id 改用 parseUintParam 数值化
- my_articles.go:MyArticleCreate 复用 ArticleCreate(db, /my/articles);
  MyArticleUpdate/MyArticleDelete 保留 author_id 所有权约束(404
  article_not_found);my 更新不触碰标签(表单无该字段,与旧行为一致)
- main.go:admin/my 文章旧 POST 路由移除,改 POST/PUT/DELETE
  /api/admin/articles[/:id] 与 /api/my/articles[/:id]
- 模板:article_create.html/my_article_form.html 表单改 blogAPI 提交
  (easyMDE.value() 同步正文、editor e.submitter 分流草稿/发布),错误内联
  articleError/myArticleError;article_list.html/my_articles.html 删除改
  blogDelete 委托(base.html 新公共函数,DELETE + reload 保筛选状态)
- main_test 冒烟补文章 CRUD 断言;go build/vet/test 全绿
2026-08-27 19:40:02 +08:00
kevin a0061b14e3 feat: 评论接口 JSON 化——/api/article/:slug/comments 与 admin 审核操作
- comment.go:commentForm 加 json tag;PostComment 校验分支改 APIError
  (404 article_not_found、403 comments_disabled/comments_guests_disabled、
  400 校验码、500 article_error),保留 guest 令牌与 flash 机制,成功返回
  {ok,redirect:/article/:slug#comment-N,comment_id}
- api.go:新增 APIErrorf(支持 %d/%s 占位符键如 comments_too_long)
- admin_comment.go:approve/reject/delete 改 JSON(parseUintParam 拒绝非
  数值 id 400),成功带原 ?saved=1&msg= 查询串 redirect
- main.go:PostComment 迁入 /api;评论审核三操作迁入 /api/admin/comments
- article.html:评论表单改 blogAPI 提交,错误内联 commentError div
- comment_list.html:审核操作改 to commentAct() 委托(confirm 在函数内,
  取消不发请求),成功 reload 保持筛选状态
- 测试:security_test env 路由同步 /api;session_upload 评论用例改 JSON
- main_test 冒烟补评论 API 路由断言;go build/vet/test 全绿
2026-08-27 19:36:09 +08:00
kevin 923d57475b docs: TODO_API_REFACTOR 勾选任务 7/8(认证 API 与模板) 2026-08-27 19:31:38 +08:00
kevin 7062c755a3 feat: 认证接口 JSON 化——/api/auth/login|register|logout
- auth.go:Login/Register 改 JSON 绑定(loginRequest/registerRequest),
  错误码复用 i18n 键:登录失败 login_error(401)、限流锁定 login_locked(429)、
  注册校验 400、用户名冲突 user_username_exists(409)、注册禁用
  registration_disabled(403);成功返回 {ok,redirect}(角色定向 /admin 或 /)
- main.go:旧 POST /login /register /logout 移除,迁入 /api/auth 分组
- i18n:新增 registration_disabled(中英)
- security_test.go:env 路由改 /api/auth/*,e.login 改 JSON 登录,
  新增 postJSON/respCode/respRedirect/respOK 测试辅助
- p2_validation_test.go:TestLoginRateLimited/TestLoginTimingDoesNotRevealUser/
  TestRegisterRejectsInvalidEmail 迁移 JSON 断言(429/401/409/400)
- base.html:logout-form 全局委托 fetch + login/register 模板 id/JS
- main_test.go:冒烟断言补 /api/auth 三端点
- go build/vet/test ./... 全绿
2026-08-27 19:31:29 +08:00
kevin 2c46846309 feat: 搬移附件/头像端点模板 JS 至 /api 路径
- article_create.html:附件上传/删除/列表 fetch 改 /api/admin/articles/attachments
  (删除改用 DELETE 方法),与新路由匹配
- profile.html:头像上传 fetch 改 /api/profile/avatar
- 验证:grep 确认模板/static 无残留旧 URL,go build/vet/test 全绿
2026-08-27 19:26:19 +08:00
kevin b1e9d9cae6 feat: API 基建——/api JSON 接口统一契约与认证分支
- handlers/api.go:APIOK/APIError/bindJSON,统一 {ok,redirect,data} /
  {ok:false,code,error} 响应契约(code=i18n 键,error 按请求语言翻译)
- i18n:新增 api_error/api_unauthorized/api_forbidden/api_invalid_request(中英)
- middleware/auth.go:AuthRequired/AdminRequired 按 /api 前缀分支:
  JSON 401/403(页面保持 302),新增 isAPIRequest + apiAuthError
- main.go:路由注册提取为 registerRoutes;建立 /api 分组并搬移附件三件套
  (admin/my)与 /api/profile/avatar(旧 /admin|my/articles/attachments、
  /profile/avatar 路由移除)
- handlers/login_ratelimit.go:loginRateLimiter 导出为 LoginRateLimiter
- templates/layouts/base.html:blogAPI/blogForm/blogShowError 共享 fetch 助手
- main_test.go:TestRegisterRoutesSmoke 冒烟测试(注册期 gin 静态/参数
  冲突即 panic + 关键 /api 路由断言)
- go build/vet/test ./... 全绿
2026-08-27 19:24:43 +08:00
kevin 5afc7fa9be docs: README 项目结构章节增加目录作用总览表 + 根目录文件说明 2026-08-27 19:05:37 +08:00
kevin f307781f58 docs: 全部 Go 代码注释汉化
- 48 个 Go 文件所有注释(行注释/块注释/行尾注释,含 _test.go)翻译为中文
- 保留技术标识符:SECURITY_TODO(n)、unsafe-inline、sqlite/mysql、路由参数等
- 代码、字符串字面量、日志消息保持英文原文,零逻辑改动
- go build/vet 通过,go test -count=1 ./... 全绿
2026-08-27 19:03:03 +08:00
kevin fed6bf9570 docs: 更新 README(安全加固章节 + 配置结构 + 项目结构 + 快速开始说明) 2026-08-27 18:40:09 +08:00
kevin 46d6f3cd94 fix: 完成 P3 安全修复 #14-17(文件类型校验/默认关闭 Gravatar/RSS 固定 URL/bcrypt 成本) 2026-08-27 18:34:17 +08:00
kevin c9f858b626 fix: 完成 P2 安全修复 #9-13 #22-25
- #9 CDN 本地化:marked/DOMPurify/highlight.js/cropperjs/easymde 入 static/vendor(go:embed),Tailwind 改静态构建(scripts/build_tailwind.sh),CSP 收紧为 default-src 'self'
- #10 登录限速:IP+用户名维度 5 次失败锁 15 分钟,内存实现有界(handlers/login_ratelimit.go)
- #25 计时侧信道:用户不存在时执行 dummy bcrypt 抹平时间差(随 #10 实施)
- #11 配置文件权限 0640
- #12 首启随机一次性密码(弃用 admin/admin)
- #13 unix socket 660 + 代理用户加组提示
- #22 storage_dir 路径穿越校验(单安全路径段)
- #23 密码最小长度统一(改密/建号/重置),#24 邮箱格式统一校验
- 新增 13 个单元测试;go test ./... 含 -race 全绿
2026-08-27 18:10:22 +08:00
kevin e314b05670 fix: 修复 P1 复审漏洞 #20 #21(禁用用户会话失效 + 头像 XSS 链)
- #20 AuthRequired 改为 AuthRequired(db):受保护路由每次回库校验
  Status == StatusNormal 且未软删,失败清 session(保留 lang/csrf_token,
  与登录轮换口径一致)并 302 /login;session user_id 先断言为数值再入
  GORM(呼应 #19),AdminRequired 同步加固;SetUserContext 仅在用户
  存在且状态正常时置 is_logged_in——禁用用户发评论不再自动通过,
  回落游客审核策略
- #21 头像两个分支(UploadAvatar / UpdateProfile)强制
  Category == image,解码失败直接拒绝、删除"回退存原始字节"路径,
  统一经 processAvatar 解码→256px 缩放→JPEG 重编码;addUploadFileType
  增加危险扩展黑名单(.html/.htm/.xhtml/.xht/.svg/.xml/.js/.mjs),
  拒绝添加并在上传设置页提示(模板 + 中英 i18n)
- 附带修复:processAvatar 依赖的 png/gif 解码器此前未注册(旧代码靠
  回退存原始字节掩盖,PNG 头像从未真正处理过),补 blank import
- 新增回归测试 session_upload_security_test.go(6 用例:禁用/锁定/
  软删旧 cookie 302、锁定用户评论转 pending、6 组危险扩展拒绝、
  伪装扩展名头像拒绝且磁盘零写入、正常图片转存 .jpg;已变异验证:
  去掉任一修复对应测试即失败)
- SECURITY_TODO.md 勾选 #20/#21 并更新执行顺序
2026-08-27 17:35:46 +08:00
kevin f7870e8557 fix: 修复 P0 复审漏洞 #18 #19(数据库泄露 + SQL 注入)
- #18 /uploads 改为白名单子目录挂载(attachments/avatars/logos + 配置的
  存储目录),存储根不再整体暴露,blog.db 不可被下载;禁用目录列表,
  拒绝 .. 穿越与反斜杠
- #19 admin 用户管理三个 handler 的路由参数先解析为数值(uintFormID),
  非数值直接 302,消除 GORM First() 字符串条件 SQL 注入
- 新增回归测试 main_test.go(4 用例)与 TestAdminUserRoutesRejectNonNumericIDs
  (已变异验证:旧代码下注入用例失败)
- SECURITY_TODO.md 勾选 #18/#19 并更新执行顺序
2026-08-27 17:08:22 +08:00
kevin 38cd09f723 fix: 安全加固,修复 P0/P1 安全漏洞
P0(高危):
- 新增全局 CSRF 中间件(同步器令牌),覆盖全部 30 个表单与 AJAX 请求
- 修复附件上传/列表/删除越权(IDOR),增加 admin/上传者/文章作者所有权校验
- 登录/注册成功后会话轮换,修复会话固定
- 会话密钥改用 crypto/rand 生成,配置缺失 secret 时拒绝启动

P1(中危):
- session 与 comment_uid cookie 增加 Secure/SameSite 标志
- 新增安全响应头:CSP、X-Content-Type-Options、X-Frame-Options、HSTS 等
- 新增 web.trusted_proxies 配置,修复 X-Forwarded-For 伪造
- 修复浏览量记录 goroutine 访问已回收 gin.Context 的数据竞争

补充 17 个安全回归测试(middleware/handlers),go test -race 全绿
2026-08-19 12:33:09 +08:00
kevin c3f8be58c9 Merge pull request 'fix: 静态资源 URL 加版本号,避免浏览器缓存旧 404 导致持续白屏' (#3) from dsh/go_blog:fix/static-cache-busting into main
Reviewed-on: kevin/go_blog#3
2026-08-18 06:25:32 -04:00
dsh c5218f35a1 fix: cache-bust static asset URLs (?v=2)
Browsers cached the pre-fix 404 responses for /static/js/markdown.js and
/static/css/markdown.css (responses carry Cache-Control max-age=14400),
so users kept seeing broken pages after redeploy. A versioned query
string forces clients to refetch the assets; bump ?v= whenever the
static files change.
2026-08-18 06:25:01 -04:00
kevin 9c0ba8cd6d Merge pull request 'fix: static 资源嵌入二进制,修复部署后 Markdown 内容不显示' (#2) from dsh/go_blog:fix/embed-static-assets into main
Reviewed-on: kevin/go_blog#2
2026-08-18 06:18:47 -04:00
dsh bd3a875403 fix: embed static assets into the binary and never render blank content
The merged markdown-rendering change served /static from a disk directory,
but the live deployment only replaced the binary and templates, so
/static/js/markdown.js and /static/css/markdown.css 404'd and every
article/comment rendered empty (BlogMD was undefined and threw).

- main.go: serve /static from an embedded FS (go:embed) — deployments
  now only need to replace the executable; no static dir to copy.
- install_linux.sh: drop the static directory copy (no longer needed).
- article.html / comment_list.html: if BlogMD is unavailable, fall back
  to plain (HTML-escaped) text for the article body, comment bodies and
  the comment preview, so content is never blank.
- README: document the static/ directory.

Verified locally: /static/* serve 200 even with the disk directory
removed; normal render path (25 checks) and the fallback path both pass.
2026-08-18 06:18:08 -04:00
kevin 5f21c73044 Merge pull request '完善前端 Markdown 渲染:排版样式、代码高亮、标题锚点、图片灯箱、复制按钮' (#1) from dsh/go_blog:feat/markdown-rendering into main
Reviewed-on: kevin/go_blog#1
2026-08-18 06:12:12 -04:00
dsh 83866f6de2 feat: enhance frontend Markdown rendering
- Add shared BlogMD renderer (static/js/markdown.js): marked + DOMPurify
  + highlight.js pipeline with GFM support, heading id slugger with
  CJK-aware anchors, syntax highlighting, per-block copy button and
  language badge, lazy images with lightbox, external links opened
  safely in new tabs, tables wrapped for small screens.
- Add .md-body typography styles (static/css/markdown.css) so articles,
  comments and editor previews render with proper headings, tables,
  lists, blockquotes and code blocks (previously the prose classes had
  no effect because the Tailwind typography plugin is not loaded).
- Fix marked options that were set after parsing and removed from
  marked v4+ (mangle/headerIds no-ops).
- Pin CDN versions (marked 15.0.12, dompurify 3.4.13, highlight.js
  11.12.0) instead of floating 'latest' URLs.
- Wire EasyMDE preview/side-by-side to BlogMD in admin and user
  article editors; use BlogMD for comment bodies on the article page,
  admin comment list and comment preview.
- Serve /static in main.go and deploy it in install_linux.sh.
2026-08-18 06:05:53 -04:00
kevin 7d9ccdde75 up 2026-06-24 11:46:08 +08:00
kevin 2875918289 修改运行路径 2026-06-24 11:32:45 +08:00
kevin 1fc933e014 修复CDN环境下获取真实客户端IP的问题 2026-06-24 11:18:56 +08:00
kevin fe69a2e38b up 2026-06-23 15:16:50 +08:00
kevin d8779ed5bf 修复安装问题 2026-06-23 14:57:36 +08:00
kevin a316932d3f up 2026-06-23 11:23:33 +08:00
kevin 5f8c7be5ac up 2026-06-23 10:45:37 +08:00
kevin fe72cbaf9d 删除过期文件 2026-06-23 10:37:08 +08:00
kevinandClaude Fable 5 d9bb91af00 feat: add navigation links management and user registration
Features:
- Custom navigation links management in admin settings
- Multi-language support for navigation links (Chinese/English)
- Control link behavior (open in new window)
- Sort order and enable/disable toggle for nav links
- User registration system with validation
- Admin can enable/disable user registration
- Registration form with username, display name, email, password
- Link to registration from login page

Navigation Links:
- Admin interface to add/edit/delete custom nav links
- Support for both internal and external URLs
- Display links in header navigation bar
- Respects language preference

User Registration:
- Username validation (3-32 characters, alphanumeric + underscore/dash)
- Password validation (minimum 6 characters)
- Password confirmation matching
- Optional display name and email fields
- Can be toggled on/off by admin in site settings

Templates:
- Added navigation links settings page
- Added user registration page
- Updated login page with registration link
- Updated base layout to render custom nav links

Documentation:
- NAV_LINKS_FEATURE.md - Navigation links feature documentation
- REGISTRATION_FEATURE.md - User registration documentation
- IMPLEMENTATION_SUMMARY.md - Overall implementation summary

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 21:01:38 +08:00
kevinandClaude Fable 5 2bdc368399 feat: implement tag management and search functionality
Features:
- Tag system with multi-language support (Chinese/English)
- Auto-create tags when associating with articles
- Tag filtering on homepage with visual indicators
- Full-text search across title, summary, and content
- Tag cloud sidebar showing article counts
- Search page with results display

Technical Implementation:
- Database models: Tag, ArticleTag (many-to-many)
- Tag count caching for performance
- Automatic tag slug generation
- Responsive design with mobile support
- I18n support for all new UI elements

Bug Fix:
- Fixed SQL column ambiguity error in tag filtering
- Added table prefix to ORDER BY clause in publishedArticleOrder

Routes:
- GET /search - Search results page
- GET /?tag=<slug> - Filter articles by tag
- GET /api/articles?tag=<slug> - API with tag filter support

Templates:
- Added tag input field to article create/edit form
- Added search box to homepage header
- Added tag cloud sidebar on homepage and search page
- Created new search results page template

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:59:32 +08:00
kevinandClaude Fable 5 b4983f1d4d feat: add icons to homepage header and navigation links
- Add blog icon to homepage welcome title
  - Newspaper/blog icon (48px, blue)
  - Centered with flex layout

- Add lightning icon to homepage subtitle
  - Lightning bolt icon (24px, gray)
  - Represents speed and modern tech

- Add home icon to navigation home link
  - House icon with clean design
  - Consistent styling with other nav elements

- Add login icon to navigation login link
  - Login/sign-in icon
  - Only shown when user is not logged in

- Use Heroicons SVG for consistency
- Responsive and accessible design
- Smooth hover transitions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:24:30 +08:00
kevinandClaude Fable 5 b0ca76e8dc feat: add custom publish time and last updated time display
- Add custom publish time field to article create/edit forms
  - Support datetime-local input for manual time setting
  - Auto-set on first publish if left blank
  - Works for both admin and user article forms

- Display last updated time on article detail page
  - Show both published time and last updated time
  - Auto-maintained by GORM on each update
  - Format: YYYY-MM-DD HH:MM

- Add i18n support for new fields
  - article_published_at: Published Time / 发布时间
  - article_published_at_hint: Leave blank to auto-set on publish / 留空则在发布时自动设置
  - article_last_updated: Last Updated / 最后更新

- Update handlers and templates
  - handlers/article.go: Add parsePublishedAt/formatPublishedAt functions
  - handlers/home.go: Add formatUpdateTime function
  - handlers/my_articles.go: Support custom publish time in user articles
  - templates: Add datetime-local inputs and time display

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:20:19 +08:00
kevinandClaude Fable 5 b600eac21c feat: add favicon settings to site configuration
- Add Favicon field to SiteSetting model with FaviconIsURL() helper
- Implement favicon upload and management in site settings page
- Support both external URL and local file upload for favicon
- Add favicon display in page header with automatic URL detection
- Add i18n translations for favicon settings (EN/ZH)
- Update middleware and helpers to pass favicon data to templates
- Include migration script for existing databases

Users can now customize their site's favicon from /admin/settings/site
by either providing an external URL or uploading a local image file
(recommended formats: .ico, .png, .svg, 16x16 or 32x32 pixels).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 20:09:05 +08:00
kevinandClaude Fable 5 da7a39c1c8 feat: implement comprehensive reading analytics system with bot detection
Features:
- Add article view tracking with automatic deduplication (per user/IP)
- Implement intelligent bot detection (35+ patterns: Google, Bing, Baidu, GPTBot, etc)
- Create admin analytics dashboard with statistics and filtering
- Display view count and comment count on article cards
- Add search-based article filter (replaced dropdown for scalability)

Analytics Dashboard:
- Global stats: total views, human/bot views, unique IPs/users
- Top 20 articles ranking with detailed metrics
- Detailed view records with time, article, user, IP, user-agent
- Filters: article title search, IP search, show/hide bots
- Pagination support (50 records per page)

Technical Implementation:
- Async view recording (non-blocking)
- Dual deduplication (application + database layer)
- Database indexes for performance optimization
- Batch query for comment counts
- Full i18n support (Chinese/English)

Files Added:
- models/article_view.go: View tracking model
- models/bot_detector.go: Bot detection logic
- handlers/admin_analytics.go: Analytics page handler
- templates/admin/analytics_views.html: Analytics UI
- test_analytics.sh: Testing script
- Documentation: implementation guide, usage guide, changelog

Files Modified:
- handlers/home.go: Add view recording and comment count queries
- models/db.go: Add ArticleView to auto-migration
- main.go: Add analytics routes
- i18n/i18n.go: Add 35+ translation keys
- templates/admin/dashboard.html: Add analytics entry link
- templates/pages/home.html: Display view/comment counts on cards

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:57:12 +08:00
kevinandClaude Fable 5 5ec783e471 feat: implement waterfall layout with smart image positioning and infinite scroll
- Add single-column layout for homepage articles
- Implement smart image positioning based on aspect ratio:
  * Landscape images (ratio > 1.2) displayed on top
  * Portrait/square images (ratio ≤ 1.2) displayed on left side
- Add infinite scroll with lazy loading
- Create new API endpoint /api/articles for pagination
- Add loading indicator and 'no more articles' message
- Optimize performance with image lazy loading and scroll throttling
- Add responsive layout for mobile devices

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:31:18 +08:00
kevin 7047b33358 up 2026-06-22 19:07:13 +08:00
kevinandClaude Fable 5 eb9a65097e fix: redirect users based on role after login
- Admin users redirect to /admin dashboard
- Regular users redirect to home page /
- Prevents authorization errors for non-admin users

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:05:39 +08:00
kevinandClaude Fable 5 aca4983447 fix: allow clearing display name in profile settings
- Remove restriction that prevented setting display_name to empty
- Users can now clear display name to fall back to username display
- Trim whitespace from display_name input

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:02:59 +08:00
kevinandClaude Fable 5 95cbd0240f feat: display user's display name next to avatar in navigation
- Show display name (or username as fallback) next to avatar
- Improve UI readability and user experience
- Avatar and name together act as dropdown trigger

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 19:01:24 +08:00
kevinandClaude Fable 5 d64c839b10 feat: add role-based access control and user article management
- Add AdminRequired middleware to all /admin routes for proper authorization
- Restrict admin panel, comments, users, settings to admin role only
- Create separate /my/articles routes for regular users to manage their own articles
- Add MyArticlesPage handler for users to view/edit/delete their own content
- Update navigation menus with role-based visibility
- Admin dropdown shows only 'Admin Panel' link, other features in dashboard
- Regular users see 'My Articles' link in profile dropdown
- Add i18n translations for 'my_articles' and 'comment_manage' keys
- Fix permission boundary issues where author role could access admin settings

Security improvements:
- Platform settings now require admin role
- Comment moderation requires admin role
- User management requires admin role
- Regular users can only manage their own articles

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-22 18:49:33 +08:00