Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

708 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crate Guide

License: AGPL v3 Live Site

A DJ-focused vinyl record collection manager with real-time session mixing, harmonic track discovery, and turntable simulation. Import from Discogs, organize crates, and mix with BPM and key-aware suggestions.

Features

  • Session Mixing — Two-deck mixing interface with pitch faders, track suggestions, and transition history
  • Turntable Simulation — SL-1200 style deck with animated platter, tonearm, and RPM controls
  • Collection Management — Catalog records and tracks with detailed metadata
  • Discogs Integration — Import records from your Discogs collection via OAuth 1.0
  • BPM & Key — Reuse Rekordbox XML or analyze local audio in the browser, then match and stage missing BPM/key values for review
  • Crate Organization — Color-coded crates for organizing gigs and sets
  • Harmonic Mixing — Find compatible tracks by BPM range and Camelot key relationships
  • Track Discovery — Filter and search by BPM, key, genre, artist, and more
  • Session Tracking — Log played tracks, rate transitions, and save set history
  • Demo Mode — Try the full app without creating an account

Architecture

Tech Stack

Layer Technology
Frontend Nuxt 4 (SPA), Vue 3 Composition API, TypeScript
Styling Tailwind CSS v4, shadcn-vue (reka-ui)
State Pinia
Backend Supabase (PostgreSQL, Auth, Deno Edge Functions)
Testing Vitest projects, Nuxt Test Utils, Playwright
External APIs Discogs
Audio Essentia.js, music-metadata, Web Audio API, Web Worker

Project Structure

├── app/
│   ├── components/     # Vue components (auto-imported)
│   │   ├── session/    # Deck mixing UI (suggestions, faders, history)
│   │   ├── turntable/  # SL-1200 simulation (platter, tonearm, controls)
│   │   ├── tracks/     # Track list, filters, editing
│   │   ├── records/    # Record cards, details, editing
│   │   ├── crates/     # Crate management
│   │   ├── import/     # Collection import dialogs
│   │   ├── icons/      # Icon components (auto-prefixed)
│   │   ├── notices/    # Notice components (auto-prefixed)
│   │   ├── ui/         # shadcn-vue primitives
│   │   └── ...
│   ├── composables/    # Vue composables and colocated store-project tests
│   ├── layouts/        # Nuxt layouts
│   ├── middleware/     # Route middleware (auth)
│   ├── pages/          # Route pages (+ demo/ routes)
│   ├── stores/         # Pinia stores and colocated store-project tests
│   └── utils/          # Pure utilities and colocated unit tests
├── server/             # Nitro routes/utilities; configured server test project
├── shared/             # Shared types, analyzer config, and unit tests
├── test/
│   ├── e2e/            # Nuxt Test Utils browser E2E tests
│   ├── mocks/          # Shared test doubles and fixtures
│   └── nuxt/           # Nuxt runtime/component tests
├── scripts/            # Development and maintenance scripts/tests
└── supabase/
    ├── functions/      # Deno Edge Functions and Deno tests
    └── migrations/     # PostgreSQL migrations

Data Model

Entity Description
profiles User preferences, turntable settings, and public Discogs identity
discogs_credentials Private OAuth credentials accessed by verified Edge repositories
records Vinyl records with metadata (artists, labels, year, cover)
tracks Tracks with BPM, key, duration, genres, RPM, Beatport metadata
crates Color-coded record collections with descriptions
sets DJ session history with played tracks and transition ratings

Development

Prerequisites

  • Node.js 24.18.1
  • npm 11.16.0
  • Deno 2.x (for Edge Function checks and tests)
  • Docker (for Supabase local development)
  • Supabase CLI

Environment Setup

git clone <repository-url>
cd crate-guide
npm install

# One-time browser download for Nuxt Test Utils E2E tests
npx playwright install chromium

# Start the reserved local Supabase stack, then write its public URL/key to .env
npm run supa:stack:start
npm run setup:local

# Create the untracked Edge env file; add your own Discogs app values if needed
cp supabase/functions/.env.example supabase/functions/.env

Root .env — public browser connection values:

SUPABASE_URL=http://127.0.0.1:42821
SUPABASE_ANON_KEY=<public local anon key from npm run setup:local>
SITE_URL=http://localhost:3000

SITE_URL must be one absolute HTTP(S) origin with no path, query, fragment, or credentials. A root trailing slash is accepted and normalized.

Do not copy SERVICE_ROLE_KEY, database passwords, or other secret values from supabase status into the root .env; browser code needs only API_URL and the public anonymous key. npm run setup:local refuses to overwrite an existing .env unless -- --force is explicitly supplied.

supabase/functions/.env — Discogs OAuth (for Edge Functions):

