Audit all 52 tools and 119 API operations against the OpenAPI spec.
Fix schema mismatches, add missing endpoints, and update all doc surfaces.
OpenAPI spec (apps/api/src/openapi.yaml):
- Add operationId to all 119 operations for SDK code generation
- Add descriptions to 17 operations that only had summaries
- Fix 9 bare error responses with proper content/schema blocks
- Fix wrong $ref on GET /api/v1/settings/{key} 404 (was UnauthorizedError)
- Fix 10 async AI tools using 200 instead of 202 Accepted
- Add missing JXL format to 9 tool enums (convert, collage, stitch, split,
svg-to-raster, pdf-to-image, optimize-for-web, erase-object, noise-removal)
- Add 3 missing endpoints: POST /api/v1/fetch-urls, GET /api/v1/config/locale,
POST /api/v1/tools/ai-canvas-expand
- Add missing meme-templates/fonts/{filename} endpoint
- Fix watermark-image and compose: settings not required (all have defaults)
- Fix compare similarity description (0-100 percentage, not 0-1)
- Fix find-duplicates: add nullable thumbnail, add skippedFiles response field
- Fix upscale format default ("auto" not "png"), add clientJobId fields
- Fix restore-photo: remove phantom mode param, fix denoiseStrength default,
add missing colorizeStrength
- Fix smart-crop: add 5 missing parameter defaults
- Add missing params: image-enhancement deepEnhance, transparency-fixer
removeWatermark, image-to-pdf collate, image-to-base64 jxl enum
- Fix files/{id}/thumbnail security (requires auth, was marked public)
- Annotate 7 empty description strings in pipeline/role schemas
Documentation surfaces:
- VitePress config: "50 tools" to "51 tools" (2 locations)
- README: add OIDC/SSO docs link
- getting-started.md: add Color Blindness Simulation to tool table
- database.md: fix hash descriptions (bcrypt/SHA-256 to scrypt)
4.1 KiB
Database
SnapOtter uses SQLite with Drizzle ORM for data persistence. The schema is defined in apps/api/src/db/schema.ts.
The database file lives at the path set by DB_PATH (defaults to ./data/snapotter.db). In Docker, mount the /data volume to persist it across container restarts.
Tables
users
Stores user accounts. Created automatically on first run from DEFAULT_USERNAME and DEFAULT_PASSWORD.
| Column | Type | Notes |
|---|---|---|
id |
integer | Primary key, auto-increment |
username |
text | Unique, required |
passwordHash |
text | scrypt hash |
role |
text | admin or user |
mustChangePassword |
integer | Boolean flag for forced password reset |
createdAt |
text | ISO timestamp |
updatedAt |
text | ISO timestamp |
sessions
Active login sessions. Each row ties a session token to a user.
| Column | Type | Notes |
|---|---|---|
id |
text | Primary key (session token) |
userId |
integer | Foreign key to users.id |
expiresAt |
text | ISO timestamp |
createdAt |
text | ISO timestamp |
teams
Groups for organizing users. Admins can assign users to teams.
| Column | Type | Description |
|---|---|---|
id |
text UUID | Primary key |
name |
text (unique, max 50 chars) | Team name |
createdAt |
integer | Unix timestamp |
api_keys
API keys for programmatic access. The raw key is shown once on creation; only the hash is stored.
| Column | Type | Notes |
|---|---|---|
id |
integer | Primary key, auto-increment |
userId |
integer | Foreign key to users.id |
keyHash |
text | scrypt hash of the key |
name |
text | User-provided label |
createdAt |
text | ISO timestamp |
lastUsedAt |
text | Updated on each authenticated request |
Keys are prefixed with si_ followed by 96 hex characters (48 random bytes).
pipelines
Saved tool chains that users create in the UI.
| Column | Type | Notes |
|---|---|---|
id |
integer | Primary key, auto-increment |
name |
text | Pipeline name |
description |
text | Optional description |
steps |
text | JSON array of { toolId, settings } objects |
createdAt |
text | ISO timestamp |
user_files
Persistent file library with version chain tracking. Each processing step that saves a result creates a new row linked to its parent via parentId, forming a version tree.
| Column | Type | Description |
|---|---|---|
id |
text UUID | Primary key |
userId |
text UUID | FK → users (CASCADE DELETE) |
originalName |
text | Original upload filename |
storedName |
text | Filename on disk |
mimeType |
text | MIME type |
size |
integer | File size in bytes |
width |
integer | Image width in px |
height |
integer | Image height in px |
version |
integer | Version number (1 = original) |
parentId |
text UUID | null | FK → user_files (parent version) |
toolChain |
text (JSON array) | Tool IDs applied in order to produce this version |
createdAt |
integer | Unix timestamp |
jobs
Tracks processing jobs for progress reporting and cleanup.
| Column | Type | Notes |
|---|---|---|
id |
text | Primary key (UUID) |
type |
text | Tool or pipeline identifier |
status |
text | queued, processing, completed, or failed |
progress |
real | 0.0–1.0 fraction |
inputFiles |
text | JSON array of input file paths |
outputPath |
text | Path to the result file |
settings |
text | JSON of the tool settings used |
error |
text | Error message if failed |
createdAt |
text | ISO timestamp |
completedAt |
text | ISO timestamp |
settings
Key-value store for server-wide settings that admins can change from the UI.
| Column | Type | Notes |
|---|---|---|
key |
text | Primary key |
value |
text | Setting value |
updatedAt |
text | ISO timestamp |
Migrations
Drizzle handles schema migrations. The config is in apps/api/drizzle.config.ts. During development, run:
pnpm --filter @snapotter/api drizzle-kit push
In production, the schema is applied automatically on startup.