feat: add team_lead role to user management and update related functionalities
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
75
migrate-add-team-lead-role.mjs
Normal file
75
migrate-add-team-lead-role.mjs
Normal 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
30
run-migrations.sh
Normal 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"
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user