完善 D1 后台管理、真实访问统计、R2 图片上传,并修复 Dither 动画 - #2
Open
0de1l wants to merge 10 commits into
Open
Conversation
The homepage WebGL frame loop was drawing, but production sampling showed the fragment shader's time uniform only reached the GPU at initialization. Updating the memoized uniform object was not enough for the mounted ShaderMaterial, so the frame loop now writes to the live material uniforms and marks them for upload. Constraint: Preserve the original MiniLoad shader and visual style Rejected: Increase wave speed again | frame sampling showed the deployed issue was uniform upload, not motion magnitude Confidence: high Scope-risk: narrow Directive: Verify future animation fixes by sampling the canvas frame and uniform updates before changing shader visuals Tested: npm run build; production Chrome canvas sampling showed time uniform advancing and background frame hashes changing Not-tested: Cloudflare Pages redeploy runtime
The admin dashboard previously displayed fixed demo traffic numbers. This adds a lightweight first-party analytics event table, client-side pageview reporting, and an authenticated admin summary endpoint so deployed traffic metrics come from Cloudflare D1 instead of hardcoded arrays. Constraint: Cloudflare Pages uses D1 binding name DB and Edge API routes. Rejected: External analytics service | harder to surface inside the existing admin dashboard and adds another service dependency. Rejected: Store raw IP addresses | unnecessary for visit counting and worse for privacy. Confidence: high Scope-risk: moderate Directive: Keep /admin and /api paths excluded from tracking or admin refreshes will pollute traffic metrics. Tested: npm run build Tested: npx tsc --noEmit Tested: scoped eslint on analytics routes, tracker, layout, admin page, and auth route Not-tested: Cloudflare Pages runtime D1 write/read after deployment
The homepage tools and bookshelf were hardcoded in the page component, so changing them required code edits and redeploys. This moves those modules behind D1-backed read/write APIs, keeps the current homepage content as default seed data, and adds a HomePage admin section with Tools and Books editors. Constraint: First version uses cover URLs only; local upload/R2 storage is intentionally left out. Constraint: Cloudflare Pages D1 binding is expected to remain DB. Rejected: Commit a manual SQL migration file | schema is created lazily by the runtime and the user asked to exclude the SQL file. Rejected: Store React icons in D1 | database stores stable icon keys and the UI maps them to Feather icons. Confidence: high Scope-risk: moderate Directive: Keep home-module shared UI constants separate from server-only D1 helpers so Client Components do not import Cloudflare server-only code. Tested: npm run build Tested: npx tsc --noEmit Tested: scoped eslint on home module APIs, home module libs, homepage, and admin page Not-tested: Cloudflare Pages runtime D1 seed/write/delete after deployment
The post editor preview panel grew with rendered content while the source panel used its own scroll area, making long Markdown editing hard to compare. This fixes the preview panel height, lets it scroll internally, syncs preview scroll from source scroll by ratio, and keeps HomePage collapsed by default in the admin nav. Constraint: Keep the implementation lightweight without Markdown AST source-to-preview mapping. Rejected: Bidirectional scroll sync | can cause feedback loops and awkward jumps because Markdown source height and rendered preview height are not linearly equivalent. Confidence: high Scope-risk: narrow Directive: If precise preview alignment becomes necessary, add block-level Markdown position mapping instead of making this ratio sync more complex. Tested: npx eslint src/app/admin/page.tsx Tested: npx tsc --noEmit Tested: npm run build Not-tested: Manual browser scroll feel in deployed admin
The admin sidebar used broad transition-all behavior and abrupt label mounting, which made expand/collapse feel janky. This narrows the animated properties, applies an ease-out cubic bezier curve to the sidebar and nav items, and removes the committed analytics SQL reference file because runtime schema creation already owns that migration path. Constraint: Analytics schema is still created lazily by ensureAnalyticsSchema at runtime. Rejected: Keep committed analytics-schema.sql | it is redundant with runtime CREATE TABLE IF NOT EXISTS behavior and the user asked to remove it from GitHub. Confidence: high Scope-risk: narrow Directive: Keep schema creation in code authoritative unless a formal migration system is introduced. Tested: npx eslint src/app/admin/page.tsx Tested: npx tsc --noEmit Tested: npm run build Not-tested: Manual browser feel on deployed admin sidebar
The post editor preview used a small regex renderer, so code fences and other Markdown features did not match published posts. This adds a remark/rehype preview renderer, reuses the MarkdownContent enhancement layer for code blocks, and keeps source-to-preview scroll sync in the editor. Constraint: Admin preview now carries the Markdown renderer in the admin client bundle. Rejected: Add only CSS for pre/code blocks | would still leave preview behavior inconsistent with published posts. Rejected: Server-render preview API for this pass | lower bundle size but adds debounce/request state and is unnecessary for the current admin-only surface. Confidence: high Scope-risk: moderate Directive: If admin load time becomes a problem, move renderMarkdownPreviewHtml behind an authenticated preview API instead of reverting to regex parsing. Tested: npx eslint src/app/admin/page.tsx src/components/markdown-content.tsx src/lib/markdown-preview.ts Tested: npx tsc --noEmit Tested: npm run build Not-tested: Manual deployed admin preview against every Markdown extension
The HomePage Books editor only accepted externally hosted cover URLs. Cloudflare Pages now has an ASSETS R2 binding, so the admin can upload local cover images, store them in R2, and persist the returned /api/assets URL in the existing cover field. Constraint: R2 binding name is ASSETS and admin authentication continues to use the Authorization header with ADMIN_PASSWORD. Constraint: First pass supports Books cover uploads only; other image fields can reuse the upload endpoint later. Rejected: Public R2 bucket URLs | proxying through /api/assets keeps the bucket private and avoids adding a custom public bucket domain now. Confidence: high Scope-risk: moderate Directive: Keep uploads behind admin auth and do not expose direct R2 credentials to the browser. Tested: npx eslint src/app/admin/page.tsx src/app/api/admin/upload/route.ts src/app/api/assets/[...key]/route.ts Tested: npx tsc --noEmit Tested: npm run build Not-tested: Live Cloudflare Pages R2 write/read after deployment
Cloudflare Pages failed while publishing the Function after the build and asset upload completed. The R2 bucket was exposed as ASSETS, which overlaps with the common Pages/Workers static asset binding name, so the upload and read routes now use an explicit R2_ASSETS binding instead. Constraint: Cloudflare Pages Functions may reserve ASSETS for static assets.\nRejected: Keep ASSETS binding | publish can fail or collide with static asset access.\nConfidence: high\nScope-risk: narrow\nDirective: R2 upload/read routes expect the binding name R2_ASSETS; keep the Cloudflare dashboard binding in sync.\nTested: npx eslint src/app/api/admin/upload/route.ts src/app/api/assets/[...key]/route.ts\nTested: npx tsc --noEmit\nTested: npm run build\nNot-tested: Cloudflare Pages redeploy after dashboard binding rename
The R2 upload flow was already working for book covers, so the admin editor now reuses that same endpoint for posts, daily entries, and moments instead of keeping those sections URL-only. Post uploads insert Markdown image syntax at the cursor, while daily and moment uploads fill their Image URL fields. Constraint: Keep the existing /api/admin/upload endpoint and R2_ASSETS binding unchanged.\nRejected: Add separate upload APIs per content type | duplicates auth, validation, and R2 handling.\nConfidence: high\nScope-risk: narrow\nDirective: Keep future image upload surfaces routed through uploadImageAsset unless storage requirements diverge.\nTested: npx eslint src/app/admin/page.tsx\nTested: npx tsc --noEmit\nTested: npm run build\nNot-tested: Browser upload interaction against live Cloudflare R2 for post/daily/moment
Admin deletes now collect local /api/assets references from the deleted record, remove the database row, then scan remaining posts, daily entries, moments, and books before deleting only unreferenced R2 objects. The cleanup result is returned as metadata so content deletion stays successful even if asset cleanup is skipped or partially fails. Constraint: R2 objects can be reused across content, so deletion must be reference-aware.\nRejected: Delete every image URL found in the removed content | risks deleting assets still used elsewhere.\nConfidence: high\nScope-risk: moderate\nDirective: Keep asset cleanup limited to /api/assets URLs; never delete external image URLs.\nTested: npx eslint src/lib/r2-assets.ts src/app/api/admin/delete/route.ts src/app/api/admin/home-items/route.ts\nTested: npx tsc --noEmit\nTested: npm run build\nNot-tested: Live R2 deletion against production bucket
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
这个 PR 补充了一组更适合 Cloudflare Pages + D1 + R2 部署的通用能力。
主要包括:首页 Dither 背景动画修复、真实访问统计、首页工具/书架后台管理、文章 Markdown 预览增强、R2 图片上传,以及删除内容时自动清理未再被引用的 R2 图片。
主要改动
Cloudflare 配置
需要配置以下绑定或环境变量:
R2 删除策略
R2 清理采用“引用感知”策略: