diff --git a/DOCKER_GIT_DEPLOYMENT.md b/DOCKER_GIT_DEPLOYMENT.md index 175db9b..30f5fe2 100644 --- a/DOCKER_GIT_DEPLOYMENT.md +++ b/DOCKER_GIT_DEPLOYMENT.md @@ -22,7 +22,10 @@ docker-compose up docker-compose -f docker-compose.prod.yml up --build ``` -**Note**: Both development and production Docker builds automatically create the default admin account. +**Note**: Both development and production Docker builds automatically: +- Create the default admin account +- Run any pending database migrations +- Initialize/update the database schema ### 2. Deploy from Git Repository diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 4670af0..217da9a 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -20,6 +20,10 @@ chmod -R 755 /app/public/uploads echo "🔧 Setting up admin account..." node scripts/create-admin.js +# Run any pending database migrations +echo "🔄 Running database migrations..." +./run-migrations.sh + # Start the application echo "✅ Starting production server..." exec npm start diff --git a/migrate-add-team-lead-role.mjs b/migrate-add-team-lead-role.mjs new file mode 100644 index 0000000..215a8e5 --- /dev/null +++ b/migrate-add-team-lead-role.mjs @@ -0,0 +1,75 @@ +import db from './src/lib/db.js'; + +console.log('Starting migration to add team_lead role to users table constraint...'); + +try { + // Disable foreign key constraints temporarily + db.pragma('foreign_keys = OFF'); + console.log('Disabled foreign key constraints'); + + // Since SQLite doesn't support modifying CHECK constraints directly, + // we need to recreate the table with the new constraint + + // First, create a backup table with current data + db.exec('CREATE TABLE users_backup AS SELECT * FROM users'); + console.log('Created backup table'); + + // Drop the original table + db.exec('DROP TABLE users'); + console.log('Dropped original table'); + + // Recreate the table with the updated constraint + db.exec(` + CREATE TABLE users ( + id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))), + name TEXT NOT NULL, + username TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + role TEXT CHECK(role IN ('admin', 'team_lead', 'project_manager', 'user', 'read_only')) DEFAULT 'user', + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP, + is_active INTEGER DEFAULT 1, + last_login TEXT, + failed_login_attempts INTEGER DEFAULT 0, + locked_until TEXT, + can_be_assigned INTEGER DEFAULT 1, + initial TEXT + ) + `); + console.log('Created new table with updated constraint'); + + // Copy data back from backup + db.exec(` + INSERT INTO users ( + id, name, username, password_hash, role, created_at, updated_at, + is_active, last_login, failed_login_attempts, locked_until, + can_be_assigned, initial + ) + SELECT + id, name, username, password_hash, role, created_at, updated_at, + is_active, last_login, failed_login_attempts, locked_until, + can_be_assigned, initial + FROM users_backup + `); + console.log('Copied data back from backup'); + + // Drop the backup table + db.exec('DROP TABLE users_backup'); + console.log('Dropped backup table'); + + // Re-enable foreign key constraints + db.pragma('foreign_keys = ON'); + console.log('Re-enabled foreign key constraints'); + + // Verify the migration + const userCount = db.prepare('SELECT COUNT(*) as count FROM users').get(); + console.log(`✅ Migration completed successfully! Users table now has ${userCount.count} records`); + + // Verify the constraint allows the new role + console.log('✅ CHECK constraint now includes: admin, team_lead, project_manager, user, read_only'); + +} catch (error) { + console.error('❌ Migration failed:', error.message); + console.error('You may need to restore from backup manually'); + process.exit(1); +} \ No newline at end of file diff --git a/run-migrations.sh b/run-migrations.sh new file mode 100644 index 0000000..4c05ad5 --- /dev/null +++ b/run-migrations.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Database migration runner for deployment +# This script runs all pending migrations in order + +echo "🔄 Running database migrations..." + +# List of migration scripts to run (in order) +MIGRATIONS=( + "migrate-add-team-lead-role.mjs" +) + +for migration in "${MIGRATIONS[@]}"; do + if [ -f "$migration" ]; then + echo "Running migration: $migration" + if node "$migration"; then + echo "✅ Migration $migration completed successfully" + # Optionally move completed migration to a completed folder + # mkdir -p migrations/completed + # mv "$migration" "migrations/completed/" + else + echo "❌ Migration $migration failed" + exit 1 + fi + else + echo "Migration file $migration not found, skipping..." + fi +done + +echo "✅ All migrations completed" \ No newline at end of file diff --git a/src/app/admin/users/page.js b/src/app/admin/users/page.js index 9c9f15c..364dbbb 100644 --- a/src/app/admin/users/page.js +++ b/src/app/admin/users/page.js @@ -125,6 +125,8 @@ export default function UserManagementPage() { switch (role) { case "admin": return "red"; + case "team_lead": + return "purple"; case "project_manager": return "blue"; case "user": @@ -140,6 +142,8 @@ export default function UserManagementPage() { switch (role) { case "project_manager": return "Project Manager"; + case "team_lead": + return "Team Lead"; case "read_only": return "Read Only"; default: @@ -433,6 +437,7 @@ function CreateUserModal({ onClose, onUserCreated }) { + diff --git a/src/lib/init-db.js b/src/lib/init-db.js index 1ba1c79..47c9e3e 100644 --- a/src/lib/init-db.js +++ b/src/lib/init-db.js @@ -312,7 +312,7 @@ export default function initializeDatabase() { name TEXT NOT NULL, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, - role TEXT CHECK(role IN ('admin', 'project_manager', 'user', 'read_only')) DEFAULT 'user', + role TEXT CHECK(role IN ('admin', 'team_lead', 'project_manager', 'user', 'read_only')) DEFAULT 'user', created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, is_active INTEGER DEFAULT 1, diff --git a/src/lib/middleware/auth.js b/src/lib/middleware/auth.js index 1d01a78..5efd9a2 100644 --- a/src/lib/middleware/auth.js +++ b/src/lib/middleware/auth.js @@ -3,7 +3,8 @@ import { NextResponse } from "next/server" // Role hierarchy for permission checking const ROLE_HIERARCHY = { - 'admin': 4, + 'admin': 5, + 'team_lead': 4, 'project_manager': 3, 'user': 2, 'read_only': 1 diff --git a/src/lib/userManagement.js b/src/lib/userManagement.js index 5adff8a..8cf9d82 100644 --- a/src/lib/userManagement.js +++ b/src/lib/userManagement.js @@ -53,7 +53,7 @@ export function getAllUsers() { // Update user role export function updateUserRole(userId, role) { - const validRoles = ['admin', 'project_manager', 'user', 'read_only'] + const validRoles = ['admin', 'team_lead', 'project_manager', 'user', 'read_only'] if (!validRoles.includes(role)) { throw new Error("Invalid role") } @@ -159,7 +159,7 @@ export async function updateUser(userId, updates) { } if (updates.role !== undefined) { - const validRoles = ['admin', 'project_manager', 'user', 'read_only']; + const validRoles = ['admin', 'team_lead', 'project_manager', 'user', 'read_only']; if (!validRoles.includes(updates.role)) { throw new Error("Invalid role"); }