DISCOGS_CONSUMER_KEY=
DISCOGS_CONSUMER_SECRET=
DISCOGS_USER_AGENT=CrateGuide/2.0
DISCOGS_RATE_LIMIT_PER_USER=
DISCOGS_RATE_LIMIT_GLOBAL=
DISCOGS_RATE_LIMIT_WINDOW_SECONDS=
ACCOUNT_COVER_CLEANUP_SCHEDULER_SECRET=
SITE_URL=http://localhost:3000

Auth Redirect URLs

The repository's supabase/config.toml configures only the local Supabase stack. For every hosted environment, verify the actual Supabase Auth URL Configuration and explicitly add these Redirect URLs using that environment's real SITE_URL:

  • ${SITE_URL}/update-password?redirect=** (or an equivalently narrow pattern supported by Supabase)
  • ${SITE_URL}/auth/finalising?redirect=** (or an equivalently narrow pattern supported by Supabase)

The finalising callback needs a query-aware pattern because its encoded redirect value is dynamic. Confirm the hosted values before relying on OAuth deep links or password recovery. supabase config push mutates the linked hosted project; it is not a verification command and should be run only as an intentional remote configuration change.

Running Locally

Crate Guide reserves the uncommon 42820-42829 range for its local Supabase stack so it can run alongside projects using Supabase's default 54320-54329 ports. The main local endpoints are:

Service URL / port
API + Functions http://127.0.0.1:42821
PostgreSQL 127.0.0.1:42822
Studio http://127.0.0.1:42823
Email testing http://127.0.0.1:42824
Shadow database 127.0.0.1:42820
Analytics 127.0.0.1:42827
Edge inspector 127.0.0.1:42828
Pooler 127.0.0.1:42829 (currently disabled)

npm run dev:all supervises the Edge worker and Nuxt. Its health check requires the exact OPTIONS/CORS contract from authenticated-discogs-request, so a gateway-level 404 is not treated as a running function.

Staging secrets

npm run secrets:staging is deliberately disabled until a maintainer supplies deployment/staging-project.json with an authoritative supabaseProjectRef. The wrapper never trusts an arbitrary environment project ref, validates the untracked function env file, and uses the locked local Supabase CLI. Use npm run secrets:staging -- --dry-run to inspect arguments after that deployment record exists; neither this repository nor tests perform a remote upload.

# Full supervised environment (Nuxt + Supabase + Edge Functions)
npm run dev:all

# Nuxt only (requires external Supabase)
npm run dev

# Supabase + supervised Edge Functions (long-running)
npm run supa:start

# Edge Functions only when Supabase is already running (long-running)
npm run supa:functions

# Confirm the local function gateway and worker are responding
npm run supa:health

# Stop the local Supabase stack from another terminal
npm run supa:stop

The supervised commands intentionally stay in the foreground. If either Nuxt or the Edge Functions worker exits unexpectedly, or if the function runtime repeatedly fails its health check, the command reports the failure and stops its other child process instead of leaving a partially healthy-looking development environment. The local --no-verify-jwt flag only bypasses the gateway check; every current function still verifies the caller in its handler. The Discogs handlers call supabase.auth.getUser() before creating a service-role repository scoped to that verified user ID.

Testing

Vitest is the single application test runner. Its projects separate pure unit tests (app/utils and shared), Pinia/composable/middleware tests, a configured Nitro server project (currently with no test files), Nuxt runtime tests (test/nuxt), and browser E2E tests (test/e2e). The E2E project uses Nuxt Test Utils backed by the exact installed playwright runtime; there is no separate Playwright Test suite.

# Unit + store/composable + server + Nuxt runtime tests (watch mode)
npm test

# The same four projects, once
npm run test:run

# Pure unit project only
npm run test:unit

# Nuxt runtime project only
npm run test:nuxt

# Nuxt Test Utils browser E2E project
npm run test:e2e

# Deno Edge Function type-check, lint, and tests
npm run check:edge
npm run lint:edge
npm run test:edge

# Supabase SQL tests (requires the running local stack)
npm run test:db

# Current Discogs/README documentation contract
npm run check:discogs-docs

# Frozen Deno-lock vulnerability audit (queries OSV.dev)
npm run audit:edge

npm run test:db runs every pgTAP suite under supabase/tests against the local Supabase stack. It requires the Supabase CLI and a running local stack; it does not target a linked hosted project.

Code Quality

npm run format               # Write Prettier formatting
npm run format:check         # Check formatting without writes
npm run lint                 # ESLint
npm run lint:fix             # ESLint with auto-fix
npm run typecheck            # Nuxt/TypeScript checking
npm run audit:prod           # Audit production dependencies at high severity
npm run audit:all            # Audit the complete dependency graph
npm run audit:edge           # Audit frozen Edge dependencies through OSV
npm run check:conventions    # Component naming and Tailwind boundaries
npm run check:discogs-docs   # Reject stale Discogs documentation contracts
npm run test:typegen-script  # Failure/rollback tests for genTypes
npm run test:database-type-parity # Tests for the generated type parity gate
npm run check:database-types # Reject missing, empty, or differing type copies
npm run check:database-schema-types # Compare copies to the migrated schema
npm run test:audio-config    # Shared analyzer/benchmark config tests
npm run test:conventions     # Convention checker tests
npm run test:dependency-topology # Tests for the focused topology gate
npm run check:dependency-topology # Validate reviewed Vue/crossws/H3 topology
npm run test:security-headers # Unit-test the browser response policy
npm run check:security-headers # Inspect the built Cloudflare Worker response
npm run test:client-bundle-budget # Unit-test semantic asset classification
npm run check:client-bundle-budget # Inspect the built browser asset budget
npm run verify               # Comprehensive read-only verification gate
npm run verify:full          # Application, build, and local database gate
npm run build                # Production build (separate from verify)

