# trips A SvelteKit travel planning app for managing trips, travelers, transportation, lodgings, package tours, checklists, and experiences. ## Tech Stack - SvelteKit + TypeScript - SQLite (`bun:sqlite`) - Vitest for unit tests - Biome + Prettier for code quality - Bun for local/deploy build and runtime workflows - Docker multi-stage build for production image ## Features - Authenticated trip dashboard with upcoming/past trip views - Trip planning with: - flights - private vehicles - other transportation - lodgings - package tours - checklists - experiences - Admin flows for travel reference data and tour operator/tour management ## Local Development ### Requirements - Bun 1.x ### Setup ```sh cp .env.example .env bun install ``` ### Run ```sh bun run dev ``` ## Local authentication Local auth is optional and off by default. Enable it with the env vars in `.env.example` and ensure users have a matching row in both `users` and `local_credentials`. To seed a local password hash, use Argon2id with the configured parameters and insert it into `local_credentials`: ```sql INSERT INTO local_credentials (user_id, password_hash) VALUES ('', ''); ``` Passwords must be at least 12 characters. Avoid storing plaintext passwords anywhere. ## Quality and Tests ```sh bun run lint bun run test bun run check ``` ## Build and Preview ```sh bun run build bun run preview ``` ## Docker ### Build locally ```sh docker build -t trips:local . ``` ### Run locally ```sh docker compose up --build ``` The container stores SQLite data at `/data/trips.db` (mounted as the `trips-data` volume in `docker-compose.yml`). ## CI (Gitea Actions) Workflows live in `.gitea/workflows`: - `pr-checks.yml`: runs lint, tests, and Docker build on pull requests. - `main-image.yml`: builds and pushes a Docker image on push to `main`, then calls the Portainer API to redeploy. It can also be run manually from the Actions UI. ### Registry secrets for image publish Configure these repository secrets in Gitea: - `REGISTRY_USERNAME` - `REGISTRY_PASSWORD` - `PORTAINER_URL` (for example, `https://portainer.example.com`) - `PORTAINER_API_KEY` (Portainer API key for a user with access to the stack) - `PORTAINER_STACK_ID` (numeric stack ID in Portainer) - `PORTAINER_ENDPOINT_ID` (numeric environment/endpoint ID in Portainer) - `PORTAINER_INSECURE_TLS` (optional: set to `true` only if Portainer uses self-signed TLS) By default, the publish workflow pushes to: - `registry.campbellwireless.net//:latest` - `registry.campbellwireless.net//:` If your registry host differs, edit `REGISTRY_HOST` in `.gitea/workflows/main-image.yml`. ## Deploy with Portainer API The `main-image.yml` workflow now calls the Portainer API after pushing `latest`. In Portainer, create/update your stack to use a published image (not `build`), for example: ```yaml services: trips: image: registry.campbellwireless.net//:latest container_name: trips restart: unless-stopped ports: - '3000:3000' volumes: - trips-data:/data env_file: - .env environment: NODE_ENV: production DATABASE_URL: file:/data/trips.db PORT: '3000' volumes: trips-data: ``` Then in Portainer: 1. Create an API key from your user profile (`My account` -> `API keys`). 2. Open the stack details page and note the stack ID. 3. Open `Environments` and note the endpoint/environment ID where the stack runs. 4. Save these values in your Gitea repository secrets: - `PORTAINER_URL` - `PORTAINER_API_KEY` - `PORTAINER_STACK_ID` - `PORTAINER_ENDPOINT_ID` - optional `PORTAINER_INSECURE_TLS=true` Flow on each push to `main` (or manual run of `main-image.yml`): 1. Build image. 2. Push `:latest` and `:`. 3. Run Portainer preflight checks (auth + stack/endpoint IDs). 4. Call Portainer API. 5. Portainer redeploys the stack and pulls the updated image.