No description
Find a file
NathanBland 7e1c4bf80e
Some checks failed
CI/CD Pipeline / Lint, Type-check, Test (push) Successful in 51s
CI/CD Pipeline / Build and Push Image (push) Failing after 3m22s
CI/CD Pipeline / Deploy Staging (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
Update .forgejo/workflows/ci.yml
update stack ID
2026-01-14 16:15:16 -07:00
.forgejo/workflows Update .forgejo/workflows/ci.yml 2026-01-14 16:15:16 -07:00
.vscode Initial commit 2025-08-09 02:32:42 -06:00
.windsurf/rules feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
adapters/express feat: add Express server and Portainer deployment config with CI/CD pipeline 2025-08-18 17:09:24 -06:00
docs feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
drizzle/migrations feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
public Initial commit 2025-08-09 02:32:42 -06:00
scripts feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
src feat: add CSV import/export functionality with location and tag support 2026-01-14 13:26:16 -07:00
.dockerignore feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
.env.example feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
.gitignore Initial commit 2025-08-09 02:32:42 -06:00
.prettierignore Initial commit 2025-08-09 02:32:42 -06:00
.prettierrc.js feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
deploy-portainer.sh feat: add Express server and Portainer deployment config with CI/CD pipeline 2025-08-18 17:09:24 -06:00
docker-compose.prod.yml feat: simplify database volume configuration with direct bind mount path 2026-01-14 13:58:29 -07:00
docker-compose.yml feat: add CSV import/export functionality with location and tag support 2026-01-14 13:26:16 -07:00
Dockerfile feat: add Express server and Portainer deployment config with CI/CD pipeline 2025-08-18 17:09:24 -06:00
drizzle.config.ts feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
eslint.config.js feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
field-requirements.md feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
package-lock.json feat: add Express server and Portainer deployment config with CI/CD pipeline 2025-08-18 17:09:24 -06:00
package.json feat: add Express server and Portainer deployment config with CI/CD pipeline 2025-08-18 17:09:24 -06:00
qwik.env.d.ts Initial commit 2025-08-09 02:32:42 -06:00
README.md feat: add Express server and Portainer deployment config with CI/CD pipeline 2025-08-18 17:09:24 -06:00
tailwind.config.ts feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
tsconfig.json Initial commit 2025-08-09 02:32:42 -06:00
vite.config.ts feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00
vitest.config.ts feat: initialize music library project with database schema and API endpoints 2025-08-18 16:24:39 -06:00

Qwik City App


Project Structure

This project is using Qwik with QwikCity. QwikCity is just an extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.

Inside your project, you'll see the following directory structure:

├── public/
│   └── ...
└── src/
    ├── components/
    │   └── ...
    └── routes/
        └── ...
  • src/routes: Provides the directory-based routing, which can include a hierarchy of layout.tsx layout files, and an index.tsx file as the page. Additionally, index.ts files are endpoints. Please see the routing docs for more info.

  • src/components: Recommended directory for components.

  • public: Any static assets, like images, can be placed in the public directory. Please see the Vite public directory for more info.

Add Integrations and deployment

Use the npm run qwik add command to add additional integrations. Some examples of integrations includes: Cloudflare, Netlify or Express Server, and the Static Site Generator (SSG).

npm run qwik add # or `yarn qwik add`

Development

Development mode uses Vite's development server. The dev command will server-side render (SSR) the output during development.

npm start # or `yarn start`

Note: during dev mode, Vite may request a significant number of .js files. This does not represent a Qwik production build.

Preview

The preview command will create a production build of the client modules, a production build of src/entry.preview.tsx, and run a local server. The preview server is only for convenience to preview a production build locally and should not be used as a production server.

npm run preview # or `yarn preview`

Production

The production build will generate client and server modules by running both client and server build commands. The build command will use Typescript to run a type check on the source code.

npm run build # or `yarn build`

Setup & Local Development

Prerequisites

  • Node.js 18.17+ or 20.3+ (see package.json engines)
  • PostgreSQL (locally or remote). Version 14+ recommended.
  • A Postgres connection string for DATABASE_URL.

1) Install dependencies

