Commit Graph
22 Commits
Author SHA1 Message Date
tiennm99 a823f8527d fix: address ultrareview findings across backend and frontend
Backend:
- rate-limiter: retryAfter now in seconds; ms-precision lu preserves
  fractional regen residue across calls (C1, C2)
- redis-client: throw on Upstash 200-with-error envelope; redisRaw
  returns body.result (NH1)
- constants: MAX_BATCH_SIZE = MAX_CREDITS = 256 (was 512 vs 256)
- worker: content-length cap, gzip + s-maxage=10 on /api/canvas,
  broadcast via executionCtx.waitUntil with r.ok check (NC2, H4, H5)
- canvas-storage: warn on truncated Upstash read instead of silent
  zero-pad (NH2)
- get-user-id: SHA-256 (16 hex chars) replaces 32-bit string hash;
  missing cf-connecting-ip routes to anon:dev with warn (H1, H2);
  function is now async
- canvas-room: log unclean WS closes and errors; defensive close on
  unexpected client message (NH4, N5)

Frontend:
- pixel buffer capped at MAX_BATCH_SIZE with toast (NC2)
- Submit error UX: toast for 429/413/400/5xx/network; honor
  retryAfter (NC1)
- committedColors allocated upfront so WS updates during initial
  fetch no longer null-deref (NC3)
- handleWheel always renders even when zoom is clamped (C1, C2)
- canvas-decoder throws on truncated input instead of reading past
  end with || 0 (C3)
- WS reconnect refetches canvas to recover missed pixels (C4)
- pixel-buffer Map cache for O(1) getColorAt/pixelCount (NH1)
- cancel in-progress stroke on mode switch (NH3)
- canvas load error overlay with Retry button (NH5)
- DPR-aware canvas sizing (H1)
- onMount cleanup is now sync (no leaked resize listener) (H2)

Tests:
- update for async getUserId, decoder bounds check, redis error
  format; add Upstash error-envelope coverage
