mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
1075 lines
49 KiB
TypeScript
1075 lines
49 KiB
TypeScript
import { Pool } from 'pg';
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
ssl: false,
|
|
max: 20,
|
|
idleTimeoutMillis: 30000,
|
|
connectionTimeoutMillis: 2000,
|
|
});
|
|
|
|
export async function initDatabase() {
|
|
try {
|
|
console.log('🔧 Initializing production database...');
|
|
|
|
// Test connection
|
|
const client = await pool.connect();
|
|
await client.query('SELECT NOW()');
|
|
client.release();
|
|
|
|
console.log('✅ Database connection successful');
|
|
|
|
// Check if tables exist, if not create them (first time setup)
|
|
await createTablesIfNotExist();
|
|
|
|
// Run incremental migrations to update existing tables
|
|
await runMigrations();
|
|
|
|
// Create default admin user only if it doesn't exist
|
|
await createDefaultAdmin();
|
|
|
|
// Initialize roles and permissions for production
|
|
await initRolesAndPermissionsProduction();
|
|
|
|
console.log('✅ Database initialization complete');
|
|
|
|
} catch (error) {
|
|
console.error('❌ Database initialization failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function createTablesIfNotExist() {
|
|
const createUsersTable = `
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id VARCHAR(255) PRIMARY KEY,
|
|
username VARCHAR(255) UNIQUE NOT NULL,
|
|
email VARCHAR(255) UNIQUE,
|
|
name VARCHAR(255),
|
|
first_name VARCHAR(255),
|
|
last_name VARCHAR(255),
|
|
profile_image_url TEXT,
|
|
password VARCHAR(255),
|
|
role VARCHAR(50) DEFAULT 'employee',
|
|
password_changed BOOLEAN DEFAULT false,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createGroupsTable = `
|
|
CREATE TABLE IF NOT EXISTS groups (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
color VARCHAR(20) DEFAULT '#1976D2',
|
|
nocodb_config_id INTEGER,
|
|
nocodb_table_id VARCHAR(255),
|
|
nocodb_table_name VARCHAR(255),
|
|
invoice_column_name VARCHAR(255),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createSuppliersTable = `
|
|
CREATE TABLE IF NOT EXISTS suppliers (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
contact VARCHAR(255),
|
|
phone VARCHAR(255),
|
|
has_dlc BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createOrdersTable = `
|
|
CREATE TABLE IF NOT EXISTS orders (
|
|
id SERIAL PRIMARY KEY,
|
|
supplier_id INTEGER REFERENCES suppliers(id) ON DELETE CASCADE,
|
|
group_id INTEGER REFERENCES groups(id) ON DELETE CASCADE,
|
|
planned_date DATE NOT NULL,
|
|
quantity INTEGER,
|
|
unit VARCHAR(50),
|
|
status VARCHAR(50) DEFAULT 'pending' CHECK (status IN ('pending', 'planned', 'delivered')),
|
|
notes TEXT,
|
|
created_by VARCHAR(255) REFERENCES users(id),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createDeliveriesTable = `
|
|
CREATE TABLE IF NOT EXISTS deliveries (
|
|
id SERIAL PRIMARY KEY,
|
|
order_id INTEGER REFERENCES orders(id) ON DELETE SET NULL,
|
|
supplier_id INTEGER REFERENCES suppliers(id) ON DELETE CASCADE,
|
|
group_id INTEGER REFERENCES groups(id) ON DELETE CASCADE,
|
|
scheduled_date DATE NOT NULL,
|
|
delivered_date TIMESTAMP,
|
|
quantity INTEGER NOT NULL,
|
|
unit VARCHAR(50) NOT NULL,
|
|
status VARCHAR(50) DEFAULT 'pending' CHECK (status IN ('pending', 'delivered')),
|
|
notes TEXT,
|
|
bl_number VARCHAR(255),
|
|
bl_amount DECIMAL(10,2),
|
|
invoice_reference VARCHAR(255),
|
|
invoice_amount DECIMAL(10,2),
|
|
reconciled BOOLEAN DEFAULT false,
|
|
validated_at TIMESTAMP,
|
|
created_by VARCHAR(255) REFERENCES users(id),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createUserGroupsTable = `
|
|
CREATE TABLE IF NOT EXISTS user_groups (
|
|
user_id VARCHAR(255) REFERENCES users(id) ON DELETE CASCADE,
|
|
group_id INTEGER REFERENCES groups(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (user_id, group_id)
|
|
);
|
|
`;
|
|
|
|
const createSessionTable = `
|
|
CREATE TABLE IF NOT EXISTS session (
|
|
sid VARCHAR NOT NULL COLLATE "default" PRIMARY KEY,
|
|
sess JSON NOT NULL,
|
|
expire TIMESTAMP(6) NOT NULL
|
|
);
|
|
`;
|
|
|
|
const createPublicitiesTable = `
|
|
CREATE TABLE IF NOT EXISTS publicities (
|
|
id SERIAL PRIMARY KEY,
|
|
pub_number VARCHAR(255) NOT NULL,
|
|
designation TEXT NOT NULL,
|
|
start_date DATE NOT NULL,
|
|
end_date DATE NOT NULL,
|
|
year INTEGER NOT NULL,
|
|
created_by VARCHAR(255) REFERENCES users(id),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createPublicityParticipationsTable = `
|
|
CREATE TABLE IF NOT EXISTS publicity_participations (
|
|
publicity_id INTEGER REFERENCES publicities(id) ON DELETE CASCADE,
|
|
group_id INTEGER REFERENCES groups(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (publicity_id, group_id)
|
|
);
|
|
`;
|
|
|
|
const createRolesTable = `
|
|
CREATE TABLE IF NOT EXISTS roles (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) UNIQUE NOT NULL,
|
|
display_name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
color VARCHAR(7) DEFAULT '#6b7280',
|
|
is_system BOOLEAN DEFAULT false,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createPermissionsTable = `
|
|
CREATE TABLE IF NOT EXISTS permissions (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) UNIQUE NOT NULL,
|
|
display_name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
category VARCHAR(255) NOT NULL,
|
|
action VARCHAR(255) NOT NULL,
|
|
resource VARCHAR(255) NOT NULL,
|
|
is_system BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createRolePermissionsTable = `
|
|
CREATE TABLE IF NOT EXISTS role_permissions (
|
|
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
|
|
permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (role_id, permission_id)
|
|
);
|
|
`;
|
|
|
|
const createNocodbConfigTable = `
|
|
CREATE TABLE IF NOT EXISTS nocodb_config (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
base_url VARCHAR(255) NOT NULL,
|
|
project_id VARCHAR(255) NOT NULL,
|
|
api_token VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_by VARCHAR(255) REFERENCES users(id),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createCustomerOrdersTable = `
|
|
CREATE TABLE IF NOT EXISTS customer_orders (
|
|
id SERIAL PRIMARY KEY,
|
|
order_taker VARCHAR(255) NOT NULL,
|
|
customer_name VARCHAR(255) NOT NULL,
|
|
customer_phone VARCHAR(255),
|
|
customer_email VARCHAR(255),
|
|
product_designation TEXT NOT NULL,
|
|
product_reference VARCHAR(255),
|
|
gencode VARCHAR(255),
|
|
quantity INTEGER DEFAULT 1,
|
|
supplier_id INTEGER REFERENCES suppliers(id) ON DELETE SET NULL,
|
|
status VARCHAR(100) DEFAULT 'En attente de Commande',
|
|
deposit DECIMAL(10,2) DEFAULT 0.00,
|
|
is_promotional_price BOOLEAN DEFAULT false,
|
|
customer_notified BOOLEAN DEFAULT false,
|
|
notes TEXT,
|
|
group_id INTEGER REFERENCES groups(id) ON DELETE CASCADE,
|
|
created_by VARCHAR(255) REFERENCES users(id),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const createDlcProductsTable = `
|
|
CREATE TABLE IF NOT EXISTS dlc_products (
|
|
id SERIAL PRIMARY KEY,
|
|
product_name VARCHAR(255) NOT NULL,
|
|
expiry_date DATE NOT NULL,
|
|
date_type VARCHAR(50) NOT NULL DEFAULT 'DLC',
|
|
quantity INTEGER NOT NULL DEFAULT 1,
|
|
unit VARCHAR(50) NOT NULL DEFAULT 'unité',
|
|
supplier_id INTEGER NOT NULL REFERENCES suppliers(id) ON DELETE CASCADE,
|
|
location VARCHAR(255) NOT NULL DEFAULT 'Magasin',
|
|
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
notes TEXT,
|
|
alert_threshold INTEGER NOT NULL DEFAULT 15,
|
|
validated_at TIMESTAMP,
|
|
validated_by VARCHAR(255) REFERENCES users(id),
|
|
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
|
created_by VARCHAR(255) NOT NULL REFERENCES users(id),
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
gencode VARCHAR(255)
|
|
);
|
|
`;
|
|
|
|
const createTasksTable = `
|
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
id SERIAL PRIMARY KEY,
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed', 'cancelled')),
|
|
priority VARCHAR(50) NOT NULL DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
|
|
assigned_to VARCHAR(255),
|
|
due_date DATE,
|
|
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
|
created_by VARCHAR(255) NOT NULL REFERENCES users(id),
|
|
completed_at TIMESTAMP,
|
|
completed_by VARCHAR(255) REFERENCES users(id),
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`;
|
|
|
|
const tables = [
|
|
createUsersTable,
|
|
createGroupsTable,
|
|
createSuppliersTable,
|
|
createOrdersTable,
|
|
createDeliveriesTable,
|
|
createUserGroupsTable,
|
|
createSessionTable,
|
|
createPublicitiesTable,
|
|
createPublicityParticipationsTable,
|
|
createRolesTable,
|
|
createPermissionsTable,
|
|
createRolePermissionsTable,
|
|
createNocodbConfigTable,
|
|
createCustomerOrdersTable,
|
|
createDlcProductsTable,
|
|
createTasksTable
|
|
];
|
|
|
|
for (const table of tables) {
|
|
await pool.query(table);
|
|
}
|
|
|
|
console.log('✅ All tables verified/created successfully');
|
|
}
|
|
|
|
async function runMigrations() {
|
|
try {
|
|
console.log('🔄 Running database migrations...');
|
|
|
|
// Migration 1: Add missing columns to existing tables
|
|
await addMissingColumns();
|
|
|
|
// Migration 2: Update constraints
|
|
await updateConstraints();
|
|
|
|
// Migration 3: Create new tables for roles/permissions if they don't exist
|
|
await createRolesTables();
|
|
|
|
console.log('✅ All migrations completed successfully');
|
|
} catch (error) {
|
|
console.error('❌ Migration failed:', error);
|
|
// Don't throw error, continue with existing tables
|
|
}
|
|
}
|
|
|
|
async function addMissingColumns() {
|
|
try {
|
|
// Check and add delivered_date column to deliveries
|
|
const deliveredDateExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'deliveries' AND column_name = 'delivered_date'
|
|
`);
|
|
|
|
if (deliveredDateExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE deliveries ADD COLUMN delivered_date TIMESTAMP');
|
|
console.log('✅ Added delivered_date column to deliveries');
|
|
}
|
|
|
|
// Check and add validated_at column to deliveries
|
|
const validatedAtExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'deliveries' AND column_name = 'validated_at'
|
|
`);
|
|
|
|
if (validatedAtExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE deliveries ADD COLUMN validated_at TIMESTAMP');
|
|
console.log('✅ Added validated_at column to deliveries');
|
|
}
|
|
|
|
// CRITICAL FIX: Corriger la contrainte deliveries_status_check pour permettre 'planned'
|
|
console.log('🔧 CRITICAL FIX: Correcting deliveries status constraint...');
|
|
try {
|
|
await pool.query('ALTER TABLE deliveries DROP CONSTRAINT IF EXISTS deliveries_status_check');
|
|
await pool.query("ALTER TABLE deliveries ADD CONSTRAINT deliveries_status_check CHECK (status IN ('pending', 'planned', 'delivered', 'cancelled'))");
|
|
console.log('✅ CRITICAL FIX: Deliveries status constraint corrected to allow planned status');
|
|
} catch (error) {
|
|
console.error('❌ Failed to fix deliveries constraint:', error);
|
|
}
|
|
|
|
// Check and add MISSING columns to users table
|
|
const columnsToAdd = [
|
|
{ name: 'name', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'first_name', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'last_name', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'profile_image_url', type: 'TEXT', default: null }
|
|
];
|
|
|
|
for (const column of columnsToAdd) {
|
|
const columnExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'users' AND column_name = $1
|
|
`, [column.name]);
|
|
|
|
if (columnExists.rows.length === 0) {
|
|
await pool.query(`ALTER TABLE users ADD COLUMN ${column.name} ${column.type}`);
|
|
console.log(`✅ Added ${column.name} column to users table`);
|
|
}
|
|
}
|
|
|
|
// Populate name column if empty
|
|
await pool.query(`
|
|
UPDATE users SET name = COALESCE(
|
|
CASE
|
|
WHEN first_name IS NOT NULL AND last_name IS NOT NULL THEN first_name || ' ' || last_name
|
|
WHEN first_name IS NOT NULL THEN first_name
|
|
WHEN last_name IS NOT NULL THEN last_name
|
|
ELSE username
|
|
END,
|
|
username,
|
|
email
|
|
)
|
|
WHERE name IS NULL OR name = ''
|
|
`);
|
|
console.log('✅ Updated name column with existing data');
|
|
|
|
// Check and add customer_email column to customer_orders
|
|
const customerEmailExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'customer_orders' AND column_name = 'customer_email'
|
|
`);
|
|
|
|
if (customerEmailExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE customer_orders ADD COLUMN customer_email VARCHAR(255)');
|
|
console.log('✅ Added customer_email column to customer_orders');
|
|
}
|
|
|
|
// Check and add notes column to customer_orders
|
|
const notesExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'customer_orders' AND column_name = 'notes'
|
|
`);
|
|
|
|
if (notesExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE customer_orders ADD COLUMN notes TEXT');
|
|
console.log('✅ Added notes column to customer_orders');
|
|
}
|
|
|
|
// Check and add quantity column to customer_orders
|
|
const quantityExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'customer_orders' AND column_name = 'quantity'
|
|
`);
|
|
|
|
if (quantityExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE customer_orders ADD COLUMN quantity INTEGER DEFAULT 1');
|
|
console.log('✅ Added quantity column to customer_orders');
|
|
}
|
|
|
|
// Check and add quantity column to orders
|
|
const ordersQuantityExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'orders' AND column_name = 'quantity'
|
|
`);
|
|
|
|
if (ordersQuantityExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE orders ADD COLUMN quantity INTEGER');
|
|
console.log('✅ Added quantity column to orders');
|
|
}
|
|
|
|
// Check and add unit column to orders
|
|
const ordersUnitExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'orders' AND column_name = 'unit'
|
|
`);
|
|
|
|
if (ordersUnitExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE orders ADD COLUMN unit VARCHAR(50)');
|
|
console.log('✅ Added unit column to orders');
|
|
}
|
|
|
|
// Check and add completed_by column to tasks
|
|
const tasksCompletedByExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'tasks' AND column_name = 'completed_by'
|
|
`);
|
|
|
|
if (tasksCompletedByExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE tasks ADD COLUMN completed_by VARCHAR(255) REFERENCES users(id)');
|
|
console.log('✅ Added completed_by column to tasks');
|
|
}
|
|
|
|
// FORCE FIX PRODUCTION ISSUE: Recreate tasks table completed columns (Critical Fix v2)
|
|
console.log('🔧 CRITICAL FIX v2: Forcing tasks table completed columns for production...');
|
|
|
|
try {
|
|
// First verify current structure
|
|
const currentCols = await pool.query(`
|
|
SELECT column_name FROM information_schema.columns
|
|
WHERE table_name = 'tasks' AND column_name IN ('completed_at', 'completed_by')
|
|
`);
|
|
console.log('📋 Current completed columns:', currentCols.rows);
|
|
|
|
// Drop existing constraints first
|
|
await pool.query(`ALTER TABLE tasks DROP CONSTRAINT IF EXISTS tasks_completed_by_fkey CASCADE;`);
|
|
console.log('✅ Dropped constraints');
|
|
|
|
// Force drop and recreate columns
|
|
await pool.query(`ALTER TABLE tasks DROP COLUMN IF EXISTS completed_at CASCADE;`);
|
|
await pool.query(`ALTER TABLE tasks DROP COLUMN IF EXISTS completed_by CASCADE;`);
|
|
console.log('✅ Dropped old columns');
|
|
|
|
await pool.query(`ALTER TABLE tasks ADD COLUMN completed_at TIMESTAMP NULL;`);
|
|
await pool.query(`ALTER TABLE tasks ADD COLUMN completed_by VARCHAR(255) NULL;`);
|
|
console.log('✅ Added new columns');
|
|
|
|
// Re-add foreign key constraint
|
|
await pool.query(`
|
|
ALTER TABLE tasks ADD CONSTRAINT tasks_completed_by_fkey
|
|
FOREIGN KEY (completed_by) REFERENCES users(id) ON DELETE SET NULL;
|
|
`);
|
|
console.log('✅ Added constraints');
|
|
|
|
// Final verification
|
|
const verifyResult = await pool.query(`
|
|
SELECT column_name, data_type, is_nullable
|
|
FROM information_schema.columns
|
|
WHERE table_name = 'tasks' AND column_name IN ('completed_at', 'completed_by')
|
|
ORDER BY column_name
|
|
`);
|
|
|
|
console.log('🎉 CRITICAL FIX v2 COMPLETED. Final structure:', verifyResult.rows);
|
|
|
|
} catch (taskError) {
|
|
console.error('❌ Critical fix failed:', taskError);
|
|
throw taskError; // Force error to prevent app from starting with broken schema
|
|
}
|
|
|
|
// Add missing columns to roles table
|
|
const rolesColumnsToAdd = [
|
|
{ name: 'display_name', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'color', type: 'VARCHAR(7)', default: "'#6b7280'" },
|
|
{ name: 'is_active', type: 'BOOLEAN', default: 'true' }
|
|
];
|
|
|
|
for (const column of rolesColumnsToAdd) {
|
|
const columnExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'roles' AND column_name = $1
|
|
`, [column.name]);
|
|
|
|
if (columnExists.rows.length === 0) {
|
|
const defaultClause = column.default ? ` DEFAULT ${column.default}` : '';
|
|
await pool.query(`ALTER TABLE roles ADD COLUMN ${column.name} ${column.type}${defaultClause}`);
|
|
console.log(`✅ Added ${column.name} column to roles table`);
|
|
|
|
// Migrate display_name from name if needed
|
|
if (column.name === 'display_name') {
|
|
await pool.query(`UPDATE roles SET display_name = name WHERE display_name IS NULL`);
|
|
console.log('✅ Migrated display_name data from name column');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add missing columns to permissions table
|
|
const permissionsColumnsToAdd = [
|
|
{ name: 'display_name', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'action', type: 'VARCHAR(255)', default: "'read'" },
|
|
{ name: 'resource', type: 'VARCHAR(255)', default: "'system'" },
|
|
{ name: 'is_system', type: 'BOOLEAN', default: 'true' }
|
|
];
|
|
|
|
for (const column of permissionsColumnsToAdd) {
|
|
const columnExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'permissions' AND column_name = $1
|
|
`, [column.name]);
|
|
|
|
if (columnExists.rows.length === 0) {
|
|
const defaultClause = column.default ? ` DEFAULT ${column.default}` : '';
|
|
await pool.query(`ALTER TABLE permissions ADD COLUMN ${column.name} ${column.type}${defaultClause}`);
|
|
console.log(`✅ Added ${column.name} column to permissions table`);
|
|
|
|
// Migrate display_name from name if needed
|
|
if (column.name === 'display_name') {
|
|
await pool.query(`UPDATE permissions SET display_name = name WHERE display_name IS NULL`);
|
|
console.log('✅ Migrated permissions display_name data');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add description column to nocodb_config table
|
|
const descriptionExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'nocodb_config' AND column_name = 'description'
|
|
`);
|
|
|
|
if (descriptionExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE nocodb_config ADD COLUMN description TEXT');
|
|
console.log('✅ Added description column to nocodb_config');
|
|
}
|
|
|
|
// Add has_dlc column to suppliers table
|
|
const supplierHasDlcExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'suppliers' AND column_name = 'has_dlc'
|
|
`);
|
|
|
|
if (supplierHasDlcExists.rows.length === 0) {
|
|
await pool.query('ALTER TABLE suppliers ADD COLUMN has_dlc BOOLEAN DEFAULT FALSE');
|
|
console.log('✅ Added has_dlc column to suppliers table');
|
|
}
|
|
|
|
// Add missing columns to dlc_products table for compatibility
|
|
const dlcColumnsToAdd = [
|
|
{ name: 'name', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'dlc_date', type: 'DATE', default: null },
|
|
{ name: 'product_code', type: 'VARCHAR(255)', default: null },
|
|
{ name: 'description', type: 'TEXT', default: null }
|
|
];
|
|
|
|
for (const column of dlcColumnsToAdd) {
|
|
const columnExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'dlc_products' AND column_name = $1
|
|
`, [column.name]);
|
|
|
|
if (columnExists.rows.length === 0) {
|
|
const defaultClause = column.default ? ` DEFAULT ${column.default}` : '';
|
|
await pool.query(`ALTER TABLE dlc_products ADD COLUMN ${column.name} ${column.type}${defaultClause}`);
|
|
console.log(`✅ Added ${column.name} column to dlc_products table`);
|
|
}
|
|
}
|
|
|
|
// Migrate existing data if needed
|
|
const needsMigration = await pool.query(`
|
|
SELECT COUNT(*) as count FROM dlc_products
|
|
WHERE name IS NULL AND product_name IS NOT NULL
|
|
`);
|
|
|
|
if (parseInt(needsMigration.rows[0].count) > 0) {
|
|
await pool.query(`
|
|
UPDATE dlc_products SET
|
|
name = product_name,
|
|
dlc_date = expiry_date
|
|
WHERE name IS NULL OR dlc_date IS NULL
|
|
`);
|
|
console.log('✅ Migrated DLC product data to new columns');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error adding missing columns:', error);
|
|
}
|
|
}
|
|
|
|
async function updateConstraints() {
|
|
try {
|
|
// Check if orders_status_check constraint exists and needs updating
|
|
const constraintExists = await pool.query(`
|
|
SELECT 1 FROM information_schema.check_constraints
|
|
WHERE constraint_name = 'orders_status_check'
|
|
`);
|
|
|
|
if (constraintExists.rows.length > 0) {
|
|
// Drop old constraint
|
|
await pool.query('ALTER TABLE orders DROP CONSTRAINT orders_status_check');
|
|
console.log('✅ Removed old orders_status_check constraint');
|
|
}
|
|
|
|
// Add updated constraint
|
|
await pool.query(`
|
|
ALTER TABLE orders ADD CONSTRAINT orders_status_check
|
|
CHECK (status IN ('pending', 'planned', 'delivered'))
|
|
`);
|
|
console.log('✅ Added updated orders_status_check constraint');
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error updating constraints:', error);
|
|
}
|
|
}
|
|
|
|
async function createRolesTables() {
|
|
try {
|
|
// Create roles table avec colonnes manquantes
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS roles (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) UNIQUE NOT NULL,
|
|
display_name VARCHAR(255),
|
|
description TEXT,
|
|
color VARCHAR(20) DEFAULT '#6b7280',
|
|
is_system BOOLEAN DEFAULT false,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`);
|
|
|
|
// Create permissions table avec colonnes manquantes
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS permissions (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) UNIQUE NOT NULL,
|
|
display_name VARCHAR(255),
|
|
description TEXT,
|
|
category VARCHAR(255),
|
|
action VARCHAR(255),
|
|
resource VARCHAR(255),
|
|
is_system BOOLEAN DEFAULT false,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`);
|
|
|
|
// Create role_permissions table
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS role_permissions (
|
|
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
|
|
permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (role_id, permission_id)
|
|
)
|
|
`);
|
|
|
|
// Create USER_ROLES table (CORRECTION CRITIQUE - cette table manquait !)
|
|
await pool.query(`
|
|
CREATE TABLE IF NOT EXISTS user_roles (
|
|
user_id VARCHAR NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE,
|
|
assigned_by VARCHAR NOT NULL REFERENCES users(id),
|
|
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (user_id, role_id)
|
|
)
|
|
`);
|
|
|
|
// Create indexes for performance sur user_roles
|
|
await pool.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_role_id ON user_roles (role_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_assigned_by ON user_roles (assigned_by);
|
|
`);
|
|
|
|
// Ajouter les colonnes manquantes aux tables existantes si elles n'existent pas
|
|
const addColumnsQueries = [
|
|
`ALTER TABLE roles ADD COLUMN IF NOT EXISTS display_name VARCHAR(255);`,
|
|
`ALTER TABLE roles ADD COLUMN IF NOT EXISTS color VARCHAR(20) DEFAULT '#6b7280';`,
|
|
`ALTER TABLE roles ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT true;`,
|
|
`ALTER TABLE permissions ADD COLUMN IF NOT EXISTS display_name VARCHAR(255);`,
|
|
`ALTER TABLE permissions ADD COLUMN IF NOT EXISTS action VARCHAR(255);`,
|
|
`ALTER TABLE permissions ADD COLUMN IF NOT EXISTS resource VARCHAR(255);`,
|
|
`ALTER TABLE permissions ADD COLUMN IF NOT EXISTS is_system BOOLEAN DEFAULT false;`
|
|
];
|
|
|
|
for (const query of addColumnsQueries) {
|
|
try {
|
|
await pool.query(query);
|
|
} catch (err) {
|
|
// Ignorer erreurs si colonnes existent déjà
|
|
console.log('Column already exists or minor error:', err.message.substring(0, 50));
|
|
}
|
|
}
|
|
|
|
// Vérifier et assigner rôle admin à admin_local si nécessaire
|
|
const adminRoleCheck = await pool.query(`
|
|
SELECT COUNT(*) as count FROM user_roles
|
|
WHERE user_id = 'admin_local'
|
|
`);
|
|
|
|
if (adminRoleCheck.rows[0].count === '0') {
|
|
// CORRECTION CRITIQUE: utiliser 'admin_local' au lieu de 'system' pour assigned_by
|
|
await pool.query(`
|
|
INSERT INTO user_roles (user_id, role_id, assigned_by, assigned_at)
|
|
SELECT 'admin_local', r.id, 'admin_local', CURRENT_TIMESTAMP
|
|
FROM roles r
|
|
WHERE r.name = 'admin'
|
|
AND EXISTS (SELECT 1 FROM users WHERE id = 'admin_local')
|
|
`);
|
|
console.log('✅ Admin role assigned to admin_local user (self-assigned)');
|
|
}
|
|
|
|
console.log('✅ ALL tables created including CRITICAL user_roles table');
|
|
} catch (error) {
|
|
console.error('❌ Error creating roles tables:', error);
|
|
// Continue anyway, don't crash the app
|
|
}
|
|
}
|
|
|
|
async function createDefaultAdmin() {
|
|
try {
|
|
// Check if admin user exists
|
|
const existingAdmin = await pool.query(
|
|
'SELECT id, password_changed FROM users WHERE username = $1',
|
|
['admin']
|
|
);
|
|
|
|
if (existingAdmin.rows.length === 0) {
|
|
// Import hash function without bcrypt
|
|
const { hashPassword } = await import('./auth-utils.production');
|
|
const hashedPassword = await hashPassword('admin');
|
|
|
|
await pool.query(`
|
|
INSERT INTO users (id, username, email, name, first_name, last_name, password, role, password_changed)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
`, [
|
|
'admin_local',
|
|
'admin',
|
|
'admin@logiflow.com',
|
|
'Administrateur',
|
|
'Admin',
|
|
'LogiFlow',
|
|
hashedPassword,
|
|
'admin',
|
|
false
|
|
]);
|
|
|
|
console.log('✅ Default admin user created: admin/admin');
|
|
} else {
|
|
// Only reset password if it hasn't been changed by the user
|
|
const admin = existingAdmin.rows[0];
|
|
if (!admin.password_changed) {
|
|
const { hashPassword } = await import('./auth-utils.production');
|
|
const newHashedPassword = await hashPassword('admin');
|
|
|
|
await pool.query(
|
|
'UPDATE users SET password = $1 WHERE username = $2',
|
|
[newHashedPassword, 'admin']
|
|
);
|
|
|
|
console.log('✅ Admin user password updated with default password (admin/admin)');
|
|
} else {
|
|
console.log('✅ Admin user exists with custom password - not resetting');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ Failed to create admin user:', error);
|
|
}
|
|
}
|
|
|
|
async function initRolesAndPermissionsProduction() {
|
|
try {
|
|
console.log('🎭 Initializing roles and permissions for production...');
|
|
|
|
// Check if roles already exist (avoid re-creating)
|
|
const existingRoles = await pool.query('SELECT COUNT(*) as count FROM roles');
|
|
if (existingRoles.rows[0].count > 0) {
|
|
console.log('✅ Roles already exist, checking for missing DLC permissions...');
|
|
await ensureDlcPermissionsExist();
|
|
return;
|
|
}
|
|
|
|
// Create 4 default roles with proper French names
|
|
const roles = [
|
|
{ name: 'admin', displayName: 'Administrateur', description: 'Accès complet à toutes les fonctionnalités', color: '#dc2626' },
|
|
{ name: 'manager', displayName: 'Manager', description: 'Gestion des commandes, livraisons et fournisseurs', color: '#2563eb' },
|
|
{ name: 'employee', displayName: 'Employé', description: 'Accès en lecture aux données et publicités', color: '#16a34a' },
|
|
{ name: 'directeur', displayName: 'Directeur', description: 'Supervision générale et gestion stratégique', color: '#7c3aed' }
|
|
];
|
|
|
|
const createdRoles = {};
|
|
for (const role of roles) {
|
|
const result = await pool.query(`
|
|
INSERT INTO roles (name, display_name, description, color, is_system, is_active)
|
|
VALUES ($1, $2, $3, $4, true, true)
|
|
RETURNING id, name
|
|
`, [role.name, role.displayName, role.description, role.color]);
|
|
createdRoles[role.name] = result.rows[0].id;
|
|
console.log(`✅ Created role: ${role.displayName}`);
|
|
}
|
|
|
|
// Create 49 permissions with French categories
|
|
const permissions = [
|
|
// Dashboard (tableau_de_bord)
|
|
{ category: 'tableau_de_bord', name: 'dashboard_read', displayName: 'Voir tableau de bord', description: 'Accès en lecture au tableau de bord', action: 'read', resource: 'dashboard' },
|
|
|
|
// Stores (magasins)
|
|
{ category: 'magasins', name: 'groups_read', displayName: 'Voir magasins', description: 'Accès en lecture aux magasins', action: 'read', resource: 'groups' },
|
|
{ category: 'magasins', name: 'groups_create', displayName: 'Créer magasins', description: 'Création de nouveaux magasins', action: 'create', resource: 'groups' },
|
|
{ category: 'magasins', name: 'groups_update', displayName: 'Modifier magasins', description: 'Modification des magasins existants', action: 'update', resource: 'groups' },
|
|
{ category: 'magasins', name: 'groups_delete', displayName: 'Supprimer magasins', description: 'Suppression de magasins', action: 'delete', resource: 'groups' },
|
|
|
|
// Suppliers (fournisseurs)
|
|
{ category: 'fournisseurs', name: 'suppliers_read', displayName: 'Voir fournisseurs', description: 'Accès en lecture aux fournisseurs', action: 'read', resource: 'suppliers' },
|
|
{ category: 'fournisseurs', name: 'suppliers_create', displayName: 'Créer fournisseurs', description: 'Création de nouveaux fournisseurs', action: 'create', resource: 'suppliers' },
|
|
{ category: 'fournisseurs', name: 'suppliers_update', displayName: 'Modifier fournisseurs', description: 'Modification des fournisseurs', action: 'update', resource: 'suppliers' },
|
|
{ category: 'fournisseurs', name: 'suppliers_delete', displayName: 'Supprimer fournisseurs', description: 'Suppression de fournisseurs', action: 'delete', resource: 'suppliers' },
|
|
|
|
// Orders (commandes)
|
|
{ category: 'commandes', name: 'orders_read', displayName: 'Voir commandes', description: 'Accès en lecture aux commandes', action: 'read', resource: 'orders' },
|
|
{ category: 'commandes', name: 'orders_create', displayName: 'Créer commandes', description: 'Création de nouvelles commandes', action: 'create', resource: 'orders' },
|
|
{ category: 'commandes', name: 'orders_update', displayName: 'Modifier commandes', description: 'Modification des commandes', action: 'update', resource: 'orders' },
|
|
{ category: 'commandes', name: 'orders_delete', displayName: 'Supprimer commandes', description: 'Suppression de commandes', action: 'delete', resource: 'orders' },
|
|
|
|
// Deliveries (livraisons)
|
|
{ category: 'livraisons', name: 'deliveries_read', displayName: 'Voir livraisons', description: 'Accès en lecture aux livraisons', action: 'read', resource: 'deliveries' },
|
|
{ category: 'livraisons', name: 'deliveries_create', displayName: 'Créer livraisons', description: 'Création de nouvelles livraisons', action: 'create', resource: 'deliveries' },
|
|
{ category: 'livraisons', name: 'deliveries_update', displayName: 'Modifier livraisons', description: 'Modification des livraisons', action: 'update', resource: 'deliveries' },
|
|
{ category: 'livraisons', name: 'deliveries_delete', displayName: 'Supprimer livraisons', description: 'Suppression de livraisons', action: 'delete', resource: 'deliveries' },
|
|
|
|
// Publicities (publicites)
|
|
{ category: 'publicites', name: 'publicities_read', displayName: 'Voir publicités', description: 'Accès en lecture aux publicités', action: 'read', resource: 'publicities' },
|
|
{ category: 'publicites', name: 'publicities_create', displayName: 'Créer publicités', description: 'Création de nouvelles publicités', action: 'create', resource: 'publicities' },
|
|
{ category: 'publicites', name: 'publicities_update', displayName: 'Modifier publicités', description: 'Modification des publicités', action: 'update', resource: 'publicities' },
|
|
{ category: 'publicites', name: 'publicities_delete', displayName: 'Supprimer publicités', description: 'Suppression de publicités', action: 'delete', resource: 'publicities' },
|
|
{ category: 'publicites', name: 'publicities_participate', displayName: 'Participer aux publicités', description: 'Participation des magasins aux publicités', action: 'participate', resource: 'publicities' },
|
|
|
|
// Customer Orders (commandes_clients)
|
|
{ category: 'commandes_clients', name: 'customer_orders_read', displayName: 'Voir commandes clients', description: 'Accès en lecture aux commandes clients', action: 'read', resource: 'customer_orders' },
|
|
{ category: 'commandes_clients', name: 'customer_orders_create', displayName: 'Créer commandes clients', description: 'Création de nouvelles commandes clients', action: 'create', resource: 'customer_orders' },
|
|
{ category: 'commandes_clients', name: 'customer_orders_update', displayName: 'Modifier commandes clients', description: 'Modification des commandes clients', action: 'update', resource: 'customer_orders' },
|
|
{ category: 'commandes_clients', name: 'customer_orders_delete', displayName: 'Supprimer commandes clients', description: 'Suppression de commandes clients', action: 'delete', resource: 'customer_orders' },
|
|
{ category: 'commandes_clients', name: 'customer_orders_print', displayName: 'Imprimer commandes clients', description: 'Impression des barcodes et documents', action: 'print', resource: 'customer_orders' },
|
|
|
|
// Users (utilisateurs)
|
|
{ category: 'utilisateurs', name: 'users_read', displayName: 'Voir utilisateurs', description: 'Accès en lecture aux utilisateurs', action: 'read', resource: 'users' },
|
|
{ category: 'utilisateurs', name: 'users_create', displayName: 'Créer utilisateurs', description: 'Création de nouveaux utilisateurs', action: 'create', resource: 'users' },
|
|
{ category: 'utilisateurs', name: 'users_update', displayName: 'Modifier utilisateurs', description: 'Modification des utilisateurs', action: 'update', resource: 'users' },
|
|
{ category: 'utilisateurs', name: 'users_delete', displayName: 'Supprimer utilisateurs', description: 'Suppression d\'utilisateurs', action: 'delete', resource: 'users' },
|
|
|
|
// Role Management (gestion_roles)
|
|
{ category: 'gestion_roles', name: 'roles_read', displayName: 'Voir rôles', description: 'Accès en lecture aux rôles', action: 'read', resource: 'roles' },
|
|
{ category: 'gestion_roles', name: 'roles_create', displayName: 'Créer rôles', description: 'Création de nouveaux rôles', action: 'create', resource: 'roles' },
|
|
{ category: 'gestion_roles', name: 'roles_update', displayName: 'Modifier rôles', description: 'Modification des rôles', action: 'update', resource: 'roles' },
|
|
{ category: 'gestion_roles', name: 'roles_delete', displayName: 'Supprimer rôles', description: 'Suppression de rôles', action: 'delete', resource: 'roles' },
|
|
{ category: 'gestion_roles', name: 'permissions_read', displayName: 'Voir permissions', description: 'Accès en lecture aux permissions', action: 'read', resource: 'permissions' },
|
|
{ category: 'gestion_roles', name: 'permissions_assign', displayName: 'Assigner permissions', description: 'Attribution de permissions aux rôles', action: 'assign', resource: 'permissions' },
|
|
|
|
// Reconciliation (rapprochement)
|
|
{ category: 'rapprochement', name: 'bl_reconciliation_read', displayName: 'Voir rapprochement BL', description: 'Accès au rapprochement des bons de livraison', action: 'read', resource: 'bl_reconciliation' },
|
|
{ category: 'rapprochement', name: 'bl_reconciliation_update', displayName: 'Modifier rapprochement BL', description: 'Modification du rapprochement des BL', action: 'update', resource: 'bl_reconciliation' },
|
|
|
|
// Administration
|
|
{ category: 'administration', name: 'nocodb_config_read', displayName: 'Voir config NocoDB', description: 'Accès à la configuration NocoDB', action: 'read', resource: 'nocodb_config' },
|
|
{ category: 'administration', name: 'nocodb_config_update', displayName: 'Modifier config NocoDB', description: 'Modification de la configuration NocoDB', action: 'update', resource: 'nocodb_config' },
|
|
{ category: 'administration', name: 'system_admin', displayName: 'Administration système', description: 'Accès complet à l\'administration', action: 'admin', resource: 'system' },
|
|
|
|
// Calendar (calendrier)
|
|
{ category: 'calendrier', name: 'calendar_read', displayName: 'Voir calendrier', description: 'Accès en lecture au calendrier', action: 'read', resource: 'calendar' },
|
|
{ category: 'calendrier', name: 'calendar_update', displayName: 'Modifier calendrier', description: 'Modification des événements du calendrier', action: 'update', resource: 'calendar' },
|
|
|
|
// DLC Management (gestion_dlc)
|
|
{ category: 'gestion_dlc', name: 'dlc_read', displayName: 'Voir produits DLC', description: 'Accès en lecture aux produits DLC', action: 'read', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_create', displayName: 'Créer produits DLC', description: 'Création de nouveaux produits DLC', action: 'create', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_update', displayName: 'Modifier produits DLC', description: 'Modification des produits DLC', action: 'update', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_delete', displayName: 'Supprimer produits DLC', description: 'Suppression de produits DLC', action: 'delete', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_validate', displayName: 'Valider produits DLC', description: 'Validation des produits DLC', action: 'validate', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_print', displayName: 'Imprimer étiquettes DLC', description: 'Impression des étiquettes DLC', action: 'print', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_stats', displayName: 'Voir statistiques DLC', description: 'Accès aux statistiques DLC', action: 'stats', resource: 'dlc' },
|
|
|
|
// Task Management (gestion_taches)
|
|
{ category: 'gestion_taches', name: 'tasks_read', displayName: 'Voir tâches', description: 'Accès en lecture aux tâches', action: 'read', resource: 'tasks' },
|
|
{ category: 'gestion_taches', name: 'tasks_create', displayName: 'Créer tâches', description: 'Création de nouvelles tâches', action: 'create', resource: 'tasks' },
|
|
{ category: 'gestion_taches', name: 'tasks_update', displayName: 'Modifier tâches', description: 'Modification des tâches', action: 'update', resource: 'tasks' },
|
|
{ category: 'gestion_taches', name: 'tasks_delete', displayName: 'Supprimer tâches', description: 'Suppression de tâches', action: 'delete', resource: 'tasks' },
|
|
{ category: 'gestion_taches', name: 'tasks_assign', displayName: 'Assigner tâches', description: 'Attribution de tâches aux utilisateurs', action: 'assign', resource: 'tasks' },
|
|
{ category: 'gestion_dlc', name: 'dlc_delete', displayName: 'Supprimer produits DLC', description: 'Suppression de produits DLC', action: 'delete', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_validate', displayName: 'Valider produits DLC', description: 'Validation des produits DLC', action: 'validate', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_print', displayName: 'Imprimer étiquettes DLC', description: 'Impression des étiquettes DLC', action: 'print', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_stats', displayName: 'Voir statistiques DLC', description: 'Accès aux statistiques DLC', action: 'read', resource: 'dlc_stats' }
|
|
];
|
|
|
|
const createdPermissions = {};
|
|
for (const perm of permissions) {
|
|
const result = await pool.query(`
|
|
INSERT INTO permissions (name, display_name, description, category, action, resource, is_system)
|
|
VALUES ($1, $2, $3, $4, $5, $6, true)
|
|
RETURNING id, name
|
|
`, [perm.name, perm.displayName, perm.description, perm.category, perm.action, perm.resource]);
|
|
createdPermissions[perm.name] = result.rows[0].id;
|
|
}
|
|
console.log(`✅ Created ${permissions.length} permissions`);
|
|
|
|
// Assign permissions to roles
|
|
const rolePermissions = {
|
|
'admin': Object.keys(createdPermissions), // Admin gets all permissions
|
|
'manager': [
|
|
'dashboard_read', 'groups_read', 'suppliers_read', 'suppliers_create', 'suppliers_update',
|
|
'orders_read', 'orders_create', 'orders_update', 'deliveries_read', 'deliveries_create',
|
|
'deliveries_update', 'publicities_read', 'publicities_create', 'publicities_update',
|
|
'publicities_participate', 'customer_orders_read', 'customer_orders_create',
|
|
'customer_orders_update', 'customer_orders_print', 'users_read', 'bl_reconciliation_read',
|
|
'bl_reconciliation_update', 'calendar_read', 'calendar_update',
|
|
'dlc_read', 'dlc_create', 'dlc_update', 'dlc_validate', 'dlc_print', 'dlc_stats',
|
|
'tasks_read', 'tasks_create', 'tasks_update', 'tasks_assign'
|
|
],
|
|
'employee': [
|
|
'dashboard_read', 'groups_read', 'suppliers_read', 'orders_read', 'deliveries_read',
|
|
'publicities_read', 'customer_orders_read', 'customer_orders_create', 'customer_orders_print',
|
|
'calendar_read', 'dlc_read', 'dlc_create', 'dlc_update',
|
|
'tasks_read', 'tasks_create', 'tasks_update'
|
|
],
|
|
'directeur': [
|
|
'dashboard_read', 'groups_read', 'groups_create', 'groups_update', 'suppliers_read',
|
|
'suppliers_create', 'suppliers_update', 'orders_read', 'orders_create', 'orders_update',
|
|
'deliveries_read', 'deliveries_create', 'deliveries_update', 'publicities_read',
|
|
'publicities_create', 'publicities_update', 'publicities_participate',
|
|
'customer_orders_read', 'customer_orders_create', 'customer_orders_update',
|
|
'customer_orders_print', 'users_read', 'users_create', 'users_update', 'roles_read',
|
|
'bl_reconciliation_read', 'bl_reconciliation_update', 'calendar_read', 'calendar_update',
|
|
'dlc_read', 'dlc_create', 'dlc_update', 'dlc_delete', 'dlc_validate', 'dlc_print', 'dlc_stats',
|
|
'tasks_read', 'tasks_create', 'tasks_update', 'tasks_delete', 'tasks_assign'
|
|
]
|
|
};
|
|
|
|
for (const [roleName, permissionNames] of Object.entries(rolePermissions)) {
|
|
const roleId = createdRoles[roleName];
|
|
for (const permName of permissionNames) {
|
|
const permId = createdPermissions[permName];
|
|
if (permId) {
|
|
await pool.query(`
|
|
INSERT INTO role_permissions (role_id, permission_id)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (role_id, permission_id) DO NOTHING
|
|
`, [roleId, permId]);
|
|
}
|
|
}
|
|
console.log(`✅ Assigned ${permissionNames.length} permissions to ${roleName}`);
|
|
}
|
|
|
|
console.log('✅ Production roles and permissions initialization complete!');
|
|
} catch (error) {
|
|
console.error('❌ Error initializing roles and permissions:', error);
|
|
// Continue anyway
|
|
}
|
|
}
|
|
|
|
async function ensureDlcPermissionsExist() {
|
|
try {
|
|
console.log('🔍 Checking for missing DLC permissions...');
|
|
|
|
// Check if DLC permissions exist
|
|
const dlcPermissions = await pool.query(`
|
|
SELECT name FROM permissions WHERE category = 'gestion_dlc'
|
|
`);
|
|
|
|
if (dlcPermissions.rows.length >= 7) {
|
|
console.log('✅ All DLC permissions already exist');
|
|
return;
|
|
}
|
|
|
|
console.log('⚠️ Missing DLC permissions, creating them...');
|
|
|
|
// Create missing DLC permissions
|
|
const requiredDlcPermissions = [
|
|
{ category: 'gestion_dlc', name: 'dlc_read', displayName: 'Voir produits DLC', description: 'Accès en lecture aux produits DLC', action: 'read', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_create', displayName: 'Créer produits DLC', description: 'Création de nouveaux produits DLC', action: 'create', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_update', displayName: 'Modifier produits DLC', description: 'Modification des produits DLC', action: 'update', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_delete', displayName: 'Supprimer produits DLC', description: 'Suppression de produits DLC', action: 'delete', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_validate', displayName: 'Valider produits DLC', description: 'Validation des produits DLC', action: 'validate', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_print', displayName: 'Imprimer étiquettes DLC', description: 'Impression des étiquettes DLC', action: 'print', resource: 'dlc' },
|
|
{ category: 'gestion_dlc', name: 'dlc_stats', displayName: 'Voir statistiques DLC', description: 'Accès aux statistiques DLC', action: 'stats', resource: 'dlc' }
|
|
];
|
|
|
|
const createdDlcPermissions = {};
|
|
for (const perm of requiredDlcPermissions) {
|
|
// Check if permission already exists
|
|
const existing = await pool.query('SELECT id FROM permissions WHERE name = $1', [perm.name]);
|
|
|
|
if (existing.rows.length === 0) {
|
|
const result = await pool.query(`
|
|
INSERT INTO permissions (name, display_name, description, category, action, resource, is_system)
|
|
VALUES ($1, $2, $3, $4, $5, $6, true)
|
|
RETURNING id, name
|
|
`, [perm.name, perm.displayName, perm.description, perm.category, perm.action, perm.resource]);
|
|
|
|
createdDlcPermissions[perm.name] = result.rows[0].id;
|
|
console.log(`✅ Created DLC permission: ${perm.displayName}`);
|
|
} else {
|
|
createdDlcPermissions[perm.name] = existing.rows[0].id;
|
|
console.log(`✅ DLC permission exists: ${perm.displayName}`);
|
|
}
|
|
}
|
|
|
|
// Assign DLC permissions to existing roles
|
|
const rolePermissions = {
|
|
'admin': ['dlc_read', 'dlc_create', 'dlc_update', 'dlc_delete', 'dlc_validate', 'dlc_print', 'dlc_stats'],
|
|
'manager': ['dlc_read', 'dlc_create', 'dlc_update', 'dlc_validate', 'dlc_print', 'dlc_stats'],
|
|
'employee': ['dlc_read', 'dlc_create', 'dlc_update'],
|
|
'directeur': ['dlc_read', 'dlc_create', 'dlc_update', 'dlc_delete', 'dlc_validate', 'dlc_print', 'dlc_stats']
|
|
};
|
|
|
|
for (const [roleName, permissionNames] of Object.entries(rolePermissions)) {
|
|
const roleQuery = await pool.query('SELECT id FROM roles WHERE name = $1', [roleName]);
|
|
if (roleQuery.rows.length > 0) {
|
|
const roleId = roleQuery.rows[0].id;
|
|
|
|
for (const permName of permissionNames) {
|
|
const permId = createdDlcPermissions[permName];
|
|
if (permId) {
|
|
await pool.query(`
|
|
INSERT INTO role_permissions (role_id, permission_id)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (role_id, permission_id) DO NOTHING
|
|
`, [roleId, permId]);
|
|
}
|
|
}
|
|
console.log(`✅ Assigned DLC permissions to ${roleName}`);
|
|
}
|
|
}
|
|
|
|
console.log('✅ DLC permissions initialization complete!');
|
|
} catch (error) {
|
|
console.error('❌ Error ensuring DLC permissions exist:', error);
|
|
// Continue anyway
|
|
}
|
|
}
|
|
|
|
export { pool }; |