feat: tasks app, security hardening, mobile fixes, iOS app shell
- Custom SQLite task manager replacing TickTick wrapper - 73 tasks migrated from TickTick across 15 projects - RRULE recurrence engine with lazy materialization - Dashboard tasks widget (desktop sidebar + mobile card) - Tasks page with project tabs, add/edit/complete/delete - Security: locked ports to localhost, removed old containers - Gitea Actions runner configured and all 3 CI jobs passing - Fixed mobile overflow on dashboard cards - iOS Capacitor app shell (Second Brain) - Frontend/backend guide docs for adding new services - TickTick Google Calendar sync re-authorized Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
229
codex.txt
Normal file
229
codex.txt
Normal file
@@ -0,0 +1,229 @@
|
||||
Platform Codex — Full Build & Remediation Log
|
||||
================================================
|
||||
Date: 2026-03-29
|
||||
|
||||
PLATFORM OVERVIEW
|
||||
=================
|
||||
A premium SaaS-style personal dashboard integrating self-hosted services:
|
||||
- Budget (Actual Budget)
|
||||
- Inventory (NocoDB)
|
||||
- Fitness (SparkyFitness)
|
||||
- Trips
|
||||
- Reader (Miniflux)
|
||||
- Media (Shelfmark/Spotizerr/Booklore)
|
||||
|
||||
Stack: SvelteKit (Svelte 5 runes) + Python gateway + Express.js services
|
||||
Orchestration: Docker Compose, 6 containers
|
||||
Reverse proxy: Pangolin at dash.quadjourney.com
|
||||
Gitea repo: gate.quadjourney.com/yusiboyz/platform
|
||||
|
||||
|
||||
WHAT WAS BUILT
|
||||
==============
|
||||
|
||||
1. Design System
|
||||
- Unified CSS token system: spacing (--sp-*), radius (--radius-*),
|
||||
shadows (--shadow-*), colors, typography (--text-*)
|
||||
- Migrated 235 raw values across 31 files
|
||||
- Documented in frontend-v2/DESIGN_SYSTEM.md
|
||||
|
||||
2. Gateway Modular Refactor
|
||||
- Split 1878-line server.py into 15 modular files:
|
||||
server.py (thin router), config.py, database.py, auth.py, proxy.py,
|
||||
dashboard.py, plus integrations/ directory (booklore, kindle, image_proxy,
|
||||
qbittorrent, etc.)
|
||||
- ThreadingHTTPServer for concurrent requests
|
||||
- 30-second per-user dashboard cache
|
||||
|
||||
3. Fitness API Integration
|
||||
- Replaced all mock data with real SparkyFitness backend API calls
|
||||
- AI food input with multi-item splitting (/api/fitness/foods/split)
|
||||
- Canonical food storage with dedup (naive singularize + pre-creation check)
|
||||
- Entry editing (quantity) and deletion
|
||||
- Confirmation modal for resolved items with quantity +/-
|
||||
- Local timezone date fix (was showing UTC = next day after 7pm CDT)
|
||||
- Per-user goals editing in Settings
|
||||
- Shared food database across users, independent fitness goals
|
||||
|
||||
4. Media App (Built from Scratch)
|
||||
- Books tab: search via Shelfmark/Anna's Archive, download + auto-import
|
||||
to Booklore, per-book library dropdown
|
||||
- Music tab: Spotify search via Spotizerr, track/album/artist/playlist
|
||||
search types, Spotify embed player, download progress polling
|
||||
- Library tab: Booklore library browser (237 books), cover resolution via
|
||||
ISBN -> Open Library with localStorage cache, book detail modal,
|
||||
format badges (EPUB/PDF), library filter pills
|
||||
- Send to Kindle: SMTP2GO API integration, per-book Kindle label selector
|
||||
("Madiha"/"Hafsa"), sends from bookdrop or booklore-books volumes
|
||||
- "After download, also send to" Kindle option on book search
|
||||
|
||||
5. Multi-User Support
|
||||
- Created Madiha's account
|
||||
- Per-user nav visibility (cosmetic only, documented as such)
|
||||
- hiddenByUser map in +layout.server.ts
|
||||
- Shared food database, independent fitness goals
|
||||
|
||||
6. Frontend Architecture
|
||||
- SvelteKit with Svelte 5 runes ($state, $props, $derived, $effect, $bindable)
|
||||
- SvelteKit hooks.server.ts for auth on Immich/Karakeep proxies
|
||||
- Settings page: real API data, fitness goals editing, disconnect confirmation
|
||||
dialog, theme toggle, sign out
|
||||
|
||||
|
||||
SECURITY REMEDIATION (Gitea Issues #1-#10)
|
||||
==========================================
|
||||
|
||||
All 10 issues completed. Re-audited and verified in code on 2026-03-29.
|
||||
|
||||
#2 Auth Boundary
|
||||
- /api/auth/register disabled (403)
|
||||
- Gateway admin seeded from ADMIN_USERNAME/ADMIN_PASSWORD env vars only
|
||||
- Trips USERNAME/PASSWORD have no default fallback
|
||||
- Fitness user seed requires env vars (no "changeme" default)
|
||||
- All passwords use bcrypt
|
||||
|
||||
#3 Trips Sharing Security
|
||||
- handle_share_api enforces password via X-Share-Password header + bcrypt
|
||||
- share_password stored as bcrypt hash
|
||||
- All plaintext password logging removed
|
||||
- Existing plaintext passwords invalidated by migration
|
||||
- Dead hash_password function removed
|
||||
|
||||
#4 Fitness Authorization
|
||||
- All user_id query params enforced to authenticated user's own ID
|
||||
- /api/users returns only current user
|
||||
- Wildcard CORS removed
|
||||
|
||||
#5 Gateway Trust Model
|
||||
- Inventory and budget require API keys (X-API-Key middleware)
|
||||
- Token validation uses protected endpoints per service type
|
||||
- /debug-nocodb removed from inventory
|
||||
- /test removed from inventory
|
||||
- NocoDB search filter sanitized (strips operator injection chars)
|
||||
- SERVICE_LEVEL_AUTH renamed to GATEWAY_KEY_SERVICES
|
||||
- Trust model documented in docs/trust-model.md
|
||||
- Per-user vs gateway-key services clearly distinguished
|
||||
- Known limitations documented (no per-user isolation on shared services)
|
||||
|
||||
#6 Repository Hygiene
|
||||
- No .env or .db files tracked in git
|
||||
- .gitignore covers: .env*, *.db*, services/**/.env, data/, test-results/
|
||||
- .env.example updated with all current env vars (no secrets)
|
||||
|
||||
#7 Transport Security
|
||||
- Gateway: _internal_ssl_ctx removed entirely (internal services use plain HTTP)
|
||||
- Gateway: ssl import removed from config.py
|
||||
- Gateway: proxy.py uses urlopen() without context parameter
|
||||
- Gateway: logout cookie includes HttpOnly, Secure, SameSite=Lax
|
||||
- Gateway: image proxy uses default TLS + domain allowlist + content-type validation
|
||||
- Trips: all 5 CERT_NONE sites removed (OpenAI, Gemini, Google Places, Geocode)
|
||||
- Inventory: permissive cors() removed AND dead cors import removed
|
||||
- Budget: permissive cors() removed AND dead cors import removed
|
||||
|
||||
#8 Dependency Security
|
||||
- Budget path-to-regexp vulnerability fixed
|
||||
- .gitea/workflows/security.yml committed with 3 jobs + workflow_dispatch trigger
|
||||
- Gitea Actions enabled ([actions] ENABLED = true in app.ini)
|
||||
- Runner (gitea/act_runner) added to Gitea docker-compose
|
||||
- Runner registered as platform-runner on gitea_gitea network
|
||||
- Config sets container.network = gitea_gitea so job containers can git clone
|
||||
- Runner token stored in /media/yusiboyz/Media/Scripts/gitea/.env
|
||||
- All 3 jobs verified passing:
|
||||
- dependency-audit: SUCCESS (npm audit on budget + frontend)
|
||||
- secret-scanning: SUCCESS (no tracked .env/.db, no hardcoded secrets)
|
||||
- dockerfile-lint: SUCCESS (all Dockerfiles have USER + HEALTHCHECK)
|
||||
- Runner setup documented in .gitea/README.md
|
||||
|
||||
#9 Performance Hardening
|
||||
- Inventory /issues: server-side NocoDB WHERE filter (no full scan)
|
||||
- Inventory /needs-review-count: server-side filter + pageInfo.totalRows
|
||||
- Budget /summary: 1-minute cache
|
||||
- Budget /transactions/recent: 30-second cache
|
||||
- Budget /uncategorized-count: 2-minute cache
|
||||
- Budget buildLookups: 2-minute cache
|
||||
- Gateway /api/dashboard: 30-second per-user cache
|
||||
- Actual Budget per-account API constraint documented
|
||||
|
||||
#10 Deployment Hardening
|
||||
- All 6 containers run as non-root (appuser/node)
|
||||
- Health checks on gateway, trips, fitness, inventory, budget, frontend
|
||||
- PYTHONUNBUFFERED=1 on all Python services
|
||||
- Trips Dockerfile only copies server.py (not whole context)
|
||||
- Frontend uses multi-stage build
|
||||
|
||||
|
||||
RE-AUDIT FINDINGS (2026-03-29)
|
||||
==============================
|
||||
1 inaccuracy found in prior report: CORS dead imports (const cors = require('cors'))
|
||||
remained in inventory/server.js and budget/server.js after app.use(cors()) was removed.
|
||||
Fixed by removing the dead imports.
|
||||
|
||||
All other claims verified accurate in code:
|
||||
- Trips TLS: zero CERT_NONE or check_hostname = False
|
||||
- Settings disconnect: confirm() dialog present
|
||||
- /test cleanup: no references remain
|
||||
- Cosmetic nav: documented as cosmetic-only, no false authz claims
|
||||
|
||||
|
||||
GITEA ACTIONS RUNNER SETUP (2026-03-29)
|
||||
=======================================
|
||||
Problem: Workflow existed in repo but no runner was configured to execute it.
|
||||
|
||||
What was done:
|
||||
1. Added [actions] ENABLED = true to Gitea app.ini
|
||||
File: /media/yusiboyz/Media/Scripts/gitea/gitea/gitea/conf/app.ini
|
||||
2. Restarted Gitea to pick up config change
|
||||
3. Generated runner token: docker exec -u git gitea gitea actions generate-runner-token
|
||||
4. Added runner service to Gitea docker-compose:
|
||||
File: /media/yusiboyz/Media/Scripts/gitea/docker-compose.yml
|
||||
Image: gitea/act_runner:latest
|
||||
Container: gitea-runner
|
||||
5. Saved token in /media/yusiboyz/Media/Scripts/gitea/.env as RUNNER_TOKEN
|
||||
6. First attempt: job containers created on auto-generated network, could not
|
||||
reach server:3000 for git clone (hung on git fetch)
|
||||
7. Fix: created /data/config.yaml inside runner with container.network = gitea_gitea
|
||||
and set CONFIG_FILE=/data/config.yaml env var
|
||||
8. Recreated runner container (docker compose up -d runner) to pick up env change
|
||||
9. Triggered workflow via API: POST /api/v1/repos/yusiboyz/platform/actions/workflows/security.yml/dispatches
|
||||
10. All 3 jobs ran to completion: dependency-audit, secret-scanning, dockerfile-lint = SUCCESS
|
||||
11. Added workflow_dispatch trigger to security.yml for manual runs
|
||||
12. Updated .gitea/README.md with setup documentation
|
||||
|
||||
Key detail: job containers must be on gitea_gitea network to resolve "server:3000"
|
||||
for git operations. Without this, git fetch hangs indefinitely.
|
||||
|
||||
|
||||
BUGS FIXED DURING BUILD
|
||||
========================
|
||||
- SERVICE_MAP import bug: captured empty dict at import time, fixed with module reference
|
||||
- Gateway Dockerfile missing modules: only copied server.py, fixed to copy all .py + integrations/
|
||||
- Non-root container permission denied: fixed with COPY --chown=appuser
|
||||
- Fitness date timezone: toISOString() returns UTC, fixed with local date construction
|
||||
- Dashboard fitness widget not updating: plain let vs $state() in Svelte 5
|
||||
- Food library empty: /api/fitness/foods/recent returns entry-shaped data, fixed mapFood
|
||||
- Book covers from search: double-wrapped image proxy URLs, fixed to proxy directly
|
||||
- Booklore cover API returns HTML: switched to Open Library + Google Books fallback
|
||||
- Booklore books API too slow (14s): moved to lazy client-side cover resolution
|
||||
- Fitness entries orphaned after DB reset: reassigned to new user IDs
|
||||
- Madiha accidentally disconnected fitness: added confirm() dialog
|
||||
- Double Kindle sends: actually processed + delivered SMTP2GO events, added debounce
|
||||
- Kindle email typo: lowercase L vs uppercase I in address
|
||||
|
||||
|
||||
MANUAL OPS ACTIONS
|
||||
==================
|
||||
1. Store admin password securely (set via ADMIN_PASSWORD env var)
|
||||
2. Clean up local untracked .env files with real credentials if needed
|
||||
3. Monitor @sveltejs/kit for a non-breaking cookie fix in future releases
|
||||
|
||||
|
||||
ARCHITECTURE REFERENCE
|
||||
======================
|
||||
- Trust model: docs/trust-model.md
|
||||
- CI workflows: .gitea/workflows/security.yml
|
||||
- Runner setup: .gitea/README.md
|
||||
- Design system: frontend-v2/DESIGN_SYSTEM.md
|
||||
- Env var reference: .env.example
|
||||
- Gitea instance: localhost:3300 (gate.quadjourney.com)
|
||||
- Gitea compose: /media/yusiboyz/Media/Scripts/gitea/docker-compose.yml
|
||||
- Platform compose: /media/yusiboyz/Media/Scripts/platform/docker-compose.yml
|
||||
Reference in New Issue
Block a user