taskflow / backend /src /utils /migrate.js
Sughan-0077's picture
🚀 Complete Taskflow Suite: Initial Clean Release
f6c14df
Raw
History Blame Contribute Delete
4.75 kB
require('dotenv').config();
const { query } = require('./db');
const migrate = async () => {
console.log('Running database migrations...');
await query(`
CREATE TABLE IF NOT EXISTS organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
slug VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
`);
await query(`
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
password_hash VARCHAR(255),
role VARCHAR(50) NOT NULL DEFAULT 'member' CHECK (role IN ('admin', 'member')),
oauth_provider VARCHAR(50),
oauth_provider_id VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(email, organization_id)
);
`);
await query(`CREATE INDEX IF NOT EXISTS idx_users_org ON users(organization_id);`);
await query(`CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);`);
await query(`
CREATE TABLE IF NOT EXISTS tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
title VARCHAR(500) NOT NULL,
description TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'todo' CHECK (status IN ('todo', 'in_progress', 'in_review', 'done', 'cancelled')),
priority VARCHAR(50) NOT NULL DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'critical')),
creator_id UUID NOT NULL REFERENCES users(id) ON DELETE SET NULL,
assignee_id UUID REFERENCES users(id) ON DELETE SET NULL,
due_date TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
`);
await query(`CREATE INDEX IF NOT EXISTS idx_tasks_org ON tasks(organization_id);`);
await query(`CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(organization_id, status);`);
await query(`CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee_id);`);
await query(`CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(organization_id, priority);`);
await query(`CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(organization_id, due_date);`);
await query(`
CREATE TABLE IF NOT EXISTS task_comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
author_id UUID NOT NULL REFERENCES users(id) ON DELETE SET NULL,
body TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
`);
await query(`CREATE INDEX IF NOT EXISTS idx_comments_task ON task_comments(task_id);`);
await query(`
CREATE TABLE IF NOT EXISTS audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
task_id UUID REFERENCES tasks(id) ON DELETE SET NULL,
actor_id UUID REFERENCES users(id) ON DELETE SET NULL,
actor_name VARCHAR(255) NOT NULL,
actor_email VARCHAR(255) NOT NULL,
action VARCHAR(100) NOT NULL,
entity_type VARCHAR(50) NOT NULL DEFAULT 'task',
old_values JSONB,
new_values JSONB,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
`);
await query(`CREATE INDEX IF NOT EXISTS idx_audit_org ON audit_logs(organization_id);`);
await query(`CREATE INDEX IF NOT EXISTS idx_audit_task ON audit_logs(task_id);`);
await query(`CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs(organization_id, created_at DESC);`);
await query(`
CREATE TABLE IF NOT EXISTS invites (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
email VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL DEFAULT 'member',
token VARCHAR(255) UNIQUE NOT NULL,
invited_by UUID REFERENCES users(id) ON DELETE SET NULL,
expires_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
`);
await query(`CREATE INDEX IF NOT EXISTS idx_invites_token ON invites(token);`);
await query(`CREATE INDEX IF NOT EXISTS idx_invites_org ON invites(organization_id);`);
console.log('✅ Migrations complete.');
process.exit(0);
};
migrate().catch(err => {
console.error('Migration failed:', err);
process.exit(1);
});