This commit is contained in:
Matthias Hochmeister
2026-02-23 17:08:58 +01:00
commit f09748f4a1
97 changed files with 17729 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
authentik_sub VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255),
preferred_username VARCHAR(255),
given_name VARCHAR(255),
family_name VARCHAR(255),
profile_picture_url TEXT,
refresh_token TEXT,
refresh_token_expires_at TIMESTAMP WITH TIME ZONE,
last_login_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
preferences JSONB DEFAULT '{}',
is_active BOOLEAN DEFAULT TRUE
);
CREATE INDEX idx_users_authentik_sub ON users(authentik_sub);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_last_login ON users(last_login_at);
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

View File

@@ -0,0 +1,223 @@
# Database Migrations
This directory contains SQL migration files for the Feuerwehr Dashboard database schema.
## Overview
Migrations are automatically executed when the application starts. Each migration file is tracked in the `migrations` table to ensure it only runs once.
## Migration Files
Migration files follow the naming convention: `{number}_{description}.sql`
Example:
- `001_create_users_table.sql`
- `002_add_roles_table.sql`
The numeric prefix determines the execution order. Always use sequential numbering.
## How Migrations Work
### Automatic Execution (Docker)
When running the application with Docker, migrations are automatically executed during startup:
1. Application starts
2. Database connection is established
3. `runMigrations()` function is called
4. Migration tracking table (`migrations`) is created if it doesn't exist
5. Each `.sql` file in this directory is checked:
- If already executed (recorded in `migrations` table): **skipped**
- If new: **executed within a transaction**
6. Successfully executed migrations are recorded in the tracking table
### Migration Tracking
The system creates a `migrations` table to track which migrations have been executed:
```sql
CREATE TABLE migrations (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) UNIQUE NOT NULL,
executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
```
This ensures each migration runs exactly once, even across multiple deployments.
## Manual Migration Execution
If you need to run migrations manually (development or troubleshooting):
### Using psql (PostgreSQL CLI)
```bash
# Connect to the database
docker exec -it feuerwehr-postgres psql -U feuerwehr_user -d feuerwehr_db
# Run a specific migration
\i /path/to/migration/001_create_users_table.sql
# Or if inside the container
\i /docker-entrypoint-initdb.d/migrations/001_create_users_table.sql
```
### Using npm/node script
Create a migration runner script:
```bash
# From backend directory
npm run migrate
```
You can add this to `package.json`:
```json
{
"scripts": {
"migrate": "ts-node src/scripts/migrate.ts"
}
}
```
## Creating New Migrations
1. **Determine the next migration number**
- Check existing files in this directory
- Use the next sequential number (e.g., if `001_` exists, create `002_`)
2. **Create a new `.sql` file**
```bash
touch src/database/migrations/002_add_new_feature.sql
```
3. **Write your SQL schema changes**
```sql
-- Always include IF EXISTS checks for safety
CREATE TABLE IF NOT EXISTS my_table (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Add indexes
CREATE INDEX IF NOT EXISTS idx_my_table_name ON my_table(name);
```
4. **Restart the application**
- Docker will automatically detect and run the new migration
- Or call `runMigrations()` programmatically
## Best Practices
### 1. Make Migrations Idempotent
Always use `IF EXISTS` or `IF NOT EXISTS` clauses:
```sql
CREATE TABLE IF NOT EXISTS users (...);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
ALTER TABLE users ADD COLUMN IF NOT EXISTS new_column VARCHAR(255);
```
### 2. Use Transactions
Each migration runs within a transaction automatically. If any part fails, the entire migration is rolled back.
### 3. Never Modify Existing Migrations
Once a migration has been deployed to production:
- **Never modify it**
- Create a new migration for changes
- This ensures consistency across all environments
### 4. Test Migrations Locally
Before deploying:
```bash
# Start fresh database
docker-compose down -v
docker-compose up -d postgres
# Run migrations
npm run dev
# or
npm run migrate
```
### 5. Include Rollback Instructions
Document how to revert changes in comments:
```sql
-- Migration: Add user preferences column
-- Rollback: ALTER TABLE users DROP COLUMN preferences;
ALTER TABLE users ADD COLUMN preferences JSONB DEFAULT '{}';
```
## Troubleshooting
### Migration Failed
If a migration fails:
1. **Check the logs** for error details
2. **Fix the SQL** in the migration file
3. **Remove the failed entry** from the tracking table:
```sql
DELETE FROM migrations WHERE filename = '002_failed_migration.sql';
```
4. **Restart the application** or re-run migrations
### Reset All Migrations
For development only (destroys all data):
```bash
# Drop and recreate database
docker-compose down -v
docker-compose up -d postgres
# Migrations will run automatically on next start
docker-compose up backend
```
### Check Migration Status
```sql
-- See which migrations have been executed
SELECT * FROM migrations ORDER BY executed_at DESC;
-- Check if a specific table exists
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'users'
);
```
## Current Migrations
### 001_create_users_table.sql
Creates the main `users` table for storing authenticated user data:
- **UUID primary key** with automatic generation
- **Authentik integration** fields (sub, email, profile data)
- **Token management** (refresh_token, expiration)
- **Audit fields** (last_login, created_at, updated_at)
- **User preferences** as JSONB
- **Active status** flag
- **Indexes** for performance:
- `authentik_sub` (unique identifier from OIDC)
- `email` (for lookups)
- `last_login_at` (for activity tracking)
- **Automatic timestamp updates** via trigger
## References
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
- [pg (node-postgres) Library](https://node-postgres.com/)
- Application database config: `src/config/database.ts`