npm install

2) Configure environment

Create a .env file in the project root with your database URL. Example:

# .env
DATABASE_URL=postgres://postgres:postgres@localhost:5432/music_lookup

Notes:

  • src/lib/db/client.ts preloads dotenv, so .env will be read automatically in dev/test.
  • Ensure your database exists and is reachable by the connection string.

3) Run database migrations

Apply all pending Drizzle migrations to your database:

npm run db:migrate

If you change the schema in src/lib/db/schema.ts, generate a new migration first:

npm run db:generate
npm run db:migrate

Optionally seed development data:

npm run db:seed

4) Start the dev server

npm run dev    # SSR dev server
# or
npm start      # opens browser, SSR mode

Open the app at the URL printed by Vite (usually http://localhost:5173).

Data Model & Requirements

  • The canonical requirements for the Pieces feature live in field-requirements.md.
  • Key fields (Pieces): title (required), hymnTune, composer, lastPlayed, description (Notes).
  • "Type" is managed via Tags (/api/tags) with kind=type. The UI should suggest existing values but allow creating new ones.
  • Search is fulltext across title, hymn tune, composer, notes, tags, and book titles (locations).

Common Commands

  • Lint: npm run lint
  • Format check: npm run fmt.check (or fix: npm run fmt)
  • Type check: npm run build.types
  • Unit tests: npm test (watch: npm run test:watch, coverage: npm run coverage)
  • Build preview server: npm run preview

Troubleshooting

  • "DATABASE_URL is not set" or connection errors: verify .env exists, Postgres is running, and the database name/user/password are correct.
  • Migrations fail: ensure the DB exists and the user has permissions; check drizzle/migrations/ for the latest SQL and re-run npm run db:migrate.
  • UI issues after schema changes: confirm you generated and applied new migrations (npm run db:generate && npm run db:migrate) and optionally reseed (npm run db:seed).

References

Quick Start

# 1) Install deps
npm install

# 2) Create .env with your Postgres connection string
printf "DATABASE_URL=postgres://postgres:postgres@localhost:5432/music_lookup\n" > .env

# 3) Apply migrations (and optionally seed)
npm run db:migrate
npm run db:seed   # optional

# 4) Start dev server
npm run dev

Then open http://localhost:5173

Resetting the DB for local development

If your local DB has drifted or migrations failed, use one of the following approaches.

  • Option B (one command): Drop and recreate the whole database via script

    Uses scripts/reset-db.mjs to terminate connections, drop, recreate, then run migrations (and optional seed).

    npm run db:reset        # drop + recreate + migrate
    npm run db:reset:seed   # drop + recreate + migrate + seed
    npm run db:reset:no-migrate  # drop + recreate only
    
  • Option A: Reset the public schema (keeps the same database)

    DANGER: This drops all tables in the database schema referenced by DATABASE_URL.

    # Uses the DATABASE_URL from your .env
    export $(grep -v '^#' .env | xargs)  # loads DATABASE_URL into the shell (bash/zsh)
    psql "$DATABASE_URL" -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
    
    # Re-apply schema
    npm run db:migrate
    npm run db:seed   # optional
    
  • Option B: Drop and recreate the whole database

    Replace music_lookup with your actual DB name from DATABASE_URL.

    # Terminate connections, drop, recreate
    psql -d postgres -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'music_lookup';"
    dropdb music_lookup || true
    createdb music_lookup
    
    # Re-apply schema
    npm run db:migrate
    npm run db:seed   # optional
    
  • Option C: Create a fresh DB and point .env to it

    createdb music_lookup_dev
    # then update DATABASE_URL in .env to use music_lookup_dev and run migrations/seed
    npm run db:migrate
    

Notes

  • If you see errors like "Failed query" or missing columns on pages like /pieces/, confirm that all migrations have been applied (e.g., 0001, 0002, 0003).
  • The project auto-loads .env in dev/test via src/lib/db/client.ts.
  • After a reset, always run npm run db:migrate (and npm run db:seed if you want demo data).

CI/CD and Production Deployment

This repo ships with a Forgejo Actions workflow and Docker/Compose config to deploy via Portainer.

Docker images

  • Dockerfile is multi-stage:
    • dev target runs npm run dev.host on port 5173.
    • prod image builds the app and serves it with a Node/Express server on port 3000.
    • On container start in prod, it runs database migrations via node scripts/migrate.mjs (requires DATABASE_URL).

Local development with Docker Compose

docker compose up --build
# App: http://localhost:5173
# Postgres: localhost:5432 (music/music)

.env.example has a sample DATABASE_URL. For dev compose, export DATABASE_URL in your shell or .env before docker compose up.

Production stack (Portainer)

  • docker-compose.prod.yml defines a two-service stack:
    • web (this app), exposes 3000 internally and maps to host 8080 by default.
    • db (Postgres 16-alpine) with a persisted volume db_data.
  • The web service expects DATABASE_URL=postgres://music:music@db:5432/music.
  • The image reference uses a tag placeholder: registry.aqueous.network/music-lookup:${API_IMAGE_TAG:-latest}.
    • Portainer webhook will pass API_IMAGE_TAG so the stack pulls the requested tag.

Configurable ports:

  • WEB_PORT external host port (default 8080).
  • WEB_INTERNAL_PORT internal container port (default 3000).
  • PORT Express server port inside the container. This is set automatically from WEB_INTERNAL_PORT.

Example overrides in Portainer stack env/variables:

WEB_PORT=9000
WEB_INTERNAL_PORT=4000

Then access the app at http://localhost:9000.

Portainer webhook deployment

  • Script: deploy-portainer.sh
    • Triggers a Portainer stack redeploy via webhook.
    • Appends API_IMAGE_TAG (and optional UI_IMAGE_TAG) query params to the webhook.
    • For production, the script polls the stack status until success/failure.
  • Required env when running the script (set by CI):
    • PORTAINER_URL (e.g., https://port.aqueous.network)
    • PORTAINER_API_KEY (Portainer API key)
    • WEBHOOK_URL (stack webhook URL generated by Portainer)
    • Args: <STACK_ID> <environment: staging|production> <image_tag>

Forgejo Actions workflow

Workflow: .forgejo/workflows/ci.yml

  • test: npm ci + lint + typecheck + vitest
  • build: Builds and pushes image to registry.aqueous.network/music-lookup with tags:
    • ${GITHUB_SHA}, ${BRANCH_NAME}, and latest
  • deploy-staging: on develop branch push
  • deploy-production: on main branch push

Configure repository secrets in Forgejo:

  • PORTAINER_API_KEY Portainer API key
  • PORTAINER_WEBHOOK_URL Webhook URL for your stack (Portainer > Stacks > Webhooks)

Environment variables inside the workflow (edit as needed):

  • REGISTRY defaults to registry.aqueous.network
  • IMAGE_NAME defaults to music-lookup
  • PORTAINER_STACK_ID_STAGING / PORTAINER_STACK_ID_PRODUCTION Update to your stack IDs

First-time production setup (manual)

  1. Build and push an initial image locally or trigger the CI build on main.
  2. In Portainer, create a new Stack using docker-compose.prod.yml from this repo.
    • Add an environment variable API_IMAGE_TAG with the image tag you want to deploy (e.g., latest or a commit SHA).
    • Ensure the stack has network egress to your registry.
  3. Create a Webhook for the stack and copy its URL.
  4. Add the webhook to Forgejo repository secrets as PORTAINER_WEBHOOK_URL.
  5. Set PORTAINER_STACK_ID_* values in the workflow to your stack ID.
  6. Push to develop or main to trigger deploy.

Notes on production serving

  • The production image now runs a Qwik City Node/Express server (SSR). You can add middleware (compression, security headers), logging, and health checks as needed.

Express Server

This app has a minimal Express server implementation. After running a full build, you can start the server using the command:

npm run serve

Then visit: