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.
- 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
| 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 |
├── 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
| 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 |
- 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
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/.envRoot .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
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.
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.
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:stopThe 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.
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:edgenpm 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.
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.
# 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:resetnpm 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.
| 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.
Additional documentation in docs/:
Contributions are welcome. Please open an issue to discuss proposed changes before submitting a pull request.
- Fork the repository
- Create a feature branch (
git checkout -b feature/description) - Commit changes using Conventional Commits
- Push to your fork and open a pull request
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.