luis a9fc77f32f
Build and Deploy / build-deploy (push) Successful in 49s
Do things in the demo periodically
2026-07-14 15:20:56 +01:00
2026-07-02 13:52:20 +01:00
2026-06-20 18:37:17 +01:00
2026-07-14 15:20:56 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-07-14 15:20:56 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 23:38:22 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00
2026-06-20 18:37:17 +01:00

fizalgo

A household chore tracker with a bit of gamification. People log task executions (who did what, where, with how much effort); tasks belong to divisions (rooms) and have a frequency; completed work earns XP. The /next page ranks tasks by urgency (a Postgres view), /stats charts cumulative XP and shows history.

Stack

  • Next.js 16 (App Router) | React 19 | TypeScript
  • Drizzle ORM + PostgreSQL
  • HeroUI v3 components | Tailwind CSS 4
  • i18next (server-first, runtime locale switch between PT and EN)
  • Bun (package manager + script runner) | Biome (lint/format)

Architecture notes

  • Two databases, one instance. A single Postgres server holds fizalgo (real, authenticated data) and fizalgo_demo (a throwaway guest sandbox). Every request picks one via getRequestDb() (src/db/request.ts): a valid session -> real, otherwise -> demo.
  • Auth is per-user, session-based (Lucia-guide pattern, no library): a random token in an httpOnly cookie, only its SHA-256 hash stored in Session; passwords hashed with node:crypto scrypt. There is no open signup: users are provisioned with db:create-user, each linked to a Person.
  • Guest demo vs. real. / is a public landing with "Try the demo" and "Login". Guests browse the whole app against the demo DB (/do-things, the random-data generator, is guest-only). Logging in switches every route to the real DB.
  • Rendering / revalidation. All data routes read the session cookie, so they're dynamic; with Next's default staleTimes.dynamic = 0, navigation always re-renders fresh, so cross-route revalidatePath is unnecessary; only in-place updates revalidate.

Local development

Postgres runs in Docker; the app runs on the host.

cp .env.example .env                          # set POSTGRES_PASSWORD (any value)
docker compose -f docker-compose.dev.yml up -d
bun install
bun db:migrate                                # creates both DBs + the TaskPeriod view
bun db:seed                                   # seed the real DB
bun db:reset-demo                             # seed the demo sandbox
bun db:create-user --username you --password secret --person "Your Name"
bun dev                                        # -> http://localhost:3000

Then: / -> Try the demo (demo DB, no login) or Login with the user you created (real DB). The language toggle (PT/EN) is in the navbar menu.

Scripts

Script What it does
bun dev Dev server (HMR)
bun run build / bun start Production build / serve
bun run lint Biome check
bun db:generate Generate a Drizzle migration from src/db/schema.ts
bun db:migrate Apply migrations to both DBs (creates them if missing)
bun db:seed Seed the real DB from seed/seed.json (falls back to seed/seed.example.json)
bun db:reset-demo Wipe + reseed the demo DB
bun db:create-user --username <u> --password <p> --person <name> Provision a real-DB user

Environment

All connection URLs derive from these parts (.env, see .env.example):

Var Notes
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB Credentials + base DB name
POSTGRES_HOST / POSTGRES_PORT localhost:5432 for local dev
DATA_DIR Where Postgres stores data (compose bind-mount; deploy uses /opt/fizalgo/data)
APP_PORT Host port the app listens on

Real env files are never committed; seed/seed.json (real household data) is gitignored.

Internationalisation

Server-first: getDict() (src/i18n/server.ts) resolves the cookie's locale bundle and server components pass slices to client components as props. Client-side t() is available via I18nProvider but used sparingly (e.g. the language switcher). Translations live in src/i18n/{pt,en}.json; types are inferred from them (src/i18n/i18next.d.ts).

Deployment

Deploys to a Gitea-Actions server on push to master (.gitea/workflows/deploy.yaml), running the full docker-compose.yml stack (app + Postgres). The app is a Next standalone build; migrations run at container start against both DBs.

Host prep (once):

  • Create /opt/fizalgo/ and /opt/fizalgo/data/.
  • Gitea repo -> Actions -> Variables: POSTGRES_USER, POSTGRES_DB, APP_PORT; Secrets: POSTGRES_PASSWORD.
  • Point a Caddy vhost at the app's APP_PORT.
  • Confirm the default branch is master.

After the first deploy, seed the real DB once (docker compose exec app bun run db:seed) and create a user (... db:create-user ...). The demo DB can be reset on a schedule with db:reset-demo.

S
Description
No description provided
Readme AGPL-3.0 1.3 MiB
Languages
TypeScript 93.4%
CSS 5%
Dockerfile 1.2%
Shell 0.2%
JavaScript 0.2%