npm run verify runs formatting, lint, type checking, the application, E2E, and browser tests, all three Edge gates, the Discogs documentation contract, and the maintenance/convention/dependency-topology tests above. It is read-only; run npm run format separately when files need formatting. A production build is also a separate release check. For release and deployment-affecting handoffs, run npm run verify:full; it adds the production build and local database tests, so it requires a running local Supabase stack.

The live schema comparison generates types into a temporary directory and byte-compares them with both tracked copies; it never rewrites the CI worktree. Edge auditing covers every npm package in supabase/deno.lock and fails closed on unsupported remote or JSR entries. A temporary high-severity exception in security/edge-audit-suppressions.json must name the advisory and package plus an owner, rationale, and unexpired YYYY-MM-DD date.

Browser containment is source-controlled in the Nitro response path rather than a Cloudflare dashboard. npm run check:security-headers imports the built Cloudflare Pages Worker, verifies its HTTPS and local-HTTP responses, and checks the generated static-asset routing boundary. See docs/browser-security.md for the policy and the read-only post-deploy check.

The production bundle gate follows Nuxt's semantic entry graph and reports Worker, WASM, CSS, and named lazy modules separately from initial JavaScript. See docs/client-bundle-budget.md for the measured baseline and exact 3% non-regression allowance.

npm run audit:prod checks the installed production dependency graph and fails on high or critical advisories. The exact esbuild development dependency is intentional: it keeps the shared Nuxt/Vite production graph on the fixed release while the incompatible ESLint inspector copy remains isolated to development. Recheck both npm audit --omit=dev and npm explain esbuild when changing it.

GitHub Actions runs source-controlled CI for every pull request and push to main. The application job checks the focused dependency topology, audits both the production and complete dependency graphs, installs Chromium, Firefox, and WebKit, runs npm run verify, and builds the production application. The database job starts the tracked local Supabase stack and runs npm run test:db, without using a linked hosted project. Locally, npm run verify:full is the nearest equivalent to both jobs; it requires Docker and a running local Supabase stack.

Database

# Generate TypeScript types (requires the CLI and a running local Supabase stack)
npm run genTypes

# Run every pgTAP suite against the running local Supabase stack
npm run test:db

# Reset local database (applies migrations + seed)
npm run supa:reset

npm run genTypes validates and Prettier-formats generated output before replacing the intentional byte-identical copies at shared/types/database.ts and supabase/functions/_shared/types/database.ts. The application and Deno Edge Function module trees consume their respective copies. Any failure before replacement leaves both files unchanged; a partial replacement restores both prior states and reports if restoration cannot complete. Run npm run check:database-types to check the tracked copies without a database; npm run verify includes this gate and rejects one-sided drift.

Edge Functions

Function Purpose
get-discogs-request-token Verifies the caller, acquires quota, and starts the Discogs OAuth 1.0 flow.
get-discogs-access-token Validates the callback, exchanges/stores credentials, and refreshes public identity.
authenticated-discogs-request Dispatches only validated folder, folder-release, and release reads with server signing.
cleanup-record-covers Drains durable obsolete-cover jobs for the verified user without accepting client paths.
cleanup-orphaned-record-covers Processes one bounded, leased account-cover cleanup batch for an authenticated scheduler.
delete-account Requires recent authentication, schedules durable bounded cleanup, then deletes the account.

For local emulation, npm run supa:functions uses --no-verify-jwt; handler authentication remains active. Source-controlled supabase/config.toml sets verify_jwt = false for these functions so asymmetric project signing keys do not depend on legacy gateway verification. Every handler still authenticates its caller internally before privileged work. This documents repository configuration only—verify the actual function, gateway, migration, and secret state in each hosted environment before release. Verify each hosted environment separately.

Documentation

Additional documentation in docs/:

Contributing

Contributions are welcome. Please open an issue to discuss proposed changes before submitting a pull request.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/description)
  3. Commit changes using Conventional Commits
  4. Push to your fork and open a pull request

License

Crate Guide is a personal, non-commercial project maintained for fun and intended to remain open source. It is licensed under the GNU Affero General Public License v3.0. Network users must be offered the corresponding source for modified versions.

See Third-Party Notices for dependency attribution.

About

Find compatible tracks across your physical record collection.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages