feat: add team_lead role to user management and update related functionalities

This commit is contained in:
2025-11-14 08:08:01 +01:00
parent eec0c0a281
commit 23b3c0e9e8
8 changed files with 123 additions and 5 deletions

View File

@@ -22,7 +22,10 @@ docker-compose up
docker-compose -f docker-compose.prod.yml up --build 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 ### 2. Deploy from Git Repository

View File

@@ -20,6 +20,10 @@ chmod -R 755 /app/public/uploads
echo "🔧 Setting up admin account..." echo "🔧 Setting up admin account..."
node scripts/create-admin.js node scripts/create-admin.js
# Run any pending database migrations
echo "🔄 Running database migrations..."
./run-migrations.sh
# Start the application # Start the application
echo "✅ Starting production server..." echo "✅ Starting production server..."
exec npm start exec npm start

View File

@@ -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);
}

30
run-migrations.sh Normal file
View File

@@ -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"

View File

@@ -125,6 +125,8 @@ export default function UserManagementPage() {
switch (role) { switch (role) {
case "admin": case "admin":
return "red"; return "red";
case "team_lead":
return "purple";
case "project_manager": case "project_manager":
return "blue"; return "blue";
case "user": case "user":
@@ -140,6 +142,8 @@ export default function UserManagementPage() {
switch (role) { switch (role) {
case "project_manager": case "project_manager":
return "Project Manager"; return "Project Manager";
case "team_lead":
return "Team Lead";
case "read_only": case "read_only":
return "Read Only"; return "Read Only";
default: default:
@@ -433,6 +437,7 @@ function CreateUserModal({ onClose, onUserCreated }) {
<option value="read_only">Read Only</option> <option value="read_only">Read Only</option>
<option value="user">User</option> <option value="user">User</option>
<option value="project_manager">Project Manager</option> <option value="project_manager">Project Manager</option>
<option value="team_lead">Team Lead</option>
<option value="admin">Admin</option> <option value="admin">Admin</option>
</select> </select>
</div> </div>

View File

@@ -312,7 +312,7 @@ export default function initializeDatabase() {
name TEXT NOT NULL, name TEXT NOT NULL,
username TEXT UNIQUE NOT NULL, username TEXT UNIQUE NOT NULL,
password_hash TEXT 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, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
is_active INTEGER DEFAULT 1, is_active INTEGER DEFAULT 1,

View File

@@ -3,7 +3,8 @@ import { NextResponse } from "next/server"
// Role hierarchy for permission checking // Role hierarchy for permission checking
const ROLE_HIERARCHY = { const ROLE_HIERARCHY = {
'admin': 4, 'admin': 5,
'team_lead': 4,
'project_manager': 3, 'project_manager': 3,
'user': 2, 'user': 2,
'read_only': 1 'read_only': 1

View File

@@ -53,7 +53,7 @@ export function getAllUsers() {
// Update user role // Update user role
export function updateUserRole(userId, 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)) { if (!validRoles.includes(role)) {
throw new Error("Invalid role") throw new Error("Invalid role")
} }
@@ -159,7 +159,7 @@ export async function updateUser(userId, updates) {
} }
if (updates.role !== undefined) { 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)) { if (!validRoles.includes(updates.role)) {
throw new Error("Invalid role"); throw new Error("Invalid role");
} }