2026-04-17 10:14:45 +07:00
tiennm99 8e1f8c4049 test: add Redis integration tests with Testcontainers
Docker-based tests with real Redis verifying:
- BITFIELD write → GETRANGE read round-trip for all 32 colors
- Pixel placement at canvas boundaries and various positions
- Pixel overwrite correctness
- 5-bit boundary isolation (adjacent pixels don't corrupt)
- Batch atomicity (100 pixels in single BITFIELD)
- Rate limiter Lua script: credit grant, deduction, regen, cap, rejection

Test commands: npm test (unit), npm run test:integration (Docker),
npm run test:all (both). 82 total tests.
2026-04-16 22:52:36 +07:00
tiennm99 fcddb1f9c5 test: add unit tests for Durable Object and clean up test setup
- Add CanvasRoom broadcast/close/error tests with mock WebSockets
- Remove @cloudflare/vitest-pool-workers (incompatible with Vitest 4)
- Clean up vitest config (single config, no integration workspace)
- 71 tests across 7 test files, all passing
2026-04-16 22:45:16 +07:00
tiennm99 33cfd3d7b3 test: add comprehensive unit tests with Vitest
65 tests covering canvas decoder, pixel buffer, user ID hashing,
canvas storage (with binary encoding regression test), Redis client
API shape, and worker endpoint validation.
2026-04-16 22:37:12 +07:00
tiennm99 b35769cc73 fix: binary-safe canvas read via Upstash base64 encoding
Use path-based REST API with Upstash-Encoding: base64 header for
GETRANGE to prevent binary data corruption through JSON text encoding.
Verified all 32 colors round-trip correctly.
2026-04-16 22:19:26 +07:00
tiennm99 e3eb34c6de fix: BITFIELD via raw REST API and improve error handling
- Use Upstash REST API directly for BITFIELD (SDK builder broken in v1.37)
- Add redisRaw() helper for raw command execution
- Wrap setPixels in try-catch, return JSON errors instead of 500 text
- Client handles non-JSON server responses gracefully
- Toolbar: larger 44px touch targets, separators, better contrast
2026-04-16 22:00:58 +07:00
tiennm99 e0cf802ec2 feat: batch drawing with paint/draw modes, undo/redo, and submit
Replace per-pixel immediate placement with local buffer system:
- Paint mode (click) and Draw mode (drag) for pixel placement
- Undo/Redo strokes (Ctrl+Z/Y) before submitting
- Submit button sends batch to server for storage + broadcast
- Right-click drag to pan in draw mode
- Increase MAX_BATCH_SIZE to 512 for larger batches
2026-04-16 21:22:19 +07:00
tiennm99 eef6879ff2 fix: use Hibernation API for reliable WebSocket broadcast
Replace manual session tracking with Cloudflare's Hibernation API
so WebSocket connections survive Durable Object eviction.
2026-04-16 20:51:47 +07:00
tiennm99 50f4365034 feat: enable observability logs and traces 2026-04-16 20:47:14 +07:00
tiennm99 c357a3f265 fix: use new_sqlite_classes for free-plan Durable Objects and gitignore .wrangler 2026-04-16 20:46:52 +07:00
tiennm99 f97ca4d34d refactor: add Redis key prefix for namespace isolation
Introduce REDIS_KEY_PREFIX constant to namespace all Redis keys under
'rplace:', preventing collisions in shared Redis instances.
2026-04-16 20:39:12 +07:00
tiennm99 fc49de154a add project documentation and detailed README
- README: features, architecture diagram, setup guide, API reference,
  configuration table, project structure
- docs/system-architecture.md: data flow, storage design, rate limiting
- docs/code-standards.md: conventions, project layout, API format
- docs/deployment-guide.md: step-by-step CF Workers + Upstash deploy
2026-04-16 17:05:29 +07:00
tiennm99 078ccaa70e fix: final review polish before documentation
- Handle base64 encoding from Upstash GETRANGE (atob fallback)
- Add optimistic credit deduction on pixel placement
- Handle non-ok API responses in placePixel
- Enable WebSocket proxy in Vite dev config
- Remove dead CANVAS_WIDTH/HEIGHT vars from wrangler.json
- Suppress favicon 404 with empty data URI
2026-04-16 17:03:38 +07:00
tiennm99 75441d650a feat: add touch support, cooldown bar, and cap batch size
- Cap MAX_BATCH_SIZE to 32 (was 256) to limit burst grief damage
- Add touch handlers: pinch-zoom, drag-pan, long-press to place pixel
- Add credit bar visualization with fill animation and color states
  (yellow while regenerating, green when full)
2026-04-16 16:52:00 +07:00
tiennm99 7793163e0b fix: critical Upstash API and UX issues from second review
- Fix setPixels() to use Upstash bitfield builder (.set().exec())
  instead of flat array (would throw at runtime)
- Fix getFullCanvas() to use GETRANGE for reliable binary retrieval
  instead of GET which may mangle BITFIELD data
- Fix getUserId hash overflow: use >>> 0 instead of Math.abs()
- Add client-side credit regeneration timer (1/sec)
- Add exponential backoff to WebSocket reconnection (1s → 30s cap)
2026-04-16 16:37:07 +07:00
tiennm99 b3b4916263 fix: address code review findings
- Fix WebSocket reconnection logic in App.svelte (was a no-op)
- Remove spoofable x-forwarded-for fallback, use cf-connecting-ip only
- Wrap Durable Object broadcast in try/catch to prevent place failures
- Cache OffscreenCanvas to avoid re-allocation every render frame
2026-04-16 16:27:35 +07:00
tiennm99 2ccf1c9779 refactor: migrate from Next.js/Vercel to Svelte/Hono/Cloudflare Workers
Replace Next.js + SSE with Svelte frontend + Hono API + Durable Objects
WebSocket on Cloudflare Workers. Single worker serves static assets and
API routes. Native WebSocket replaces SSE polling for real-time updates.
17kb gzipped client bundle vs 90kb+ React runtime.
2026-04-16 16:23:27 +07:00
tiennm99 3beaa9d43f feat: implement core pixel canvas with real-time updates
Next.js App Router + Upstash Redis BITFIELD for 2048x2048 canvas.
- 32-color palette (5-bit encoding) from rplace.live
- Batch pixel placement API (up to 256 pixels per request)
- Stackable credit rate limiting via Redis Lua script
- SSE real-time broadcasting via Redis sorted set
- HTML5 Canvas frontend with zoom/pan and color picker
2026-04-16 15:56:20 +07:00
tiennm99 1c6b2edcff add implementation plan for rplace pixel canvas
7-phase plan: setup, canvas backend (Redis BITFIELD), rate limiting
(stackable credits), SSE real-time updates, frontend canvas, auth, deploy.
2026-04-16 15:24:55 +07:00
tiennm99 62188d26d2 update references and add credits to README
Remove unfetchable URLs (redditinc, reddit.com, tejash.dev, rplace.live).
Add credits section to README attributing all referenced projects.
2026-04-16 15:24:50 +07:00
tiennm99 9bdde946a0 add r/place reference links and resources
Curated collection of official post-mortems, technical walkthroughs,
open-source implementations, and live instances for r/place research.
2026-04-16 14:43:23 +07:00
tiennm99andGitHub 8ca80531cc Initial commit 2026-04-16 14:36:24 +07:00