A Progressive Web App (PWA) to track a personal collection of Haribo candy packets.
(Note: The live demo is read-only. Write access is restricted via session cookies).
This project solves a specific user problem: preventing redundant purchases while browsing candy in-store by providing mobile access to a personal catalog.
Instead of relying on generic tracking apps, I built a custom PWA that feels like a native iOS app and requires zero ongoing hosting costs.
| Layer | Choice | Rationale |
|---|---|---|
| Framework | Astro (SSR) | Handles the static shell and API routes at the edge. |
| Interactivity | Vue.js 3 | Used for specific UI elements (image croppers, filtering, rating) while keeping the rest of the page lightweight. |
| Database | Turso (LibSQL) | Edge-hosted SQLite. Provides low-latency queries and strict relational typing via Drizzle ORM. |
| Storage | Cloudflare R2 | S3-compatible object storage for hosting WebP images via presigned URLs. |
| Hosting | Cloudflare Pages | Free edge hosting. Runs Astro API routes as Cloudflare Workers. |
| Search | Fuse.js | Client-side fuzzy search to handle typos without requiring a server round-trip. |
- PWA Integration: Configured with an Apple Touch Icon, dynamic safe-area insets (
viewport-fit=cover), and amanifest.json. Installs to the iOS Home Screen without Safari UI components. - Image Capture: Uses
<input capture="environment">to open the mobile camera. Integratescropperjsto frame the packet, then converts the canvas to a compressed WebP blob for R2 upload. - Filtering & Search: A dashboard that filters data based on categorical AND-logic, fuzzy text search, and advanced constraints (rating, date, location).
- API & Auth: A custom Astro middleware intercepts requests.
GETrequests are public, butPOST/PUT/DELETEmethods and UI routes require a verified session cookie. Payloads are validated viaZodbefore hitting the database.
To run this project locally, you need Node.js 20+ and a Turso database instance.
git clone https://github.com/neriAle/haribo-tracker.git
cd haribo-tracker
npm installCreate a .env file in the root directory and populate it with your Turso and Cloudflare R2 credentials:
# Database (Turso)
TURSO_DATABASE_URL="libsql://your_db_url.turso.io"
TURSO_AUTH_TOKEN="your_token"
# Storage (Cloudflare R2)
R2_ENDPOINT="https://your_account_id.r2.cloudflarestorage.com"
R2_ACCESS_KEY_ID="your_access_key"
R2_SECRET_ACCESS_KEY="your_secret_key"
R2_BUCKET_NAME="your_bucket_name"
R2_PUBLIC_URL="https://pub-your_bucket_id.r2.dev"
# Auth
ADMIN_PASSWORD="your_secure_password"
SESSION_SECRET="your_secure_cookie"Push the Drizzle schema to your SQLite database:
npm run db:pushThen, seed the categories to the categories table, if you want to put your own categories update the array in src/db/seed.ts, then run:
npm run db:seednpm run devNavigate to http://localhost:4321 to view the app.
This project uses Vitest for API endpoints and database mocking to ensure branch coverage on destructive actions (like orphaned image cleanup during a DELETE operation).
npm run testTo visualize code coverage after testing, use this command instead:
npm run coverage