Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
cp .env.example .env
bun install
Run
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:
INSERT INTO local_credentials (user_id, password_hash)
VALUES ('<user-id>', '<argon2id hash>');
Passwords must be at least 12 characters. Avoid storing plaintext passwords anywhere.
Quality and Tests
bun run lint
bun run test
bun run check
Build and Preview
bun run build
bun run preview
Docker
Build locally
docker build -t trips:local .
Run locally
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 tomain, 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_USERNAMEREGISTRY_PASSWORDPORTAINER_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 totrueonly if Portainer uses self-signed TLS)
By default, the publish workflow pushes to:
registry.campbellwireless.net/<owner>/<repo>:latestregistry.campbellwireless.net/<owner>/<repo>:<short-sha>
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:
services:
trips:
image: registry.campbellwireless.net/<owner>/<repo>: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:
- Create an API key from your user profile (
My account->API keys). - Open the stack details page and note the stack ID.
- Open
Environmentsand note the endpoint/environment ID where the stack runs. - Save these values in your Gitea repository secrets:
PORTAINER_URLPORTAINER_API_KEYPORTAINER_STACK_IDPORTAINER_ENDPOINT_ID- optional
PORTAINER_INSECURE_TLS=true
Flow on each push to main (or manual run of main-image.yml):
- Build image.
- Push
:latestand:<short-sha>. - Run Portainer preflight checks (auth + stack/endpoint IDs).
- Call Portainer API.
- Portainer redeploys the stack and pulls the updated image.