Commit graph

116 commits

Author SHA1 Message Date
le king fu
cfedde0fa6 test(smoke): add non-regression smoke for uuid + package overrides
Plain-node script invoked via `npm test`. Catches the buffer-bounds
regression area from GHSA-w5hq-g745-h8pq (uuid v3/v5 with buffer arg)
and validates package.json structure. No jest/Expo runtime needed —
runs in seconds, suitable for the defenseur-auto chain to gate auto-PRs.
2026-05-02 11:49:13 -04:00
le king fu
54ed29c50f docs(eas-build): document APK upload retry pattern for transient 502/504
Captures lesson from v1.6.2 release: when curl reports HTTP 502/504 ~60s
into the upload of a 90+ MB APK, the cause is client-side bandwidth dropping
under ~1.5 MB/s, not Traefik or Forgejo config. Retry — bandwidth recovers.

Adds: retry loop, diagnostics checklist, correct DELETE asset path
(/releases/{release_id}/assets/{asset_id}, not /releases/assets/{asset_id}).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 07:17:51 -04:00
le king fu
22f96bc9a9 chore: bump version to 1.6.2 (versionCode 14)
Security release: 5 CVE resolved in build-time dep chain via overrides
(@xmldom/xmldom, uuid, postcss). 4 HIGH + 1 MEDIUM cleared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 19:48:49 -04:00
2245484407 Merge pull request 'fix(security): override postcss to ^8.5.10' (#80) from fix/vuln-postcss-override into master 2026-04-28 01:26:11 +00:00
le king fu
08cba37775 fix(security): override postcss to ^8.5.10
Resolves GHSA-qx2v-qp2m-jg93 (PostCSS XSS via Unescaped </style> in CSS
Stringify Output) in the @expo/metro-config + tailwindcss build chain.
Build-time only, not runtime-exploitable in RN, but cleared for audit hygiene.

Defenseur scan post-override: 13/13 passed, 0 findings (the residual uuid
cascade is suppressed via defenseurs allowlist for GHSA-w5hq-g745-h8pq).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 15:37:19 -04:00
cc1e187c85 Merge pull request 'verify(security): seed STATE+SECURITY + defenseur rescan (#76)' (#79) from fix/vuln-C-verification into master 2026-04-24 18:14:34 +00:00
ced900b191 Merge pull request 'fix(security): override uuid to ^11.0.0 (#75)' (#78) from fix/vuln-B-uuid-override into master 2026-04-24 18:14:25 +00:00
dfe5214b57 Merge pull request 'fix(security): override @xmldom/xmldom to ^0.8.13 (#74)' (#77) from fix/vuln-A-xmldom-override into master 2026-04-24 18:13:56 +00:00
le king fu
f52e1e9e06 docs(security): seed STATE.md and SECURITY.md after vuln remediation
STATE.md follows the 3-section monorepo pattern (Position actuelle, Decisions
recentes, Blockers actifs). SECURITY.md tracks resolved CVE (4 HIGH xmldom)
and residuals (GHSA-w5hq-g745-h8pq uuid, non-exploitable in practice).

Refs #76

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 07:30:41 -04:00
le king fu
ed4c10f29c fix(security): override uuid to ^11.0.0
Resolves GHSA-w5hq-g745-h8pq in the transitive chain (xcode + @expo/ngrok).
Per spec decision D3, we pin ^11.0.0 (not ^14.0.0) to avoid ESM-only breaking
CJS consumers. Actual vulnerable code paths (v3/v5/v6 with buf param) are not
used by xcode or @expo/ngrok — they only call uuid.v4() — so the override is
safe in practice even though npm advisory range is <14.0.0.

Refs #75

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 07:16:35 -04:00
le king fu
5842a686b2 fix(security): override @xmldom/xmldom to ^0.8.13
Resolves 4 HIGH CVE in the xmldom transitive dep chain (Expo CLI + xcode/plist).
Not runtime-exploitable in APK (build-time deps only) but cleaned for audit hygiene.

- GHSA-2v35-w6hq-6mfw (DoS — uncontrolled recursion in XML serialization)
- GHSA-f6ww-3ggp-fr8h (XML injection via DOCTYPE serialization)
- GHSA-x6wf-f3px-wcqx (XML injection via processing instruction serialization)
- GHSA-j759-j44w-7fr8 (XML injection via comment serialization)

Refs #74

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 07:13:40 -04:00
8a0cc97018 Merge PR #73 — fix: widget render-optimiste (#71) 2026-04-19 20:24:18 +00:00
le king fu
af43a3f1a8 fix: render-optimiste + timing instrumentation for widget toggles (#71)
Widget tap-to-expand felt slow (several seconds). Inverts the order in all
three click handlers so the widget re-renders BEFORE awaiting the
AsyncStorage write — the user sees the change immediately, persistence
finishes in the background.

- TOGGLE_COMPLETE / TOGGLE_EXPAND / TOGGLE_SUBTASK : render before persist
- EXPAND_DEBOUNCE_MS 2000 -> 600 (still blocks accidental double-taps,
  no longer feels laggy when collapsing right after expanding)
- persistState() wraps setWidgetState in try/catch — on failure the next
  handler call re-reads the prior state from AsyncStorage, UI self-heals
- Dev-only timed() helper logs each step to logcat for measurement:
  adb logcat -s ReactNativeJS | grep '\[widget\]'

Out of scope: cold start of the Android headless task service (suspected
main contributor to perceived slowness, unfixable from JS).
2026-04-19 16:17:41 -04:00
le king fu
9cf507429a docs: archive spec-simpl-liste-web (milestone 12/12 done)
Design document for the Simpl-Liste Web frontend, Logto integration,
and hybrid mobile/web sync. Milestone spec-simpl-liste-web is fully
delivered — preserving the spec as historical reference.
2026-04-19 15:57:00 -04:00
le king fu
7e7a518b74 chore: sync package-lock.json to version 1.6.1
Lockfile version field was left at 1.5.1 after the 1.6.1 bump in 9a53022.
2026-04-19 15:56:49 -04:00
le king fu
9a53022421 chore: bump version to 1.6.1 (versionCode 13)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:44:19 -04:00
fe43b65cfd Merge pull request 'fix: replace broken swipe-to-refresh with toolbar button (#61)' (#67) from issue-61-refresh-button-toolbar into master 2026-04-09 13:44:08 +00:00
le king fu
5b0d27175c fix: replace broken swipe-to-refresh with toolbar refresh button (#61)
The RefreshControl on DraggableFlatList never worked because the
library wraps its FlatList in a GestureDetector with Gesture.Pan(),
which intercepts vertical swipes before RefreshControl can detect
them — particularly with activationDistance=0 in position sort mode.

Replace with a toolbar refresh button (RefreshCw icon) on inbox and
list detail screens. The button uses an Animated spin during refresh,
matching the web UX. Removes all dead RefreshControl code and the
useless refreshControl prop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:37:26 -04:00
1a1eddfd68 Merge pull request 'fix: refresh Android widget after sync push and pull (#65)' (#66) from issue-65-widget-sync-refresh into master 2026-04-09 13:37:06 +00:00
le king fu
23f3144dc4 fix: refresh Android widget after sync push and pull (#65)
The sync client writes directly to the DB via drizzle, bypassing the
repository functions that normally trigger syncWidgetData(). As a
result, changes coming from the web (or any remote source) never
refreshed the home screen widget.

Call syncWidgetData() once at the end of pullChanges (after all remote
changes are applied) and after a successful pushChanges (to reflect
synced state). Single call per cycle avoids spamming widget updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 09:01:29 -04:00
le king fu
1df41cef1f fix: auto-apply migrations on startup + cleanup duplicate inboxes (#60)
- Add migration 0002 to soft-delete duplicate inboxes per user, keeping
  the oldest one and reassigning tasks to it.
- Run drizzle migrations on server startup via drizzle-orm/node-postgres
  migrator.
- Update Dockerfile to copy the migrations folder into the runtime image
  and externalize pg/drizzle-orm from the esbuild bundle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:53:09 -04:00
le king fu
2a0dd01307 chore: bump version to 1.6.0 (versionCode 12)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:37:50 -04:00
137dc83bf8 Merge pull request 'fix: issues #60 #61 #62 #63 — inbox, refresh, subtask depth, chevron/detail' (#64) from issue-60-fix-duplicate-inbox into master 2026-04-09 01:33:40 +00:00
le king fu
78471543c3 fix: separate subtask expand chevron from detail view icon (#63)
Split TaskItem into two independent states:
- `expanded` (chevron): toggles subtask visibility, only shown when
  subtasks exist
- `detailOpen` (search icon + title click): opens detail panel with
  notes, priority, edit/delete actions

The two actions are fully independent — expanding subtasks does not
open the detail view and vice versa.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:15:59 -04:00
le king fu
21020406b2 fix: prevent sub-subtask creation, limit nesting to 2 levels (#62)
Web: hide "Add subtask" button when depth >= 1 in TaskItem.
API: reject task creation if parentId points to a task that already
has a parentId (max depth validation).
Mobile: hide subtask section in task detail when viewing a subtask.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:13:43 -04:00
le king fu
894ac03072 feat: add refresh button on web + swipe-to-refresh on mobile (#61)
Web: add a RefreshCw button next to the list title in TaskList that
calls router.refresh() with a spin animation.

Mobile: add RefreshControl to DraggableFlatList on both inbox and
list detail screens, using the app's blue accent color.

Also deduplicate list insert values in sync/route.ts (review feedback).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:04:55 -04:00
le king fu
6c36ebcce5 fix: wrap inbox merge in transaction, revert seed to random UUID (#60)
Address review feedback:
1. Wrap inbox deduplication (select, reassign tasks, soft-delete) in a
   db.transaction() for atomicity.
2. Revert seed.ts to use random UUID — a fixed ID shared across users
   would cause PK conflicts. The sync endpoint handles deduplication.
3. Subtasks share the same listId as their parent, so the reassign
   query already covers them (clarified in comment).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:54:55 -04:00
le king fu
d9daf9eda4 fix: resolve duplicate inbox on web after mobile sync (#60)
When mobile syncs its inbox (fixed ID) to the web, check if an inbox
already exists for the user. If so, reassign tasks and soft-delete the
old inbox to prevent duplicates. Also harmonize seed.ts to use the same
fixed inbox ID as mobile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:50:12 -04:00
le king fu
71ee702739 chore: bump version to 1.5.2 (versionCode 11)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:42:22 -04:00
0893cea489 Merge pull request 'fix: resolve sync data inconsistency between mobile and web (#55)' (#58) from fix/simpl-liste-55-sync-data-inconsistency into master 2026-04-08 19:32:15 +00:00
le king fu
8f7204e4b1 fix: resolve sync data inconsistency between mobile and web (#55)
Three root causes fixed:

1. API GET /api/sync returned raw entities but mobile client expected
   {changes: [...], sync_token} format — pullChanges() iterated
   data.changes which was undefined, silently skipping all server data.
   Now transforms entities into the SyncPullChange format.

2. Mobile outbox writes used snake_case keys (due_date, list_id, etc.)
   but server processOperation spreads data directly into Drizzle which
   expects camelCase (dueDate, listId). Fixed all outbox writes to use
   camelCase. Also fixed task_tag → taskTag entity type.

3. Missing completedAt in task outbox payloads — completion state was
   lost during sync. Added completedAt to both create and update outbox
   entries, and added Date conversion in server update handler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:28:36 -04:00
818f66205b Merge pull request 'fix: update deps for high vulnerabilities (#54)' (#57) from fix/simpl-liste-54-vulnerability-updates into master 2026-04-08 19:23:37 +00:00
le king fu
5b16882a83 fix: update drizzle-orm and @xmldom/xmldom to fix high vulnerabilities (#54)
- drizzle-orm 0.45.1 → 0.45.2 (SQL injection via improperly escaped identifiers)
- @xmldom/xmldom 0.8.11 → 0.8.12 (XML injection via unsafe CDATA serialization)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:22:42 -04:00
45d463c849 Merge pull request 'fix: rename .env.example to .env.template (#53)' (#56) from fix/simpl-liste-53-env-example-false-positive into master 2026-04-08 19:21:40 +00:00
le king fu
a5e9aa6f09 fix: rename .env.example to .env.template to avoid false positive (#53)
The defenseur flagged web/.env.example as a tracked secret file.
Renaming to .env.template avoids the .env* pattern match while
keeping the same purpose as a configuration template.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:20:55 -04:00
le king fu
89b3dada4a fix: replace Link with anchor tags for Logto auth routes
Next.js <Link> components prefetch routes on render/hover. When used
for /api/logto/sign-out, this triggered the sign-out handler during
normal navigation, clearing the session cookie and causing auth loops.

Also: wrap getAuthenticatedUser with React cache() for deduplication,
clean up diagnostic logging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:31:04 -04:00
le king fu
9933c3678e fix: pass full URL to handleSignIn for callback URI matching
The Logto SDK needs the full callback URL (not just searchParams) to
verify it matches the redirect URI registered during sign-in.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:28:08 -04:00
le king fu
f786947941 fix: resolve Logto auth crash on web — remove illegal cookie set in layout
The (app)/layout.tsx was calling cookieStore.set() which is forbidden in
Server Components under Next.js 16 (only allowed in Server Actions and
Route Handlers). This caused a 500 error immediately after Logto login.

Also includes: mobile sync client improvements, i18n updates, web API
rate limiting, Bearer token support for mobile clients, and Dockerfile
optimizations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:12:59 -04:00
14c208be46 Merge pull request 'fix: force widget refresh after subtask toggle (#32)' (#33) from fix/simpl-liste-32-widget-subtask-toggle into master 2026-04-08 16:44:46 +00:00
le king fu
6328a8d0d3 fix: correct Logto session cookie prefix in middleware
Cookie is named `logto_<appId>` not `logto:<appId>`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:56:21 -04:00
8462aa9ef4 Merge pull request 'feat: add mobile sync client with outbox pattern (#40)' (#47) from issue-40-sync-mobile into master 2026-04-06 16:59:00 +00:00
b7a090df71 Merge pull request 'feat: implement web frontend with full task management UI (#39)' (#46) from issue-39-frontend-web into master 2026-04-06 16:58:35 +00:00
f4df9bbfd0 Merge pull request 'feat: setup Next.js web project with Drizzle + PostgreSQL schema (#35)' (#42) from issue-35-web-setup into master 2026-04-06 16:58:04 +00:00
d486be9227 Merge pull request 'feat: extract shared types, colors, priority and recurrence (#34)' (#41) from issue-34-shared-types into master 2026-04-06 16:57:29 +00:00
le king fu
c496d9586c feat: add mobile sync client with outbox pattern (#40)
- sync_outbox table in SQLite (migration 0004)
- Sync service: push/pull changes, fullSync, outbox cleanup
- Outbox writing in task/list/tag repositories after mutations
- Settings store: syncEnabled, lastSyncAt, userId
- Sync polling: on launch, every 2 min, on return from background
- Settings UI: Compte section with connect/sync/disconnect buttons
- i18n keys for sync strings (FR + EN)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 12:54:44 -04:00
le king fu
cb04adcc2e feat: implement web frontend with full task management UI (#39)
- Protected (app) layout with sidebar, header, theme toggle
- List detail page with tasks, filters, sorting
- Inline task editing (title, notes, priority, due date, recurrence)
- Subtask creation and nested display
- Dark mode (class-based, persisted to localStorage)
- WebSocket sync hook (connects via ticket auth, auto-refresh)
- Responsive sidebar (hamburger on mobile)
- French UI strings throughout
- Components: Sidebar, TaskList, TaskItem, TaskForm, FilterBar,
  ThemeToggle, Header, AppShell

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 12:40:11 -04:00
2f2a48f644 Merge pull request 'feat: add WebSocket server with ticket auth and heartbeat (#38)' (#45) from issue-38-websocket into issue-35-web-setup 2026-04-06 16:07:00 +00:00
le king fu
6d2e7449f3 feat: add WebSocket server with ticket auth and heartbeat (#38)
- Custom server (server.ts) wrapping Next.js + ws on same port
- Ticket-based auth: validates ephemeral nonce from /api/ws-ticket
- Origin validation against allowlist
- Session revalidation every 15 min (sends auth_expired, closes)
- Heartbeat every 30s (ping/pong, terminates dead connections)
- broadcastToUser() for API routes to notify connected clients
- Shared ticket store between API route and WS server via globalThis
- Health endpoint now reports active WS connections
- Dockerfile updated to use custom server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:55:28 -04:00
46ead345b4 Merge pull request 'feat: implement REST API backend with full CRUD and sync (#37)' (#44) from issue-37-api-rest into issue-35-web-setup 2026-04-06 15:52:30 +00:00
le king fu
be9ba65337 feat: implement REST API backend with full CRUD and sync (#37)
- Lists, Tasks, Tags CRUD endpoints with soft-delete
- Sync endpoints (GET since + POST batch with idempotency keys)
- WS ticket endpoint (ephemeral nonce, 30s TTL, single use)
- Auth middleware on all endpoints via getAuthenticatedUser()
- BOLA prevention: userId check on every entity operation
- Zod strict schemas for input validation
- Filters and sorting on task listing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:47:53 -04:00