mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-12 01:46:08 +00:00
refactor(db): move rate-limit extras into Prisma migration and remove db-extras script
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
- Keep API and UI imports on `@/` aliases when available.
|
- Keep API and UI imports on `@/` aliases when available.
|
||||||
|
|
||||||
## Important locations
|
## Important locations
|
||||||
- Custom SQL not managed by Prisma migrations: `prisma/migrations/*.sql` and runner `scripts/db-extras.ts`.
|
- Custom SQL managed by Prisma migrations: `prisma/migrations/*/migration.sql`.
|
||||||
- Shared API response helpers: `lib/api-response.ts`.
|
- Shared API response helpers: `lib/api-response.ts`.
|
||||||
- Auth + access-control helpers: `lib/auth.ts`.
|
- Auth + access-control helpers: `lib/auth.ts`.
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -15,8 +15,7 @@
|
|||||||
"db:push": "prisma db push",
|
"db:push": "prisma db push",
|
||||||
"db:migrate": "prisma migrate deploy",
|
"db:migrate": "prisma migrate deploy",
|
||||||
"db:seed": "prisma db seed",
|
"db:seed": "prisma db seed",
|
||||||
"db:setup": "bun run db:generate && bun run db:push && bun run db:extras",
|
"db:setup": "bun run db:generate && bun run db:push && bun run db:migrate",
|
||||||
"db:extras": "bun run scripts/db-extras.ts",
|
|
||||||
"r2:cleanup-orphans:dry": "bun run scripts/r2-orphan-cleanup.ts --dry-run",
|
"r2:cleanup-orphans:dry": "bun run scripts/r2-orphan-cleanup.ts --dry-run",
|
||||||
"r2:cleanup-orphans": "bun run scripts/r2-orphan-cleanup.ts",
|
"r2:cleanup-orphans": "bun run scripts/r2-orphan-cleanup.ts",
|
||||||
"bunny:cleanup-orphans:dry": "bun run scripts/bunny-orphan-cleanup.ts --dry-run",
|
"bunny:cleanup-orphans:dry": "bun run scripts/bunny-orphan-cleanup.ts --dry-run",
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
-- Rate limiting extras migration
|
||||||
|
-- Keeps compatibility with existing environments created via prisma db push.
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS rate_limits (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
key VARCHAR(255) NOT NULL,
|
||||||
|
action VARCHAR(50) NOT NULL,
|
||||||
|
count INTEGER NOT NULL DEFAULT 1,
|
||||||
|
window_start TIMESTAMP NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'rate_limits_key_action_key'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE rate_limits
|
||||||
|
ADD CONSTRAINT rate_limits_key_action_key UNIQUE (key, action);
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rate_limits_key_action ON rate_limits(key, action);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rate_limits_window_start ON rate_limits(window_start);
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_class
|
||||||
|
WHERE relname = 'rate_limits'
|
||||||
|
AND relkind = 'r'
|
||||||
|
AND relpersistence <> 'u'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE rate_limits SET UNLOGGED;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION cleanup_rate_limits() RETURNS void AS $$
|
||||||
|
BEGIN
|
||||||
|
DELETE FROM rate_limits WHERE window_start < NOW() - INTERVAL '1 hour';
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
-- Rate Limiting Table (UNLOGGED for performance)
|
|
||||||
-- Run this migration manually: psql $DATABASE_URL -f prisma/migrations/rate_limit.sql
|
|
||||||
|
|
||||||
-- Drop if exists (for re-running)
|
|
||||||
DROP TABLE IF EXISTS rate_limits;
|
|
||||||
|
|
||||||
-- Create UNLOGGED table for rate limiting
|
|
||||||
-- UNLOGGED = no WAL writes = faster, but data lost on crash (acceptable for rate limits)
|
|
||||||
CREATE UNLOGGED TABLE rate_limits (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
key VARCHAR(255) NOT NULL, -- e.g., "register:192.168.1.1" or "login:[email protected]"
|
|
||||||
action VARCHAR(50) NOT NULL, -- e.g., "register", "login", "api"
|
|
||||||
count INTEGER NOT NULL DEFAULT 1,
|
|
||||||
window_start TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
||||||
|
|
||||||
-- Unique constraint for upsert operations
|
|
||||||
UNIQUE(key, action)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Index for fast lookups
|
|
||||||
CREATE INDEX idx_rate_limits_key_action ON rate_limits(key, action);
|
|
||||||
|
|
||||||
-- Index for cleanup operations
|
|
||||||
CREATE INDEX idx_rate_limits_window_start ON rate_limits(window_start);
|
|
||||||
|
|
||||||
-- Auto-cleanup function: removes expired entries
|
|
||||||
CREATE OR REPLACE FUNCTION cleanup_rate_limits() RETURNS void AS $$
|
|
||||||
BEGIN
|
|
||||||
DELETE FROM rate_limits WHERE window_start < NOW() - INTERVAL '1 hour';
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/**
|
|
||||||
* db-extras.ts — Run all custom SQL migrations that Prisma doesn't manage.
|
|
||||||
*
|
|
||||||
* This script runs after `prisma db push` / `prisma migrate deploy` to set up
|
|
||||||
* tables and functions that need raw SQL (UNLOGGED tables, custom functions, etc.).
|
|
||||||
*
|
|
||||||
* Usage: bun run db:extras
|
|
||||||
*
|
|
||||||
* To add new custom migrations:
|
|
||||||
* 1. Create a .sql file in prisma/migrations/
|
|
||||||
* 2. Add the filename to the EXTRAS array below
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { readFileSync } from 'fs';
|
|
||||||
import { join } from 'path';
|
|
||||||
import pg from 'pg';
|
|
||||||
|
|
||||||
const EXTRAS = [
|
|
||||||
'rate_limit.sql',
|
|
||||||
// Add future custom SQL files here
|
|
||||||
];
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const url = process.env.DATABASE_URL;
|
|
||||||
if (!url) {
|
|
||||||
console.error('❌ DATABASE_URL is not set');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip Prisma-specific query params (e.g. ?schema=public) that pg doesn't understand
|
|
||||||
const cleanUrl = url.split('?')[0];
|
|
||||||
const client = new pg.Client({ connectionString: cleanUrl });
|
|
||||||
|
|
||||||
try {
|
|
||||||
await client.connect();
|
|
||||||
console.log('✅ Connected to database\n');
|
|
||||||
|
|
||||||
for (const file of EXTRAS) {
|
|
||||||
const filePath = join(import.meta.dirname, '..', 'prisma', 'migrations', file);
|
|
||||||
const sql = readFileSync(filePath, 'utf-8');
|
|
||||||
|
|
||||||
console.log(`▸ Running ${file}...`);
|
|
||||||
await client.query(sql);
|
|
||||||
console.log(` ✓ ${file} applied\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ All database extras applied successfully');
|
|
||||||
} catch (err) {
|
|
||||||
console.error('❌ Database extras failed:', err);
|
|
||||||
process.exit(1);
|
|
||||||
} finally {
|
|
||||||
await client.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
Reference in New Issue
Block a user