Changeset 33517cc for server.js


Ignore:
Timestamp:
09/19/26 10:30:30 (11 days ago)
Author:
Klimentina Efremova <klimentina08642@…>
Branches:
finki-main, main
Children:
06ebe74
Parents:
62b2964
Message:

Turned database from SQLite to PostgressSQL, updated database changes from Phase 1 and 2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • server.js

    r62b2964 r33517cc  
    11const http = require('http');
    22const url = require('url');
    3 const database = require('./database.js');
     3const { Pool } = require('pg');
    44const fs = require('fs');
    55const path = require('path');
    … …  
    211211}
    212212
    213 // ===== FIXED: requireAuth function to check both sessions and tempAdminSessions =====
    214213function requireAuth(req, res, callback) {
    215214    const cookies = parseCookies(req);
    216215    const sessionId = cookies.sessionId;
    217216
    218     console.log(`🔐 requireAuth - Session ID from cookie: ${sessionId || 'none'}`);
    219     console.log(`🔐 requireAuth - Sessions map size: ${sessions.size}`);
    220     console.log(`🔐 requireAuth - TempAdminSessions map size: ${tempAdminSessions.size}`);
    221 
    222     // Check both regular sessions and temp admin sessions
    223     if (!sessionId) {
    224         console.log(`❌ requireAuth - No session cookie, redirecting to login`);
     217    if (!sessionId || !sessions.has(sessionId)) {
    225218        res.writeHead(302, { 'Location': '/login.html' });
    226219        res.end();
    … …  
    228221    }
    229222
    230     // Check if session exists in regular sessions
    231     if (sessions.has(sessionId)) {
    232         const userId = sessions.get(sessionId);
    233         console.log(`✅ requireAuth - Found in regular sessions, user: ${userId}`);
    234         callback(userId);
    235         return;
    236     }
    237 
    238     // Check if session exists in temp admin sessions
     223    const userId = sessions.get(sessionId);
     224
    239225    if (tempAdminSessions.has(sessionId)) {
    240         const userId = tempAdminSessions.get(sessionId);
    241         console.log(`⚠️ requireAuth - Found in temp admin sessions, user: ${userId}`);
    242 
    243         // For temp sessions, we need to check if the request is for allowed pages
    244         // Allow access to change password page and API endpoints needed for password change
    245         const allowedPaths = [
    246             '/change-password.html',
    247             '/api/force-change-password',
    248             '/api/user',
    249             '/style.css',
    250             '/script.js',
    251             '/images/'
    252         ];
    253 
    254         const isAllowed = allowedPaths.some(path => req.url.includes(path));
    255 
    256         if (!isAllowed) {
    257             console.log(`🔄 requireAuth - Redirecting to change password page`);
     226        if (!req.url.includes('/change-password') && !req.url.includes('/api/force-change-password')) {
    258227            res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
    259228            res.end();
    260229            return;
    261230        }
    262 
    263         callback(userId);
    264         return;
    265     }
    266 
    267     // Session not found in either map
    268     console.log(`❌ requireAuth - Session ID ${sessionId} not found in any session map`);
    269     res.writeHead(302, { 'Location': '/login.html' });
    270     res.end();
     231    }
     232
     233    callback(userId);
    271234}
    272235
    … …  
    355318
    356319                database.database.get(
    357                     'SELECT boss_id FROM boss WHERE boss_id = ?',
     320                    'SELECT boss_id FROM boss WHERE boss_id = $1',
    358321                    [personalId],
    359322                    (err, boss) => {
    … …  
    376339}
    377340
    378 // Database initialization function
    379 async function initializeDatabase() {
    380     console.log('🔍 Checking database schema...');
    381 
    382     // List of all required tables
    383     const requiredTables = [
    384         'client',
    385         'store',
    386         'category',
    387         'users',
    388         'personal',
    389         'product',
    390         'boss',
    391         'employees',
    392         'works_in_store',
    393         'permissions',
    394         'order',
    395         'order_items',
    396         'review',
    397         'request',
    398         'refund',
    399         'report',
    400         'audit_log',
    401         'color',
    402         'image',
    403         'delivery_address',
    404         'roles',
    405         'user_roles'
    406     ];
    407 
    408     try {
    409         // For SQLite, we need to use a different approach to check tables
    410         const result = await new Promise((resolve, reject) => {
    411             database.database.all(
    412                 "SELECT name FROM sqlite_master WHERE type='table'",
    413                 [],
    414                 (err, rows) => {
    415                     if (err) reject(err);
    416                     else resolve(rows || []);
    417                 }
    418             );
    419         });
    420 
    421         const existingTables = result.map(row => row.name);
    422         const missingTables = requiredTables.filter(table => !existingTables.includes(table));
    423 
    424         if (missingTables.length > 0) {
    425             console.log(`⚠️ Missing tables: ${missingTables.join(', ')}`);
    426             console.log('🔄 Recreating entire database...');
    427 
    428             // Drop all tables in correct order (respecting foreign keys)
    429             await dropAllTables();
    430 
    431             // Create all tables
    432             await createAllTables();
    433 
    434             // Create indexes
    435             await createIndexes();
    436 
    437             // Insert initial data
    438             await insertInitialData();
    439 
    440             console.log('✅ Database recreation completed');
    441         } else {
    442             console.log('✅ All required tables exist');
    443             // Even if tables exist, ensure admin user exists with ID 000000
    444             await ensureAdminUser();
    445         }
    446     } catch (err) {
    447         console.error('❌ Error checking database schema:', err);
    448         console.log('⚠️ Attempting to recreate database anyway...');
    449 
    450         try {
    451             await dropAllTables();
    452             await createAllTables();
    453             await createIndexes();
    454             await insertInitialData();
    455             console.log('✅ Database recreation completed');
    456         } catch (createErr) {
    457             console.error('❌ Failed to recreate database:', createErr);
    458         }
    459     }
     341
     342
     343const pool = new Pool({
     344    connectionString: process.env.DATABASE_URL,
     345    host: process.env.PGHOST || process.env.DB_HOST || 'localhost',
     346    port: Number(process.env.PGPORT || process.env.DB_PORT || 5432),
     347    user: process.env.PGUSER || process.env.DB_USER || 'postgres',
     348    password: process.env.PGPASSWORD || process.env.DB_PASSWORD || '',
     349    database: process.env.PGDATABASE || process.env.DB_NAME || 'handcraft_marketplace',
     350    max: Number(process.env.PG_POOL_MAX || 10),
     351    idleTimeoutMillis: 30000
     352});
     353
     354let transactionClient = null;
     355
     356function dbQuery(sql, params = [], callback) {
     357    const client = transactionClient || pool;
     358    client.query(sql, params)
     359        .then(result => callback(null, result))
     360        .catch(err => callback(err));
    460361}
    461362
    462 // Function to ensure admin user exists with ID 000000
    463 function ensureAdminUser() {
    464     return new Promise((resolve) => {
    465         database.database.get(
    466             'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?',
    467             ['000000', 'admin', 'admin@handcraft.com'],
    468             (err, existingAdmin) => {
    469                 if (err) {
    470                     console.error('Error checking for existing admin:', err.message);
    471                     resolve();
     363const database = {
     364    database: {
     365        get(sql, params, callback) {
     366            if (typeof params === 'function') {
     367                callback = params;
     368                params = [];
     369            }
     370            dbQuery(sql, params || [], (err, result) => {
     371                callback(err, result && result.rows ? result.rows[0] : undefined);
     372            });
     373        },
     374        all(sql, params, callback) {
     375            if (typeof params === 'function') {
     376                callback = params;
     377                params = [];
     378            }
     379            dbQuery(sql, params || [], (err, result) => {
     380                callback(err, result ? result.rows : []);
     381            });
     382        },
     383        run(sql, params, callback) {
     384            if (typeof params === 'function') {
     385                callback = params;
     386                params = [];
     387            }
     388            const normalized = String(sql).trim().toUpperCase();
     389            if (normalized === 'BEGIN TRANSACTION' || normalized === 'BEGIN') {
     390                if (transactionClient) {
     391                    callback?.(null);
    472392                    return;
    473393                }
    474 
    475                 // Insert admin user if it doesn't exist
    476                 if (!existingAdmin) {
    477                     const adminId = '000000';
    478                     const adminPassword = bcrypt.hashSync('Admin123!', 10);
    479 
    480                     // Start a transaction
    481                     database.database.run('BEGIN TRANSACTION', (err) => {
    482                         if (err) {
    483                             console.error('Error beginning transaction:', err);
    484                             resolve();
    485                             return;
    486                         }
    487 
    488                         // Insert into users table
    489                         database.database.run(
    490                             `INSERT INTO users (id, username, email, password, user_type, force_password_change)
    491                              VALUES (?, ?, ?, ?, ?, ?)`,
    492                             [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1],
    493                             function(err) {
    494                                 if (err) {
    495                                     database.database.run('ROLLBACK');
    496                                     console.error('Error inserting admin user:', err.message);
    497                                     resolve();
    498                                     return;
    499                                 }
    500 
    501                                 // Insert into personal table (required for boss table)
    502                                 database.database.run(
    503                                     `INSERT INTO personal (id, first_name, last_name, ssn, email, password)
    504                                      VALUES (?, ?, ?, ?, ?, ?)`,
    505                                     [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword],
    506                                     function(err) {
    507                                         if (err) {
    508                                             database.database.run('ROLLBACK');
    509                                             console.error('Error inserting admin personal:', err.message);
    510                                             resolve();
    511                                             return;
    512                                         }
    513 
    514                                         // Insert into boss table (store owner)
    515                                         database.database.run(
    516                                             `INSERT INTO boss (boss_id, signature)
    517                                              VALUES (?, ?)`,
    518                                             [adminId, 'Admin Signature'],
    519                                             function(err) {
    520                                                 if (err) {
    521                                                     database.database.run('ROLLBACK');
    522                                                     console.error('Error inserting admin boss:', err.message);
    523                                                     resolve();
    524                                                     return;
    525                                                 }
    526 
    527                                                 // Insert into permissions
    528                                                 database.database.run(
    529                                                     `INSERT INTO permissions (personal_id, type, authorisation)
    530                                                      VALUES (?, ?, ?)`,
    531                                                     [adminId, 'ADMIN', 'full_access'],
    532                                                     function(err) {
    533                                                         if (err) {
    534                                                             console.error('Error inserting admin permissions:', err.message);
    535                                                             // Continue even if this fails
    536                                                         }
    537 
    538                                                         // Assign admin role
    539                                                         database.database.get(
    540                                                             'SELECT role_id FROM roles WHERE name = ?',
    541                                                             ['admin'],
    542                                                             (err, adminRole) => {
    543                                                                 if (!err && adminRole) {
    544                                                                     database.database.run(
    545                                                                         'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)',
    546                                                                         [adminId, adminRole.role_id],
    547                                                                         (err) => {
    548                                                                             if (err) {
    549                                                                                 console.error('Error assigning admin role:', err.message);
    550                                                                             }
    551                                                                         }
    552                                                                     );
    553                                                                 }
    554 
    555                                                                 database.database.run('COMMIT', (commitErr) => {
    556                                                                     if (commitErr) {
    557                                                                         console.error('Error committing transaction:', commitErr);
    558                                                                         database.database.run('ROLLBACK');
    559                                                                     } else {
    560                                                                         console.log('\n');
    561                                                                         console.log('🔐 ===== ADMIN CREDENTIALS =====');
    562                                                                         console.log('🆔 ID: 000000');
    563                                                                         console.log('👤 Username: admin');
    564                                                                         console.log('📧 Email: admin@handcraft.com');
    565                                                                         console.log('🔑 Password: Admin123!');
    566                                                                         console.log('⚠️ This is a first-time login. You will be required to change your password after 2FA verification.');
    567                                                                         console.log('================================\n');
    568                                                                     }
    569                                                                     resolve();
    570                                                                 });
    571                                                             }
    572                                                         );
    573                                                     }
    574                                                 );
    575                                             }
    576                                         );
    577                                     }
    578                                 );
    579                             }
    580                         );
     394                pool.connect().then(client => {
     395                    transactionClient = client;
     396                    return client.query('BEGIN');
     397                }).then(() => callback?.(null))
     398                    .catch(err => {
     399                        if (transactionClient) transactionClient.release();
     400                        transactionClient = null;
     401                        callback?.(err);
    581402                    });
    582                 } else {
    583                     console.log('✅ Admin user already exists with ID:', existingAdmin.id);
    584                     resolve();
     403                return;
     404            }
     405            if (normalized === 'COMMIT') {
     406                if (!transactionClient) {
     407                    callback?.(null);
     408                    return;
    585409                }
    586             }
    587         );
    588     });
    589 }
    590 
    591 function dropAllTables() {
    592     return new Promise((resolve, reject) => {
    593         console.log('🗑️ Dropping all tables...');
    594 
    595         // Drop in reverse order of creation (respect foreign keys)
    596         const dropQueries = [
    597             'DROP TABLE IF EXISTS user_roles',
    598             'DROP TABLE IF EXISTS roles',
    599             'DROP TABLE IF EXISTS delivery_address',
    600             'DROP TABLE IF EXISTS image',
    601             'DROP TABLE IF EXISTS color',
    602             'DROP TABLE IF EXISTS audit_log',
    603             'DROP TABLE IF EXISTS report',
    604             'DROP TABLE IF EXISTS refund',
    605             'DROP TABLE IF EXISTS request',
    606             'DROP TABLE IF EXISTS review',
    607             'DROP TABLE IF EXISTS order_items',
    608             'DROP TABLE IF EXISTS "order"',
    609             'DROP TABLE IF EXISTS permissions',
    610             'DROP TABLE IF EXISTS works_in_store',
    611             'DROP TABLE IF EXISTS employees',
    612             'DROP TABLE IF EXISTS boss',
    613             'DROP TABLE IF EXISTS product',
    614             'DROP TABLE IF EXISTS personal',
    615             'DROP TABLE IF EXISTS users',
    616             'DROP TABLE IF EXISTS category',
    617             'DROP TABLE IF EXISTS store',
    618             'DROP TABLE IF EXISTS client'
    619         ];
    620 
    621         let index = 0;
    622 
    623         function runNext() {
    624             if (index >= dropQueries.length) {
    625                 console.log('✅ All tables dropped');
    626                 resolve();
     410                const client = transactionClient;
     411                client.query('COMMIT')
     412                    .then(() => {
     413                        transactionClient = null;
     414                        client.release();
     415                        callback?.(null);
     416                    })
     417                    .catch(err => {
     418                        transactionClient = null;
     419                        client.release();
     420                        callback?.(err);
     421                    });
    627422                return;
    628423            }
    629 
    630             database.database.run(dropQueries[index], [], (err) => {
    631                 if (err) {
    632                     console.error(`Error dropping table: ${err.message}`);
    633                     // Continue anyway
     424            if (normalized === 'ROLLBACK') {
     425                if (!transactionClient) {
     426                    callback?.(null);
     427                    return;
    634428                }
    635                 index++;
    636                 runNext();
     429                const client = transactionClient;
     430                client.query('ROLLBACK')
     431                    .then(() => {
     432                        transactionClient = null;
     433                        client.release();
     434                        callback?.(null);
     435                    })
     436                    .catch(err => {
     437                        transactionClient = null;
     438                        client.release();
     439                        callback?.(err);
     440                    });
     441                return;
     442            }
     443            dbQuery(sql, params || [], (err, result) => {
     444                if (callback) {
     445                    callback.call(
     446                        { changes: result ? result.rowCount : 0, lastID: result?.rows?.[0]?.id },
     447                        err
     448                    );
     449                }
    637450            });
    638451        }
    639 
    640         runNext();
    641     });
    642 }
    643 
    644 function createAllTables() {
    645     return new Promise((resolve, reject) => {
    646         console.log('🏗️ Creating tables...');
    647 
    648         const createQueries = [
    649             // Client table (SERIAL ID starting from 1000)
    650             `CREATE TABLE IF NOT EXISTS client (
    651                 client_id INTEGER PRIMARY KEY AUTOINCREMENT,
    652                 first_name VARCHAR(100) NOT NULL,
    653                 last_name VARCHAR(100) NOT NULL,
    654                 email VARCHAR(255) UNIQUE NOT NULL,
    655                 password VARCHAR(255) NOT NULL,
    656                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    657             )`,
    658 
    659             // Store table (VARCHAR ID)
    660             `CREATE TABLE IF NOT EXISTS store (
    661                 store_id VARCHAR(10) PRIMARY KEY,
    662                 name VARCHAR(255) NOT NULL,
     452    },
     453
     454    async initializeDatabase() {
     455        const schema = `
     456
     457            CREATE TABLE IF NOT EXISTS category (
     458                                                    id SERIAL PRIMARY KEY,
     459                                                    name VARCHAR(50) NOT NULL,
     460                parent_category_id INTEGER REFERENCES category(id) ON DELETE SET NULL
     461                );
     462            CREATE TABLE IF NOT EXISTS store (
     463                                                 store_id VARCHAR(3) PRIMARY KEY,
     464                name VARCHAR(50) UNIQUE NOT NULL,
    663465                date_of_founding DATE NOT NULL,
    664                 physical_address TEXT NOT NULL,
    665                 store_email VARCHAR(255) UNIQUE NOT NULL,
    666                 rating DECIMAL(3,2) DEFAULT 0.0
    667             )`,
    668 
    669             // Category table (SERIAL ID starting from 1)
    670             `CREATE TABLE IF NOT EXISTS category (
    671                 category_id INTEGER PRIMARY KEY AUTOINCREMENT,
    672                 name VARCHAR(100) NOT NULL,
    673                 description TEXT,
    674                 parent_category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL
    675             )`,
    676 
    677             // Users table (VARCHAR ID)
    678             `CREATE TABLE IF NOT EXISTS users (
    679                 id VARCHAR(50) PRIMARY KEY,
     466                physical_address VARCHAR(100) NOT NULL,
     467                store_email VARCHAR(40) UNIQUE NOT NULL CHECK (store_email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'),
     468                rating DECIMAL(2,1) NOT NULL DEFAULT 0 CHECK (rating >= 0 AND rating <= 5)
     469                );
     470            CREATE TABLE IF NOT EXISTS personal (
     471                                                    id VARCHAR(10) PRIMARY KEY,
     472                first_name VARCHAR(20) NOT NULL,
     473                last_name VARCHAR(20) NOT NULL,
     474                ssn VARCHAR(13) UNIQUE NOT NULL CHECK (ssn ~ '^[0-9]{13}$'),
     475                email VARCHAR(50) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'),
     476                password VARCHAR NOT NULL
     477                );
     478            CREATE TABLE IF NOT EXISTS permissions (
     479                                                       personal_is VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
     480                type VARCHAR(50) NOT NULL,
     481                authorisation VARCHAR(50) NOT NULL
     482                );
     483            CREATE TABLE IF NOT EXISTS boss (
     484                                                boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE
     485                );
     486            CREATE TABLE IF NOT EXISTS employees (
     487                                                     employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
     488                date_of_hire DATE NOT NULL
     489                );
     490            CREATE TABLE IF NOT EXISTS client (
     491                                                  client_id SERIAL PRIMARY KEY,
     492                                                  first_name VARCHAR(50) NOT NULL,
     493                last_name VARCHAR(50) NOT NULL,
     494                email VARCHAR(50) UNIQUE NOT NULL CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'),
     495                password VARCHAR NOT NULL
     496                );
     497            CREATE TABLE IF NOT EXISTS product (
     498                                                   code VARCHAR(8) PRIMARY KEY,
     499                price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
     500                availability INTEGER NOT NULL DEFAULT 0,
     501                weight DECIMAL(5,2) NOT NULL CHECK (weight > 0),
     502                width_x_length_x_depth VARCHAR(20) NOT NULL,
     503                aprox_production_time INTEGER NOT NULL,
     504                description VARCHAR(500) NOT NULL,
     505                category_id INTEGER NOT NULL REFERENCES category(id) ON DELETE SET NULL,
     506                store_id VARCHAR(3) REFERENCES store(store_id) ON DELETE CASCADE
     507                );
     508            CREATE TABLE IF NOT EXISTS image (
     509                                                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
     510                image VARCHAR NOT NULL DEFAULT 'Image not found!'
     511                );
     512            CREATE TABLE IF NOT EXISTS color (
     513                                                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
     514                color VARCHAR(50)
     515                );
     516            CREATE TABLE IF NOT EXISTS delivery_address (
     517                                                            client_id INTEGER PRIMARY KEY REFERENCES client(client_id) ON DELETE CASCADE,
     518                address VARCHAR(200) NOT NULL,
     519                city VARCHAR(30) NOT NULL,
     520                postcode VARCHAR(20) NOT NULL,
     521                country VARCHAR(40) NOT NULL,
     522                is_default BOOLEAN DEFAULT TRUE
     523                );
     524            CREATE TABLE IF NOT EXISTS "order" (
     525                                                   order_num VARCHAR(11) PRIMARY KEY,
     526                client_id INTEGER REFERENCES client(client_id) ON DELETE CASCADE,
     527                status VARCHAR(20) NOT NULL DEFAULT 'placed order',
     528                last_date_mod TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
     529                payment_method VARCHAR(250) NOT NULL,
     530                discount DECIMAL(5,2) DEFAULT 0 CHECK (discount >= 0 AND discount <= 100),
     531                store_id VARCHAR(3) REFERENCES store(store_id) ON DELETE SET NULL,
     532                order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
     533                quantity INTEGER DEFAULT 0,
     534                delivery_address VARCHAR(500),
     535                CONSTRAINT check_status CHECK (status IN ('placed order','being processed','shipping','delivered','canceled'))
     536                );
     537            CREATE TABLE IF NOT EXISTS review (
     538                                                  order_num VARCHAR(11) PRIMARY KEY REFERENCES "order"(order_num) ON DELETE CASCADE,
     539                comment VARCHAR(300),
     540                rating DECIMAL(2,1) NOT NULL CHECK (rating >= 0 AND rating <= 5),
     541                last_mod_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
     542                review_id VARCHAR(20) UNIQUE,
     543                client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
     544                product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
     545                review_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     546                );
     547            CREATE TABLE IF NOT EXISTS refund (
     548                                                  refund_id VARCHAR(50) PRIMARY KEY,
     549                order_num VARCHAR(11) REFERENCES "order"(order_num) ON DELETE CASCADE,
     550                reason VARCHAR(300),
     551                amount DECIMAL(10,2) NOT NULL,
     552                status VARCHAR(100) NOT NULL DEFAULT 'requested refund',
     553                request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
     554                processed_date TIMESTAMP
     555                );
     556            CREATE TABLE IF NOT EXISTS report (
     557                                                  date TIMESTAMP NOT NULL,
     558                                                  store_id VARCHAR(3) NOT NULL REFERENCES store(store_id) ON DELETE CASCADE,
     559                overall_profit NUMERIC NOT NULL DEFAULT 0 CHECK (overall_profit >= 0),
     560                sales_trend VARCHAR(100) NOT NULL DEFAULT '',
     561                marketing_growth VARCHAR(100) NOT NULL DEFAULT '',
     562                owner_signature VARCHAR(50) NOT NULL DEFAULT 'Not signed yet',
     563                id VARCHAR(50) UNIQUE,
     564                period VARCHAR(50),
     565                start_date DATE,
     566                end_date DATE,
     567                type VARCHAR(50),
     568                generated_by VARCHAR(10) REFERENCES personal(id) ON DELETE SET NULL,
     569                generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
     570                PRIMARY KEY (date, store_id)
     571                );
     572            CREATE TABLE IF NOT EXISTS monthly_profit (
     573                                                          report_date TIMESTAMP NOT NULL,
     574                                                          store_id VARCHAR(3) NOT NULL,
     575                month_and_year DATE NOT NULL,
     576                profit NUMERIC NOT NULL DEFAULT 0,
     577                PRIMARY KEY (report_date, store_id),
     578                FOREIGN KEY (report_date, store_id) REFERENCES report(date, store_id) ON DELETE CASCADE
     579                );
     580            CREATE TABLE IF NOT EXISTS exchanges_data (
     581                                                          report_date TIMESTAMP NOT NULL,
     582                                                          store_id VARCHAR(3) NOT NULL,
     583                monthly_profit NUMERIC NOT NULL DEFAULT 0,
     584                date TIMESTAMP NOT NULL,
     585                sales NUMERIC NOT NULL DEFAULT 0,
     586                damages NUMERIC NOT NULL DEFAULT 0 CHECK (damages <= 0),
     587                PRIMARY KEY (report_date, store_id),
     588                FOREIGN KEY (report_date, store_id) REFERENCES report(date, store_id) ON DELETE CASCADE
     589                );
     590            CREATE TABLE IF NOT EXISTS request (
     591                                                   request_num VARCHAR(14) PRIMARY KEY,
     592                date_and_time TIMESTAMP NOT NULL,
     593                problem VARCHAR(300) NOT NULL,
     594                notes_of_communication VARCHAR,
     595                customer_satisfaction NUMERIC NOT NULL DEFAULT 0,
     596                client_id INTEGER REFERENCES client(client_id) ON DELETE CASCADE,
     597                store_id VARCHAR(3) REFERENCES store(store_id) ON DELETE CASCADE,
     598                status VARCHAR(50) DEFAULT 'pending'
     599                );
     600            CREATE TABLE IF NOT EXISTS makes_request (
     601                                                         client_id INTEGER NOT NULL REFERENCES client(client_id) ON DELETE CASCADE,
     602                order_num VARCHAR(11) UNIQUE NOT NULL REFERENCES "order"(order_num) ON DELETE CASCADE,
     603                PRIMARY KEY(client_id, order_num)
     604                );
     605            CREATE TABLE IF NOT EXISTS answers (
     606                                                   request_num VARCHAR(14) REFERENCES request(request_num) ON DELETE CASCADE,
     607                personal_id VARCHAR(10) NOT NULL REFERENCES personal(id) ON DELETE CASCADE,
     608                PRIMARY KEY(request_num, personal_id)
     609                );
     610            CREATE TABLE IF NOT EXISTS for_store (
     611                                                     request_num VARCHAR(14) REFERENCES request(request_num) ON DELETE CASCADE,
     612                store_id VARCHAR(3) REFERENCES store(store_id) ON DELETE CASCADE,
     613                PRIMARY KEY(request_num, store_id)
     614                );
     615            CREATE TABLE IF NOT EXISTS "change" (
     616                                                    date_and_time TIMESTAMP NOT NULL,
     617                                                    product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
     618                changes VARCHAR NOT NULL,
     619                PRIMARY KEY(date_and_time, product_code)
     620                );
     621            CREATE TABLE IF NOT EXISTS makes_change (
     622                                                        personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
     623                change_date_time TIMESTAMP,
     624                product_code VARCHAR(8),
     625                PRIMARY KEY(personal_id, change_date_time, product_code),
     626                FOREIGN KEY(change_date_time, product_code) REFERENCES "change"(date_and_time, product_code) ON DELETE CASCADE
     627                );
     628            CREATE TABLE IF NOT EXISTS works_in_store (
     629                                                          personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
     630                store_id VARCHAR(3) REFERENCES store(store_id) ON DELETE CASCADE,
     631                PRIMARY KEY(personal_id, store_id)
     632                );
     633            CREATE TABLE IF NOT EXISTS worked (
     634                                                  personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
     635                report_date TIMESTAMP,
     636                store_id VARCHAR(3),
     637                wage NUMERIC NOT NULL CHECK(wage >= 0),
     638                pay_method VARCHAR DEFAULT 'full_time' CHECK(pay_method IN ('full_time','part-time','custom')),
     639                total_hours NUMERIC NOT NULL,
     640                week VARCHAR(23) NOT NULL,
     641                PRIMARY KEY(personal_id, report_date, store_id),
     642                FOREIGN KEY(report_date, store_id) REFERENCES report(date, store_id) ON DELETE CASCADE
     643                );
     644            CREATE TABLE IF NOT EXISTS sells (
     645                                                 product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
     646                store_id VARCHAR(3) REFERENCES store(store_id) ON DELETE CASCADE,
     647                discount NUMERIC NOT NULL DEFAULT 0,
     648                PRIMARY KEY(product_code, store_id)
     649                );
     650            CREATE TABLE IF NOT EXISTS includes (
     651                                                    order_num VARCHAR(11) REFERENCES "order"(order_num) ON DELETE CASCADE,
     652                product_code VARCHAR(8) REFERENCES product(code) ON DELETE CASCADE,
     653                quantity INTEGER NOT NULL CHECK(quantity >= 0),
     654                PRIMARY KEY(order_num, product_code)
     655                );
     656            CREATE TABLE IF NOT EXISTS approves (
     657                                                    boss_id VARCHAR(10) REFERENCES boss(boss_id) ON DELETE CASCADE,
     658                report_date TIMESTAMP,
     659                store_id VARCHAR(3),
     660                owner_signature VARCHAR NOT NULL,
     661                PRIMARY KEY(boss_id, report_date, store_id),
     662                FOREIGN KEY(report_date, store_id) REFERENCES report(date, store_id) ON DELETE CASCADE
     663                );
     664
     665-- Application support tables required by the existing HTTP/authentication layer.
     666            CREATE TABLE IF NOT EXISTS users (
     667                                                 id VARCHAR(50) PRIMARY KEY,
    680668                username VARCHAR(100) UNIQUE NOT NULL,
    681669                email VARCHAR(255) UNIQUE NOT NULL,
    682670                password VARCHAR(255) NOT NULL,
    683671                user_type VARCHAR(50) NOT NULL,
    684                 force_password_change INTEGER DEFAULT 0,
     672                force_password_change BOOLEAN DEFAULT FALSE,
    685673                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    686             )`,
    687 
    688             // Personal table (VARCHAR ID - format: storeId(3) + '001' for owner, storeId(3) + employeeNum(3) for employees)
    689             `CREATE TABLE IF NOT EXISTS personal (
    690                 id VARCHAR(10) PRIMARY KEY,
    691                 first_name VARCHAR(100) NOT NULL,
    692                 last_name VARCHAR(100) NOT NULL,
    693                 ssn VARCHAR(13) UNIQUE NOT NULL,
    694                 email VARCHAR(255) UNIQUE NOT NULL,
    695                 password VARCHAR(255) NOT NULL,
    696                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    697             )`,
    698 
    699             // Product table (VARCHAR ID)
    700             `CREATE TABLE IF NOT EXISTS product (
    701                 id VARCHAR(50) PRIMARY KEY,
    702                 code VARCHAR(20) UNIQUE NOT NULL,
    703                 description TEXT NOT NULL,
    704                 price DECIMAL(10,2) NOT NULL,
    705                 availability INTEGER NOT NULL DEFAULT 0,
    706                 weight DECIMAL(10,2),
    707                 dimensions VARCHAR(50),
    708                 production_time INTEGER,
    709                 category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL,
    710                 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
    711                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    712             )`,
    713 
    714             // Boss table (VARCHAR ID - references personal.id)
    715             `CREATE TABLE IF NOT EXISTS boss (
    716                 boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
    717                 signature TEXT NOT NULL,
    718                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    719             )`,
    720 
    721             // Employees table (VARCHAR ID - references personal.id)
    722             `CREATE TABLE IF NOT EXISTS employees (
    723                 employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
    724                 date_of_hire DATE NOT NULL,
    725                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    726             )`,
    727 
    728             // Works_in_store table (junction)
    729             `CREATE TABLE IF NOT EXISTS works_in_store (
    730                 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
    731                 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
    732                 PRIMARY KEY (personal_id, store_id)
    733             )`,
    734 
    735             // Permissions table
    736             `CREATE TABLE IF NOT EXISTS permissions (
    737                 permission_id INTEGER PRIMARY KEY AUTOINCREMENT,
    738                 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
    739                 type VARCHAR(50) NOT NULL,
    740                 authorisation TEXT,
    741                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    742             )`,
    743 
    744             // Order table (VARCHAR ID)
    745             `CREATE TABLE IF NOT EXISTS "order" (
    746                 order_num VARCHAR(20) PRIMARY KEY,
    747                 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
    748                 order_date TIMESTAMP NOT NULL,
    749                 quantity INTEGER NOT NULL,
    750                 payment_method VARCHAR(50) NOT NULL,
    751                 discount DECIMAL(10,2) DEFAULT 0,
    752                 delivery_address TEXT NOT NULL,
    753                 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE SET NULL,
    754                 status VARCHAR(50) DEFAULT 'pending',
    755                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    756             )`,
    757 
    758             // Order_items table
    759             `CREATE TABLE IF NOT EXISTS order_items (
    760                 item_id INTEGER PRIMARY KEY AUTOINCREMENT,
    761                 order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE,
    762                 product_code VARCHAR(20) REFERENCES product(code) ON DELETE SET NULL,
    763                 quantity INTEGER NOT NULL,
    764                 price DECIMAL(10,2) NOT NULL,
    765                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    766             )`,
    767 
    768             // Review table (VARCHAR ID)
    769             `CREATE TABLE IF NOT EXISTS review (
    770                 review_id VARCHAR(20) PRIMARY KEY,
    771                 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
    772                 product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE,
    773                 rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
    774                 comment TEXT,
    775                 review_date TIMESTAMP NOT NULL,
    776                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    777             )`,
    778 
    779             // Request table (VARCHAR ID)
    780             `CREATE TABLE IF NOT EXISTS request (
    781                 request_num VARCHAR(50) PRIMARY KEY,
    782                 date_and_time TIMESTAMP NOT NULL,
    783                 problem TEXT NOT NULL,
    784                 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
    785                 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
    786                 status VARCHAR(50) DEFAULT 'pending',
    787                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    788             )`,
    789 
    790             // Refund table (VARCHAR ID)
    791             `CREATE TABLE IF NOT EXISTS refund (
    792                 refund_id VARCHAR(50) PRIMARY KEY,
    793                 order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE,
    794                 amount DECIMAL(10,2) NOT NULL,
    795                 reason TEXT NOT NULL,
    796                 status VARCHAR(50) DEFAULT 'pending',
    797                 request_date TIMESTAMP NOT NULL,
    798                 processed_date TIMESTAMP,
    799                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    800             )`,
    801 
    802             // Report table (VARCHAR ID)
    803             `CREATE TABLE IF NOT EXISTS report (
    804                 id VARCHAR(50) PRIMARY KEY,
    805                 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
    806                 period VARCHAR(50) NOT NULL,
    807                 start_date DATE NOT NULL,
    808                 end_date DATE NOT NULL,
    809                 type VARCHAR(50) NOT NULL,
    810                 generated_by VARCHAR(10) REFERENCES personal(id) ON DELETE SET NULL,
    811                 generated_at TIMESTAMP NOT NULL,
    812                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    813             )`,
    814 
    815             // Audit_log table (SERIAL ID)
    816             `CREATE TABLE IF NOT EXISTS audit_log (
    817                 log_id INTEGER PRIMARY KEY AUTOINCREMENT,
    818                 user_id VARCHAR(50),
     674                );
     675            CREATE TABLE IF NOT EXISTS roles (
     676                                                 role_id SERIAL PRIMARY KEY,
     677                                                 name VARCHAR(50) UNIQUE NOT NULL,
     678                description TEXT
     679                );
     680            CREATE TABLE IF NOT EXISTS user_roles (
     681                                                      user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE,
     682                role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE,
     683                PRIMARY KEY(user_id, role_id)
     684                );
     685            CREATE TABLE IF NOT EXISTS audit_log (
     686                                                     log_id BIGSERIAL PRIMARY KEY,
     687                                                     user_id VARCHAR(50),
    819688                action VARCHAR(100) NOT NULL,
    820689                resource_type VARCHAR(50),
    … …  
    823692                ip_address VARCHAR(45),
    824693                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    825             )`,
    826 
    827             // Color table (SERIAL ID)
    828             `CREATE TABLE IF NOT EXISTS color (
    829                 color_id INTEGER PRIMARY KEY AUTOINCREMENT,
    830                 name VARCHAR(50) NOT NULL,
    831                 hex_code VARCHAR(7) NOT NULL,
    832                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    833             )`,
    834 
    835             // Image table (SERIAL ID)
    836             `CREATE TABLE IF NOT EXISTS image (
    837                 image_id INTEGER PRIMARY KEY AUTOINCREMENT,
    838                 product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE,
    839                 image_url TEXT NOT NULL,
    840                 is_primary BOOLEAN DEFAULT FALSE,
    841                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    842             )`,
    843 
    844             // Delivery_address table (SERIAL ID)
    845             `CREATE TABLE IF NOT EXISTS delivery_address (
    846                 address_id INTEGER PRIMARY KEY AUTOINCREMENT,
    847                 client_id INTEGER REFERENCES client(client_id) ON DELETE CASCADE,
    848                 address TEXT NOT NULL,
    849                 city VARCHAR(100) NOT NULL,
    850                 postcode VARCHAR(20) NOT NULL,
    851                 country VARCHAR(100) NOT NULL,
    852                 is_default BOOLEAN DEFAULT FALSE,
    853                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    854             )`,
    855 
    856             // Roles table (SERIAL ID)
    857             `CREATE TABLE IF NOT EXISTS roles (
    858                 role_id INTEGER PRIMARY KEY AUTOINCREMENT,
    859                 name VARCHAR(50) UNIQUE NOT NULL,
    860                 description TEXT,
    861                 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    862             )`,
    863 
    864             // User_roles table (junction)
    865             `CREATE TABLE IF NOT EXISTS user_roles (
    866                 user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE,
    867                 role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE,
    868                 PRIMARY KEY (user_id, role_id)
    869             )`
     694                );
     695            CREATE INDEX IF NOT EXISTS idx_product_store ON product(store_id);
     696            CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id);
     697            CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_id);
     698            CREATE INDEX IF NOT EXISTS idx_order_store ON "order"(store_id);
     699            CREATE INDEX IF NOT EXISTS idx_order_date ON "order"(order_date);
     700            CREATE INDEX IF NOT EXISTS idx_review_client ON review(client_id);
     701            CREATE INDEX IF NOT EXISTS idx_review_product ON review(product_code);
     702            CREATE INDEX IF NOT EXISTS idx_request_client ON request(client_id);
     703            CREATE INDEX IF NOT EXISTS idx_request_store ON request(store_id);
     704            CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num);
     705            CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email);
     706            CREATE INDEX IF NOT EXISTS idx_client_email ON client(email);
     707            CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
     708            CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
     709            CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id);
     710
     711        `;
     712        await pool.query(schema);
     713        const roles = [
     714            ['admin', 'System administrator'],
     715            ['store_owner', 'Store owner'],
     716            ['store_employee', 'Store employee'],
     717            ['client', 'Registered client'],
     718            ['guest', 'Unregistered guest']
    870719        ];
    871 
    872         let index = 0;
    873 
    874         function runNext() {
    875             if (index >= createQueries.length) {
    876                 console.log('✅ All tables created');
    877                 resolve();
    878                 return;
    879             }
    880 
    881             const tableName = createQueries[index].split('TABLE')[1].split('(')[0].trim().replace('IF NOT EXISTS', '').trim();
    882             console.log(`Creating table: ${tableName}...`);
    883 
    884             database.database.run(createQueries[index], [], (err) => {
    885                 if (err) {
    886                     console.error(`Error creating table: ${err.message}`);
    887                     reject(err);
    888                     return;
    889                 }
    890                 console.log(`✅ Created table: ${tableName}`);
    891                 index++;
    892                 runNext();
    893             });
     720        for (const [name, description] of roles) {
     721            await pool.query(
     722                'INSERT INTO roles(name, description) VALUES($1,$2) ON CONFLICT(name) DO NOTHING',
     723                [name, description]
     724            );
    894725        }
    895726
    896         runNext();
    897     });
    898 }
    899 
    900 function createIndexes() {
    901     return new Promise((resolve, reject) => {
    902         console.log('📊 Creating indexes...');
    903 
    904         const indexQueries = [
    905             'CREATE INDEX IF NOT EXISTS idx_product_store ON product(store_id)',
    906             'CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id)',
    907             'CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_id)',
    908             'CREATE INDEX IF NOT EXISTS idx_order_store ON "order"(store_id)',
    909             'CREATE INDEX IF NOT EXISTS idx_order_date ON "order"(order_date)',
    910             'CREATE INDEX IF NOT EXISTS idx_review_client ON review(client_id)',
    911             'CREATE INDEX IF NOT EXISTS idx_review_product ON review(product_code)',
    912             'CREATE INDEX IF NOT EXISTS idx_request_client ON request(client_id)',
    913             'CREATE INDEX IF NOT EXISTS idx_request_store ON request(store_id)',
    914             'CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num)',
    915             'CREATE INDEX IF NOT EXISTS idx_refund_status ON refund(status)',
    916             'CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email)',
    917             'CREATE INDEX IF NOT EXISTS idx_client_email ON client(email)',
    918             'CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)',
    919             'CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)',
    920             'CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id)',
    921             'CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action)',
    922             'CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at)',
    923             'CREATE INDEX IF NOT EXISTS idx_delivery_client ON delivery_address(client_id)',
    924             'CREATE INDEX IF NOT EXISTS idx_works_in_store_personal ON works_in_store(personal_id)',
    925             'CREATE INDEX IF NOT EXISTS idx_works_in_store_store ON works_in_store(store_id)'
     727        const bcrypt = require('bcryptjs');
     728        const hash = bcrypt.hashSync('Admin123!', 10);
     729        await pool.query(
     730            `INSERT INTO users(id, username, email, password, user_type, force_password_change)
     731             VALUES('000000','admin','admin@handcraft.com',$1,'admin',TRUE)
     732                 ON CONFLICT(id) DO NOTHING`,
     733            [hash]
     734        );
     735        await pool.query(
     736            `INSERT INTO personal(id, first_name, last_name, ssn, email, password)
     737             VALUES('000000','Admin','User','0000000000000','admin@handcraft.com',$1)
     738                 ON CONFLICT(id) DO NOTHING`,
     739            [hash]
     740        );
     741        await pool.query(
     742            `INSERT INTO boss(boss_id) VALUES('000000') ON CONFLICT(boss_id) DO NOTHING`
     743        );
     744        await pool.query(
     745            `INSERT INTO permissions(personal_is,type,authorisation)
     746             VALUES('000000','ADMIN','full_access')
     747                 ON CONFLICT(personal_is) DO NOTHING`
     748        );
     749        await pool.query(
     750            `INSERT INTO user_roles(user_id,role_id)
     751             SELECT '000000', role_id FROM roles WHERE name='admin'
     752                 ON CONFLICT DO NOTHING`
     753        );
     754        await pool.query(
     755            `INSERT INTO category(name,parent_category_id)
     756             SELECT 'General', NULL
     757                 WHERE NOT EXISTS (SELECT 1 FROM category WHERE name='General')`
     758        );
     759        console.log('✅ PostgreSQL schema is ready');
     760    },
     761
     762    close() {
     763        return pool.end();
     764    },
     765
     766    getUserById(id, callback) {
     767        dbQuery(
     768            `SELECT u.*,
     769                    COALESCE(json_agg(json_build_object('name',r.name,'description',r.description))
     770                             FILTER (WHERE r.role_id IS NOT NULL), '[]') AS roles
     771             FROM users u
     772                      LEFT JOIN user_roles ur ON ur.user_id=u.id
     773                      LEFT JOIN roles r ON r.role_id=ur.role_id
     774             WHERE u.id=$1
     775             GROUP BY u.id`,
     776            [String(id)],
     777            (err, result) => callback(err, result?.rows?.[0])
     778        );
     779    },
     780
     781    getUserByUsername(username, callback) {
     782        dbQuery(
     783            `SELECT u.*,
     784                    COALESCE(json_agg(json_build_object('name',r.name,'description',r.description))
     785                             FILTER (WHERE r.role_id IS NOT NULL), '[]') AS roles
     786             FROM users u
     787                      LEFT JOIN user_roles ur ON ur.user_id=u.id
     788                      LEFT JOIN roles r ON r.role_id=ur.role_id
     789             WHERE u.username=$1 OR u.email=$1
     790             GROUP BY u.id
     791                 LIMIT 1`,
     792            [username],
     793            (err, result) => callback(err, result?.rows?.[0])
     794        );
     795    },
     796
     797    createUser(id, username, email, password, userType, callback) {
     798        dbQuery(
     799            `INSERT INTO users(id,username,email,password,user_type,force_password_change)
     800             VALUES($1,$2,$3,$4,$5,FALSE) RETURNING id`,
     801            [String(id), username, email, password, userType],
     802            (err, result) => {
     803                if (err) return callback(err);
     804                const roleName = userType === 'client' ? 'client' :
     805                    userType === 'store_owner' ? 'store_owner' :
     806                        userType === 'store_employee' ? 'store_employee' : 'guest';
     807                dbQuery(
     808                    `INSERT INTO user_roles(user_id,role_id)
     809                     SELECT $1, role_id FROM roles WHERE name=$2
     810                         ON CONFLICT DO NOTHING`,
     811                    [String(id), roleName],
     812                    roleErr => callback(roleErr, String(id))
     813                );
     814            }
     815        );
     816    },
     817
     818    createClient(data, callback) {
     819        dbQuery(
     820            `INSERT INTO client(first_name,last_name,email,password)
     821             VALUES($1,$2,$3,$4) RETURNING client_id`,
     822            [data.firstName || data.first_name, data.lastName || data.last_name, data.email, data.password],
     823            (err, result) => callback(err, result?.rows?.[0]?.client_id)
     824        );
     825    },
     826
     827    getClientByEmail(email, callback) {
     828        dbQuery('SELECT * FROM client WHERE email=$1', [email],
     829            (err, result) => callback(err, result?.rows?.[0]));
     830    },
     831
     832    getClientById(id, callback) {
     833        dbQuery('SELECT * FROM client WHERE client_id=$1', [id],
     834            (err, result) => callback(err, result?.rows?.[0]));
     835    },
     836
     837    getPersonalByEmail(email, callback) {
     838        dbQuery('SELECT * FROM personal WHERE email=$1', [email],
     839            (err, result) => callback(err, result?.rows?.[0]));
     840    },
     841
     842    getPersonalById(id, callback) {
     843        dbQuery('SELECT * FROM personal WHERE id=$1', [String(id)],
     844            (err, result) => callback(err, result?.rows?.[0]));
     845    },
     846
     847    verifyPassword(password, hash) {
     848        const bcrypt = require('bcryptjs');
     849        try { return bcrypt.compareSync(password, hash); } catch { return false; }
     850    },
     851
     852    verifyClientPassword(password, hash, callback) {
     853        require('bcryptjs').compare(password, hash, callback);
     854    },
     855
     856    logAudit(userId, action, resourceType, resourceId, details, ipAddress) {
     857        dbQuery(
     858            `INSERT INTO audit_log(user_id,action,resource_type,resource_id,details,ip_address)
     859             VALUES($1,$2,$3,$4,$5,$6)`,
     860            [userId == null ? null : String(userId), action, resourceType, resourceId == null ? null : String(resourceId), details, ipAddress],
     861            () => {}
     862        );
     863    },
     864
     865    ensureGeneralCategory(callback) {
     866        dbQuery(
     867            `INSERT INTO category(name,parent_category_id)
     868             SELECT 'General',NULL
     869                 WHERE NOT EXISTS(SELECT 1 FROM category WHERE name='General')`,
     870            [],
     871            err => callback(err)
     872        );
     873    },
     874
     875    getProducts(categoryId, searchTerm, callback) {
     876        const params = [];
     877        const where = [];
     878        if (categoryId) { params.push(categoryId); where.push(`p.category_id=$${params.length}`); }
     879        if (searchTerm) {
     880            params.push(`%${searchTerm}%`);
     881            where.push(`(p.description ILIKE $${params.length} OR p.code ILIKE $${params.length})`);
     882        }
     883        const sql = `SELECT p.*, c.name AS category_name, p.store_id
     884                     FROM product p LEFT JOIN category c ON c.id=p.category_id
     885                         ${where.length ? 'WHERE '+where.join(' AND ') : ''}
     886                     ORDER BY p.code`;
     887        dbQuery(sql, params, (err, result) => callback(err, result?.rows || []));
     888    },
     889
     890    getProductById(id, callback) {
     891        dbQuery(
     892            `SELECT p.*,c.name AS category_name FROM product p
     893                                                         LEFT JOIN category c ON c.id=p.category_id WHERE p.code=$1 OR p.code::text=$1 LIMIT 1`,
     894            [String(id)],
     895            (err,result)=>callback(err,result?.rows?.[0])
     896        );
     897    },
     898
     899    getProductByCode(code, callback) {
     900        dbQuery(
     901            `SELECT p.*,c.name AS category_name FROM product p
     902                                                         LEFT JOIN category c ON c.id=p.category_id WHERE p.code=$1`,
     903            [code],
     904            (err,result)=>callback(err,result?.rows?.[0])
     905        );
     906    },
     907
     908    addProduct(personalId, data, callback) {
     909        const storeId = data.store_id || data.storeId || String(data.code).slice(0,3);
     910        dbQuery(
     911            `INSERT INTO product(code,price,availability,weight,width_x_length_x_depth,
     912                                 aprox_production_time,description,category_id,store_id)
     913             VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9) RETURNING code`,
     914            [
     915                data.code, data.price, data.availability ?? 0, data.weight,
     916                data.width_x_length_x_depth || data.dimensions || '',
     917                data.aprox_production_time || data.production_time || 0,
     918                data.description, data.category_id, storeId
     919            ],
     920            (err,result)=>{
     921                if (err) return callback(err);
     922                dbQuery(
     923                    `INSERT INTO sells(product_code,store_id,discount)
     924                     VALUES($1,$2,$3) ON CONFLICT(product_code,store_id) DO UPDATE SET discount=EXCLUDED.discount`,
     925                    [data.code,storeId,data.discount || 0],
     926                    e => callback(e, data.code)
     927                );
     928            }
     929        );
     930    },
     931
     932    updateProduct(personalId, data, callback) {
     933        const fields = [];
     934        const params = [];
     935        const allowed = [
     936            ['price','price'],['availability','availability'],['weight','weight'],
     937            ['width_x_length_x_depth','width_x_length_x_depth'],
     938            ['dimensions','width_x_length_x_depth'],
     939            ['aprox_production_time','aprox_production_time'],
     940            ['production_time','aprox_production_time'],
     941            ['description','description'],['category_id','category_id']
    926942        ];
    927 
    928         let index = 0;
    929 
    930         function runNext() {
    931             if (index >= indexQueries.length) {
    932                 console.log('✅ Indexes created');
    933                 resolve();
    934                 return;
    935             }
    936 
    937             database.database.run(indexQueries[index], [], (err) => {
    938                 if (err) {
    939                     console.log(`⚠️ Index creation warning for ${indexQueries[index].substring(0, 50)}...: ${err.message}`);
    940                 }
    941                 index++;
    942                 runNext();
    943             });
     943        for (const [input,col] of allowed) {
     944            if (data[input] !== undefined) {
     945                params.push(data[input]);
     946                fields.push(`${col}=$${params.length}`);
     947            }
    944948        }
    945 
    946         runNext();
    947     });
    948 }
    949 
    950 function insertInitialData() {
    951     return new Promise((resolve, reject) => {
    952         console.log('📝 Inserting initial data...');
    953 
    954         // Insert default roles
    955         const roles = [
    956             { name: 'admin', description: 'System administrator' },
    957             { name: 'store_owner', description: 'Store owner' },
    958             { name: 'store_employee', description: 'Store employee' },
    959             { name: 'client', description: 'Registered client' },
    960             { name: 'guest', description: 'Unregistered guest' }
    961         ];
    962 
    963         let rolesInserted = 0;
    964 
    965         roles.forEach(role => {
    966             database.database.run(
    967                 `INSERT INTO roles (name, description)
    968                  VALUES (?, ?)
    969                  ON CONFLICT DO NOTHING`,
    970                 [role.name, role.description],
    971                 (err) => {
    972                     if (err) {
    973                         console.error(`Error inserting role ${role.name}:`, err.message);
    974                     }
    975                     rolesInserted++;
    976 
    977                     if (rolesInserted === roles.length) {
    978                         console.log('✅ Roles inserted');
    979                         // Create admin user with ID 000000
    980                         createAdminUser();
    981 
    982                         // Ensure General category exists
    983                         database.ensureGeneralCategory((err) => {
    984                             if (err) {
    985                                 console.error('Error ensuring General category:', err.message);
    986                             } else {
    987                                 console.log('✅ General category checked/created');
     949        if (!fields.length) return callback(null,0);
     950        params.push(data.code);
     951        dbQuery(`UPDATE product SET ${fields.join(', ')} WHERE code=$${params.length}`, params,
     952            (err,result)=>callback(err,result?.rowCount || 0));
     953    },
     954
     955    deleteProduct(productCode, storeId, personalId, callback) {
     956        dbQuery('DELETE FROM product WHERE code=$1 AND store_id=$2', [productCode,storeId],
     957            (err)=>callback(err));
     958    },
     959
     960    createCategory(data, callback) {
     961        dbQuery(
     962            `INSERT INTO category(name,parent_category_id) VALUES($1,$2) RETURNING id,name,parent_category_id`,
     963            [data.name, data.parent_category_id || null],
     964            (err,result)=>callback(err,result?.rows?.[0])
     965        );
     966    },
     967
     968    getCategories(callback) {
     969        dbQuery('SELECT * FROM category ORDER BY name', [], (err,result)=>callback(err,result?.rows||[]));
     970    },
     971
     972    getCategoriesWithParents(callback) {
     973        dbQuery(
     974            `SELECT c.*,p.name AS parent_name
     975             FROM category c LEFT JOIN category p ON p.id=c.parent_category_id
     976             ORDER BY c.name`,
     977            [], (err,result)=>callback(err,result?.rows||[])
     978        );
     979    },
     980
     981    getStores(callback) {
     982        dbQuery('SELECT * FROM store ORDER BY name', [], (err,result)=>callback(err,result?.rows||[]));
     983    },
     984
     985    createOrderNew(data, callback) {
     986        const items = data.items || data.products || data.order_items || [];
     987        const storeId = data.store_id || data.storeId || (items[0]?.code ? String(items[0].code).slice(0,3) : null);
     988        const now = new Date();
     989        const year = String(now.getFullYear()).slice(-3);
     990        const prefix = storeId || '000';
     991        const insertOrder = () => {
     992            dbQuery(
     993                `SELECT COUNT(*)::int AS n FROM "order" WHERE store_id=$1 AND EXTRACT(YEAR FROM order_date)=EXTRACT(YEAR FROM CURRENT_DATE)`,
     994                [storeId],
     995                (countErr,countResult)=>{
     996                    if (countErr) return callback(countErr);
     997                    const seq=Number(countResult.rows[0].n)+1;
     998                    const orderNum=`${prefix}${year}${String(seq).padStart(5,'0')}`;
     999                    dbQuery(
     1000                        `INSERT INTO "order"(order_num,client_id,status,last_date_mod,payment_method,discount,store_id,order_date,quantity,delivery_address)
     1001                         VALUES($1,$2,$3,CURRENT_TIMESTAMP,$4,$5,$6,CURRENT_TIMESTAMP,$7,$8) RETURNING order_num`,
     1002                        [orderNum,data.client_id,data.status||'placed order',data.payment_method,data.discount||0,storeId,
     1003                            items.reduce((n,x)=>n+Number(x.quantity||1),0),data.delivery_address||data.deliveryAddress||null],
     1004                        (err,result)=>{
     1005                            if(err) return callback(err);
     1006                            let pending=items.length;
     1007                            if(!pending) return callback(null,orderNum);
     1008                            let firstErr=null;
     1009                            for(const item of items){
     1010                                dbQuery(
     1011                                    `INSERT INTO includes(order_num,product_code,quantity) VALUES($1,$2,$3)`,
     1012                                    [orderNum,item.product_code||item.code,item.quantity||1],
     1013                                    e=>{ if(e) firstErr ||= e; if(--pending===0) callback(firstErr,firstErr?undefined:orderNum); }
     1014                                );
    9881015                            }
    989                             resolve();
    990                         });
    991                     }
     1016                        }
     1017                    );
    9921018                }
    9931019            );
    994         });
    995     });
    996 }
    997 
    998 // Function to create admin user with ID 000000
    999 function createAdminUser() {
    1000     const adminId = '000000';
    1001     const adminPassword = bcrypt.hashSync('Admin123!', 10);
    1002 
    1003     database.database.get(
    1004         'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?',
    1005         [adminId, 'admin', 'admin@handcraft.com'],
    1006         (err, existingAdmin) => {
    1007             if (err) {
    1008                 console.error('Error checking for existing admin:', err.message);
    1009                 return;
    1010             }
    1011 
    1012             if (!existingAdmin) {
    1013                 // Start a transaction
    1014                 database.database.run('BEGIN TRANSACTION', (err) => {
    1015                     if (err) {
    1016                         console.error('Error beginning transaction:', err);
    1017                         return;
    1018                     }
    1019 
    1020                     // Insert into users table
    1021                     database.database.run(
    1022                         `INSERT INTO users (id, username, email, password, user_type, force_password_change)
    1023                          VALUES (?, ?, ?, ?, ?, ?)`,
    1024                         [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1],
    1025                         function(err) {
    1026                             if (err) {
    1027                                 database.database.run('ROLLBACK');
    1028                                 console.error('Error inserting admin user:', err.message);
    1029                                 return;
    1030                             }
    1031 
    1032                             // Insert into personal table (required for boss table)
    1033                             database.database.run(
    1034                                 `INSERT INTO personal (id, first_name, last_name, ssn, email, password)
    1035                                  VALUES (?, ?, ?, ?, ?, ?)`,
    1036                                 [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword],
    1037                                 function(err) {
    1038                                     if (err) {
    1039                                         database.database.run('ROLLBACK');
    1040                                         console.error('Error inserting admin personal:', err.message);
    1041                                         return;
    1042                                     }
    1043 
    1044                                     // Insert into boss table (store owner)
    1045                                     database.database.run(
    1046                                         `INSERT INTO boss (boss_id, signature)
    1047                                          VALUES (?, ?)`,
    1048                                         [adminId, 'Admin Signature'],
    1049                                         function(err) {
    1050                                             if (err) {
    1051                                                 database.database.run('ROLLBACK');
    1052                                                 console.error('Error inserting admin boss:', err.message);
    1053                                                 return;
    1054                                             }
    1055 
    1056                                             // Insert into permissions
    1057                                             database.database.run(
    1058                                                 `INSERT INTO permissions (personal_id, type, authorisation)
    1059                                                  VALUES (?, ?, ?)`,
    1060                                                 [adminId, 'ADMIN', 'full_access'],
    1061                                                 function(err) {
    1062                                                     if (err) {
    1063                                                         console.error('Error inserting admin permissions:', err.message);
    1064                                                         // Continue even if this fails
    1065                                                     }
    1066 
    1067                                                     // Assign admin role
    1068                                                     database.database.get(
    1069                                                         'SELECT role_id FROM roles WHERE name = ?',
    1070                                                         ['admin'],
    1071                                                         (err, adminRole) => {
    1072                                                             if (!err && adminRole) {
    1073                                                                 database.database.run(
    1074                                                                     'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)',
    1075                                                                     [adminId, adminRole.role_id],
    1076                                                                     (err) => {
    1077                                                                         if (err) {
    1078                                                                             console.error('Error assigning admin role:', err.message);
    1079                                                                         }
    1080                                                                     }
    1081                                                                 );
    1082                                                             }
    1083 
    1084                                                             database.database.run('COMMIT', (commitErr) => {
    1085                                                                 if (commitErr) {
    1086                                                                     console.error('Error committing transaction:', commitErr);
    1087                                                                     database.database.run('ROLLBACK');
    1088                                                                 } else {
    1089                                                                     console.log('\n');
    1090                                                                     console.log('🔐 ===== ADMIN CREDENTIALS =====');
    1091                                                                     console.log('🆔 ID: 000000');
    1092                                                                     console.log('👤 Username: admin');
    1093                                                                     console.log('📧 Email: admin@handcraft.com');
    1094                                                                     console.log('🔑 Password: Admin123!');
    1095                                                                     console.log('⚠️ This is a first-time login. You will be required to change your password after 2FA verification.');
    1096                                                                     console.log('================================\n');
    1097                                                                 }
    1098                                                             });
    1099                                                         }
    1100                                                     );
    1101                                                 }
    1102                                             );
    1103                                         }
    1104                                     );
    1105                                 }
    1106                             );
    1107                         }
    1108                     );
    1109                 });
    1110             } else {
    1111                 console.log('✅ Admin user already exists with ID:', existingAdmin.id);
    1112             }
    1113         }
    1114     );
    1115 }
    1116 
    1117 // Initialize database on startup
    1118 (async function() {
     1020        };
     1021        insertOrder();
     1022    },
     1023
     1024    getOrdersByClient(clientId, callback) {
     1025        dbQuery(
     1026            `SELECT o.*,COALESCE(json_agg(json_build_object('product_code',i.product_code,'quantity',i.quantity,'price',p.price))
     1027                                 FILTER(WHERE i.product_code IS NOT NULL),'[]') AS items
     1028             FROM "order" o
     1029                      LEFT JOIN includes i ON i.order_num=o.order_num
     1030                      LEFT JOIN product p ON p.code=i.product_code
     1031             WHERE o.client_id=$1 GROUP BY o.order_num ORDER BY o.order_date DESC`,
     1032            [clientId],(err,result)=>callback(err,result?.rows||[])
     1033        );
     1034    },
     1035
     1036    createReviewNew(data, callback) {
     1037        const reviewId = data.review_id || ('REV'+Date.now());
     1038        dbQuery(
     1039            `INSERT INTO review(order_num,comment,rating,last_mod_date,review_id,client_id,product_code,review_date)
     1040             VALUES($1,$2,$3,CURRENT_TIMESTAMP,$4,$5,$6,CURRENT_TIMESTAMP)
     1041                 RETURNING review_id`,
     1042            [data.order_num,data.comment||null,data.rating,reviewId,data.client_id||null,data.product_code||null],
     1043            (err,result)=>callback(err,result?.rows?.[0]?.review_id || reviewId)
     1044        );
     1045    },
     1046
     1047    createRequest(data, callback) {
     1048        dbQuery(
     1049            `INSERT INTO request(request_num,date_and_time,problem,notes_of_communication,customer_satisfaction,client_id,store_id)
     1050             VALUES($1,$2,$3,$4,0,$5,$6) RETURNING request_num`,
     1051            [data.request_num,data.date_and_time,data.problem,data.notes_of_communication||null,data.client_id,data.store_id],
     1052            (err,result)=>callback(err,result?.rows?.[0]?.request_num)
     1053        );
     1054    },
     1055
     1056    createRefund(data, callback) {
     1057        dbQuery(
     1058            `INSERT INTO refund(refund_id,order_num,reason,amount,status,request_date)
     1059             VALUES($1,$2,$3,$4,$5,CURRENT_TIMESTAMP) RETURNING refund_id`,
     1060            [String(data.refund_id),data.order_num,data.reason||null,data.amount,data.status||'requested refund'],
     1061            (err,result)=>callback(err,result?.rows?.[0]?.refund_id)
     1062        );
     1063    },
     1064
     1065    getAllUsers(callback) {
     1066        dbQuery(`SELECT u.*,COALESCE(json_agg(r.name) FILTER(WHERE r.role_id IS NOT NULL),'[]') roles
     1067                 FROM users u LEFT JOIN user_roles ur ON ur.user_id=u.id
     1068                              LEFT JOIN roles r ON r.role_id=ur.role_id GROUP BY u.id ORDER BY u.created_at DESC`,
     1069            [],(err,result)=>callback(err,result?.rows||[]));
     1070    },
     1071
     1072    getAllOrders(callback) {
     1073        dbQuery(`SELECT o.*,c.first_name,c.last_name,c.email FROM "order" o
     1074                                                                      LEFT JOIN client c ON c.client_id=o.client_id ORDER BY o.order_date DESC`,
     1075            [],(err,result)=>callback(err,result?.rows||[]));
     1076    },
     1077
     1078    getStoreProducts(storeId, callback) {
     1079        dbQuery(`SELECT p.*,c.name AS category_name,s.discount FROM product p
     1080                                                                        LEFT JOIN category c ON c.id=p.category_id
     1081                                                                        LEFT JOIN sells s ON s.product_code=p.code AND s.store_id=$1
     1082                 WHERE p.store_id=$1 OR EXISTS(SELECT 1 FROM sells sx WHERE sx.product_code=p.code AND sx.store_id=$1)
     1083                 ORDER BY p.code`,[storeId],(err,result)=>callback(err,result?.rows||[]));
     1084    },
     1085
     1086    getStoreOrders(storeId, callback) {
     1087        dbQuery(`SELECT o.*,c.first_name,c.last_name FROM "order" o
     1088                                                              LEFT JOIN client c ON c.client_id=o.client_id
     1089                 WHERE o.store_id=$1 ORDER BY o.order_date DESC`,[storeId],
     1090            (err,result)=>callback(err,result?.rows||[]));
     1091    },
     1092
     1093    getStoreEmployees(storeId, callback) {
     1094        dbQuery(`SELECT p.*,e.date_of_hire,per.type,per.authorisation
     1095                 FROM personal p JOIN works_in_store w ON w.personal_id=p.id
     1096                                 LEFT JOIN employees e ON e.employee_id=p.id
     1097                                 LEFT JOIN permissions per ON per.personal_is=p.id
     1098                 WHERE w.store_id=$1 ORDER BY p.last_name,p.first_name`,
     1099            [storeId],(err,result)=>callback(err,result?.rows||[]));
     1100    },
     1101
     1102    getStoreReports(storeId, callback) {
     1103        dbQuery(`SELECT * FROM report WHERE store_id=$1 ORDER BY date DESC`,[storeId],
     1104            (err,result)=>callback(err,result?.rows||[]));
     1105    },
     1106
     1107    getStoreStats(storeId, callback) {
     1108        const sql=`SELECT
     1109                           (SELECT COUNT(*) FROM product WHERE store_id=$1)::int AS product_count,
     1110                           (SELECT COUNT(*) FROM "order" WHERE store_id=$1)::int AS order_count,
     1111                           (SELECT COALESCE(SUM(i.quantity*p.price*(1-o.discount/100)),0)
     1112                            FROM "order" o JOIN includes i ON i.order_num=o.order_num JOIN product p ON p.code=i.product_code
     1113                            WHERE o.store_id=$1) AS revenue,
     1114                           (SELECT COUNT(*) FROM works_in_store WHERE store_id=$1)::int AS employee_count,
     1115                           (SELECT COUNT(*) FROM request WHERE store_id=$1)::int AS request_count,
     1116                           (SELECT COUNT(*) FROM refund r JOIN "order" o ON o.order_num=r.order_num WHERE o.store_id=$1)::int AS refund_count`;
     1117        dbQuery(sql,[storeId],(err,result)=>callback(err,result?.rows?.[0]||{}));
     1118    },
     1119
     1120    getEmployeeTasks(personalId, storeId, callback) {
     1121        dbQuery(`SELECT r.*,a.personal_id AS answered_by
     1122                 FROM request r
     1123                          LEFT JOIN answers a ON a.request_num=r.request_num
     1124                 WHERE r.store_id=$1 AND (a.personal_id=$2 OR a.personal_id IS NULL)
     1125                 ORDER BY r.date_and_time DESC`,
     1126            [storeId,personalId],(err,result)=>callback(err,result?.rows||[]));
     1127    },
     1128
     1129    getClientStats(clientId, callback) {
     1130        dbQuery(`SELECT
     1131                         (SELECT COUNT(*) FROM "order" WHERE client_id=$1)::int AS order_count,
     1132                         (SELECT COUNT(*) FROM review WHERE client_id=$1)::int AS review_count,
     1133                         (SELECT COUNT(*) FROM request WHERE client_id=$1)::int AS request_count,
     1134                         (SELECT COUNT(*) FROM refund r JOIN "order" o ON o.order_num=r.order_num WHERE o.client_id=$1)::int AS refund_count`,
     1135            [clientId],(err,result)=>callback(err,result?.rows?.[0]||{}));
     1136    }
     1137};
     1138
     1139
     1140
     1141
     1142// PostgreSQL schema initialization.
     1143// The schema is based on the supplied Pasted markdown.md. A few invalid PostgreSQL
     1144// declarations in the original paste are corrected here (for example DECIMMAL,
     1145// PARTIAL KEY, and composite-key column-name typos). The existing HTTP layer also
     1146// needs the small application-support tables and compatibility columns defined below.
     1147(async () => {
    11191148    try {
    1120         await initializeDatabase();
     1149        await database.initializeDatabase();
    11211150        console.log('✅ Database initialization completed');
    11221151    } catch (err) {
    11231152        console.error('❌ Database initialization failed:', err);
     1153        process.exitCode = 1;
    11241154    }
    11251155})();
    … …  
    11541184        const sessionId = cookies.sessionId;
    11551185
    1156         if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {
     1186        if (!sessionId || !sessions.has(sessionId)) {
    11571187            res.writeHead(302, { 'Location': '/login.html' });
    11581188            res.end();
    … …  
    11761206        const sessionId = cookies.sessionId;
    11771207
    1178         if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {
     1208        if (!sessionId || !sessions.has(sessionId)) {
    11791209            res.writeHead(302, { 'Location': '/login.html' });
    1180             res.end();
    1181             return;
    1182         }
    1183 
    1184         if (tempAdminSessions.has(sessionId)) {
    1185             res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
    11861210            res.end();
    11871211            return;
    … …  
    12011225
    12021226                database.database.get(
    1203                     'SELECT boss_id FROM boss WHERE boss_id = ?',
     1227                    'SELECT boss_id FROM boss WHERE boss_id = $1',
    12041228                    [personalId],
    12051229                    (err, boss) => {
    … …  
    12221246        serveStaticFile(res, 'admin.html', 'text/html');
    12231247    } else if (pathname === '/store-owner.html') {
    1224         // Check if user is authenticated
    1225         const cookies = parseCookies(req);
    1226         const sessionId = cookies.sessionId;
    1227 
    1228         console.log(`📄 Accessing store-owner.html - Session ID: ${sessionId || 'none'}`);
    1229 
    1230         if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {
    1231             console.log(`❌ store-owner.html - No valid session, redirecting to login`);
    1232             res.writeHead(302, { 'Location': '/login.html' });
    1233             res.end();
    1234             return;
    1235         }
    1236 
    1237         if (tempAdminSessions.has(sessionId)) {
    1238             console.log(`⚠️ store-owner.html - Temporary session, redirecting to change password`);
    1239             res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
    1240             res.end();
    1241             return;
    1242         }
    1243 
    1244         // Get user from session
    1245         const userId = sessions.get(sessionId);
    1246         console.log(`📄 store-owner.html - User ID from session: ${userId}`);
    1247 
    1248         // Check if this is a store owner
    1249         if (userId.startsWith('personal_')) {
    1250             const personalId = userId.replace('personal_', '');
    1251 
    1252             database.database.get(
    1253                 'SELECT boss_id FROM boss WHERE boss_id = ?',
    1254                 [personalId],
    1255                 (err, boss) => {
    1256                     if (boss) {
    1257                         // Is a store owner, serve the page
    1258                         console.log(`✅ store-owner.html - User is a store owner, serving page`);
    1259                         serveStaticFile(res, 'store-owner.html', 'text/html');
    1260                     } else {
    1261                         // Not a store owner, redirect to appropriate page
    1262                         console.log(`❌ store-owner.html - User is not a store owner, redirecting`);
    1263                         res.writeHead(302, { 'Location': '/dashboard.html' });
    1264                         res.end();
    1265                     }
    1266                 }
    1267             );
    1268         } else if (userId === '000000') {
    1269             // Admin trying to access store owner page
    1270             console.log(`❌ store-owner.html - Admin trying to access, redirecting to admin`);
    1271             res.writeHead(302, { 'Location': '/admin.html' });
    1272             res.end();
    1273         } else if (userId.startsWith('client_')) {
    1274             // Client trying to access store owner page
    1275             console.log(`❌ store-owner.html - Client trying to access, redirecting to client`);
    1276             res.writeHead(302, { 'Location': '/client-dashboard.html' });
    1277             res.end();
    1278         } else {
    1279             res.writeHead(302, { 'Location': '/dashboard.html' });
    1280             res.end();
    1281         }
     1248        serveStaticFile(res, 'store-owner.html', 'text/html');
    12821249    } else if (pathname === '/store-employee.html') {
    1283         // Check if user is authenticated
    1284         const cookies = parseCookies(req);
    1285         const sessionId = cookies.sessionId;
    1286 
    1287         console.log(`📄 Accessing store-employee.html - Session ID: ${sessionId || 'none'}`);
    1288 
    1289         if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {
    1290             console.log(`❌ store-employee.html - No valid session, redirecting to login`);
    1291             res.writeHead(302, { 'Location': '/login.html' });
    1292             res.end();
    1293             return;
    1294         }
    1295 
    1296         if (tempAdminSessions.has(sessionId)) {
    1297             console.log(`⚠️ store-employee.html - Temporary session, redirecting to change password`);
    1298             res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
    1299             res.end();
    1300             return;
    1301         }
    1302 
    1303         // Get user from session
    1304         const userId = sessions.get(sessionId);
    1305         console.log(`📄 store-employee.html - User ID from session: ${userId}`);
    1306 
    1307         // Check if this is a store employee
    1308         if (userId.startsWith('personal_')) {
    1309             const personalId = userId.replace('personal_', '');
    1310 
    1311             database.database.get(
    1312                 'SELECT employee_id FROM employees WHERE employee_id = ?',
    1313                 [personalId],
    1314                 (err, employee) => {
    1315                     if (employee) {
    1316                         // Is a store employee, serve the page
    1317                         console.log(`✅ store-employee.html - User is a store employee, serving page`);
    1318                         serveStaticFile(res, 'store-employee.html', 'text/html');
    1319                     } else {
    1320                         // Check if they're a store owner (they can also access employee page)
    1321                         database.database.get(
    1322                             'SELECT boss_id FROM boss WHERE boss_id = ?',
    1323                             [personalId],
    1324                             (err, boss) => {
    1325                                 if (boss) {
    1326                                     console.log(`✅ store-employee.html - User is a store owner (can access), serving page`);
    1327                                     serveStaticFile(res, 'store-employee.html', 'text/html');
    1328                                 } else {
    1329                                     // Not authorized
    1330                                     console.log(`❌ store-employee.html - User is not authorized, redirecting`);
    1331                                     res.writeHead(302, { 'Location': '/dashboard.html' });
    1332                                     res.end();
    1333                                 }
    1334                             }
    1335                         );
    1336                     }
    1337                 }
    1338             );
    1339         } else if (userId === '000000') {
    1340             // Admin trying to access employee page
    1341             console.log(`❌ store-employee.html - Admin trying to access, redirecting to admin`);
    1342             res.writeHead(302, { 'Location': '/admin.html' });
    1343             res.end();
    1344         } else if (userId.startsWith('client_')) {
    1345             // Client trying to access employee page
    1346             console.log(`❌ store-employee.html - Client trying to access, redirecting to client`);
    1347             res.writeHead(302, { 'Location': '/client-dashboard.html' });
    1348             res.end();
    1349         } else {
    1350             res.writeHead(302, { 'Location': '/dashboard.html' });
    1351             res.end();
    1352         }
     1250        serveStaticFile(res, 'store-employee.html', 'text/html');
    13531251    } else if (pathname === '/client-dashboard.html') {
    1354         // Check if user is authenticated
    1355         const cookies = parseCookies(req);
    1356         const sessionId = cookies.sessionId;
    1357 
    1358         console.log(`📄 Accessing client-dashboard.html - Session ID: ${sessionId || 'none'}`);
    1359 
    1360         if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {
    1361             console.log(`❌ client-dashboard.html - No valid session, redirecting to login`);
    1362             res.writeHead(302, { 'Location': '/login.html' });
    1363             res.end();
    1364             return;
    1365         }
    1366 
    1367         if (tempAdminSessions.has(sessionId)) {
    1368             console.log(`⚠️ client-dashboard.html - Temporary session, redirecting to change password`);
    1369             res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
    1370             res.end();
    1371             return;
    1372         }
    1373 
    1374         // Get user from session
    1375         const userId = sessions.get(sessionId);
    1376         console.log(`📄 client-dashboard.html - User ID from session: ${userId}`);
    1377 
    1378         // Check if this is a client
    1379         if (userId.startsWith('client_')) {
    1380             // Is a client, serve the page
    1381             console.log(`✅ client-dashboard.html - User is a client, serving page`);
    1382             serveStaticFile(res, 'client-dashboard.html', 'text/html');
    1383         } else if (userId === '000000') {
    1384             // Admin trying to access client page
    1385             console.log(`❌ client-dashboard.html - Admin trying to access, redirecting to admin`);
    1386             res.writeHead(302, { 'Location': '/admin.html' });
    1387             res.end();
    1388         } else if (userId.startsWith('personal_')) {
    1389             // Personal user trying to access client page
    1390             console.log(`❌ client-dashboard.html - Personal user trying to access, redirecting to store`);
    1391             res.writeHead(302, { 'Location': '/store-owner.html' });
    1392             res.end();
    1393         } else {
    1394             res.writeHead(302, { 'Location': '/dashboard.html' });
    1395             res.end();
    1396         }
     1252        serveStaticFile(res, 'client-dashboard.html', 'text/html');
    13971253    } else if (pathname === '/products.html') {
    13981254        serveStaticFile(res, 'products.html', 'text/html');
    … …  
    15911447
    15921448                database.database.get(
    1593                     'SELECT store_id FROM store WHERE store_email = ?',
     1449                    'SELECT store_id FROM store WHERE store_email = $1',
    15941450                    [formData.storeEmail],
    15951451                    (err, existingStore) => {
    … …  
    19711827                    // Insert into store table (store_id is VARCHAR)
    19721828                    database.database.run(
    1973                         'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES (?, ?, ?, ?, ?, ?)',
     1829                        'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES ($1, $2, $3, $4, $5, $6)',
    19741830                        [
    19751831                            tempStoreData.storeId,
    … …  
    19911847                            // Insert into personal table (id is VARCHAR)
    19921848                            database.database.run(
    1993                                 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)',
     1849                                'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES ($1, $2, $3, $4, $5, $6)',
    19941850                                [
    19951851                                    tempStoreData.personalId,
    … …  
    20201876                                    // Insert into boss table (boss_id is VARCHAR, references personal.id)
    20211877                                    database.database.run(
    2022                                         'INSERT INTO boss (boss_id, signature) VALUES (?, ?)',
     1878                                        'INSERT INTO boss (boss_id, signature) VALUES ($1, $2)',
    20231879                                        [tempStoreData.personalId, tempStoreData.signature],
    20241880                                        (err) => {
    … …  
    20331889                                            // Insert into works_in_store table (personal_id is VARCHAR, store_id is VARCHAR)
    20341890                                            database.database.run(
    2035                                                 'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)',
     1891                                                'INSERT INTO works_in_store (personal_id, store_id) VALUES ($1, $2)',
    20361892                                                [tempStoreData.personalId, tempStoreData.storeId],
    20371893                                                (err) => {
    … …  
    20461902                                                    // Insert into permissions table (personal_id is VARCHAR)
    20471903                                                    database.database.run(
    2048                                                         'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)',
     1904                                                        'INSERT INTO permissions (personal_id, type, authorisation) VALUES ($1, $2, $3)',
    20491905                                                        [tempStoreData.personalId, 'BOSS', 'full_access'],
    20501906                                                        (err) => {
    … …  
    20551911                                                            // Also create entry in users table for login with force_password_change = 1
    20561912                                                            database.database.run(
    2057                                                                 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)',
     1913                                                                'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES ($1, $2, $3, $4, $5, $6)',
    20581914                                                                [
    20591915                                                                    tempStoreData.personalId,
    … …  
    21482004                        if (tempUserData.address && tempUserData.city && tempUserData.postcode && tempUserData.country) {
    21492005                            database.database.run(
    2150                                 'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES (?, ?, ?, ?, ?, ?)',
     2006                                'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES ($1, $2, $3, $4, $5, $6)',
    21512007                                [
    21522008                                    clientId,
    … …  
    23222178                        res.writeHead(200, {
    23232179                            'Content-Type': 'application/json',
    2324                             'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`
     2180                            'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict`
    23252181                        });
    23262182
    … …  
    23692225                            // Check if this is a boss (store owner)
    23702226                            database.database.get(
    2371                                 'SELECT boss_id FROM boss WHERE boss_id = ?',
     2227                                'SELECT boss_id FROM boss WHERE boss_id = $1',
    23722228                                [personal.id],
    23732229                                (err, boss) => {
    … …  
    23802236                                        // Check if first time login from users table
    23812237                                        database.database.get(
    2382                                             'SELECT force_password_change FROM users WHERE email = ?',
     2238                                            'SELECT force_password_change FROM users WHERE email = $1',
    23832239                                            [email],
    23842240                                            (err, user) => {
    … …  
    24302286                                    // Check if this is an employee
    24312287                                    database.database.get(
    2432                                         'SELECT employee_id FROM employees WHERE employee_id = ?',
     2288                                        'SELECT employee_id FROM employees WHERE employee_id = $1',
    24332289                                        [personal.id],
    24342290                                        (err, employee) => {
    … …  
    24402296                                                // This is an employee
    24412297                                                database.database.get(
    2442                                                     'SELECT force_password_change FROM users WHERE email = ?',
     2298                                                    'SELECT force_password_change FROM users WHERE email = $1',
    24432299                                                    [email],
    24442300                                                    (err, user) => {
    … …  
    24912347                                            // Treat as regular user
    24922348                                            database.database.get(
    2493                                                 'SELECT * FROM users WHERE email = ?',
     2349                                                'SELECT * FROM users WHERE email = $1',
    24942350                                                [email],
    24952351                                                (err, user) => {
    … …  
    26332489
    26342490            database.database.get(
    2635                 'SELECT * FROM users WHERE email = ?',
     2491                'SELECT * FROM users WHERE email = $1',
    26362492                [email],
    26372493                (err, user) => {
    … …  
    27162572    }
    27172573
    2718     // ===== FIXED: /api/verify-2fa endpoint with proper redirect handling =====
    27192574    else if (pathname === '/api/verify-2fa' && req.method === 'POST') {
    27202575        let body = '';
    … …  
    27562611                verificationCodes.delete(email);
    27572612
    2758                 // Determine redirect based on user type - all go to change-password.html with appropriate query parameters
     2613                // Determine redirect based on user type
    27592614                let redirectTo = 'change-password.html?forced=true';
    2760 
    2761                 // Add redirect parameter to know where to go after password change
    27622615                if (verificationData.userType === 'store_owner') {
    27632616                    redirectTo = 'change-password.html?forced=true&redirect=store-owner.html';
    … …  
    27682621                } else if (verificationData.userType === 'client') {
    27692622                    redirectTo = 'change-password.html?forced=true&redirect=client-dashboard.html';
    2770                 } else {
    2771                     redirectTo = 'change-password.html?forced=true&redirect=dashboard.html';
    27722623                }
    2773 
    2774                 console.log(`🔄 Password change required for ${verificationData.userType}. Redirecting to: ${redirectTo}`);
    2775                 console.log(`🔄 Temp session created: ${tempSessionId} for user: ${verificationData.userId}`);
    2776                 console.log(`🔐 TempAdminSessions now has ${tempAdminSessions.size} entries`);
    27772624
    27782625                res.writeHead(200, {
    … …  
    28292676            }
    28302677
    2831             console.log(`✅ ${verificationData.userType} login successful. Session: ${sessionId}, User: ${sessions.get(sessionId)}, Redirecting to: ${redirectTo}`);
    2832             console.log(`📊 Current sessions: ${Array.from(sessions.entries()).map(([id, user]) => `${id.substring(0,8)}...:${user}`).join(', ')}`);
     2678            console.log(`✅ ${verificationData.userType} login successful. Redirecting to: ${redirectTo}`);
    28332679
    28342680            res.writeHead(200, {
    28352681                'Content-Type': 'application/json',
    2836                 'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`
     2682                'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict`
    28372683            });
    28382684
    … …  
    28512697
    28522698        if (sessionId) {
    2853             const userId = sessions.get(sessionId) || tempAdminSessions.get(sessionId);
     2699            const userId = sessions.get(sessionId);
    28542700            if (userId) {
    28552701                database.logAudit(userId, 'LOGOUT', 'auth', userId.toString(), 'User logged out', ipAddress);
    … …  
    28722718            const sessionId = cookies.sessionId;
    28732719
    2874             // Check if this is a temp session
    28752720            if (tempAdminSessions.has(sessionId)) {
    28762721                // This is a temporary session (password change required)
    … …  
    28952740                                // Personal user (store owner/employee)
    28962741                                database.database.get(
    2897                                     'SELECT boss_id FROM boss WHERE boss_id = ?',
     2742                                    'SELECT boss_id FROM boss WHERE boss_id = $1',
    28982743                                    [userId],
    28992744                                    (err, boss) => {
    … …  
    29962841
    29972842                    database.database.get(
    2998                         'SELECT boss_id FROM boss WHERE boss_id = ?',
     2843                        'SELECT boss_id FROM boss WHERE boss_id = $1',
    29992844                        [personalId],
    30002845                        (err, boss) => {
    … …  
    30062851                                database.database.all(
    30072852                                    `SELECT s.* FROM store s
    3008                                      JOIN works_in_store w ON s.store_id = w.store_id
    3009                                      WHERE w.personal_id = ?`,
     2853                                                         JOIN works_in_store w ON s.store_id = w.store_id
     2854                                     WHERE w.personal_id = $1`,
    30102855                                    [personalId],
    30112856                                    (err, stores) => {
    … …  
    30312876                            } else {
    30322877                                database.database.get(
    3033                                     'SELECT employee_id FROM employees WHERE employee_id = ?',
     2878                                    'SELECT employee_id FROM employees WHERE employee_id = $1',
    30342879                                    [personalId],
    30352880                                    (err, employee) => {
    … …  
    30412886                                            database.database.all(
    30422887                                                `SELECT s.* FROM store s
    3043                                                  JOIN works_in_store w ON s.store_id = w.store_id
    3044                                                  WHERE w.personal_id = ?`,
     2888                                                                     JOIN works_in_store w ON s.store_id = w.store_id
     2889                                                 WHERE w.personal_id = $1`,
    30452890                                                [personalId],
    30462891                                                (err, stores) => {
    … …  
    32323077
    32333078                    database.database.get(
    3234                         'SELECT COUNT(*) as order_count FROM "order" WHERE store_id = ? AND strftime("%Y", order_date) = ?',
     3079                        'SELECT COUNT(*) AS order_count FROM "order" WHERE store_id = $1 AND EXTRACT(YEAR FROM order_date)::INTEGER = $2::INTEGER',
    32353080                        [storeId, new Date().getFullYear().toString()],
    32363081                        (err, result) => {
    … …  
    33633208
    33643209                    database.database.get(
    3365                         'SELECT COUNT(*) as request_count FROM request WHERE store_id = ? AND strftime("%Y", date_and_time) = ? AND strftime("%m", date_and_time) = ?',
     3210                        'SELECT COUNT(*)::int AS request_count FROM request WHERE store_id = $1 AND EXTRACT(YEAR FROM date_and_time)::int = $2::int AND EXTRACT(MONTH FROM date_and_time)::int = $3::int',
    33663211                        [storeId, now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')],
    33673212                        (err, result) => {
    … …  
    34213266
    34223267                    database.database.get(
    3423                         'SELECT store_id FROM "order" WHERE order_num = ?',
     3268                        'SELECT store_id FROM "order" WHERE order_num = $1',
    34243269                        [refundData.order_num],
    34253270                        (err, result) => {
    … …  
    34363281
    34373282                            database.database.get(
    3438                                 'SELECT COUNT(*) as refund_count FROM refund WHERE strftime("%Y", request_date) = ? AND strftime("%m", request_date) = ?',
     3283                                'SELECT COUNT(*)::int AS refund_count FROM refund WHERE EXTRACT(YEAR FROM request_date)::int = $1::int AND EXTRACT(MONTH FROM request_date)::int = $2::int',
    34393284                                [now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')],
    34403285                                (err, result) => {
    … …  
    34863331
    34873332                database.database.get(
    3488                     'SELECT store_id FROM works_in_store WHERE personal_id = ?',
     3333                    'SELECT store_id FROM works_in_store WHERE personal_id = $1',
    34893334                    [personalId],
    34903335                    (err, bossStore) => {
    … …  
    35043349
    35053350                        database.database.get(
    3506                             'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     3351                            'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    35073352                            [personalId, storeId],
    35083353                            (err, ownsStore) => {
    … …  
    35133358                                }
    35143359
    3515                                 // FIXED: Changed SQL syntax from SUBSTRING(code FROM 4) to SUBSTR(code, 4) for SQLite compatibility
     3360                                // PostgreSQL: extract the numeric product sequence from the store-prefixed product code
    35163361                                database.database.get(
    3517                                     'SELECT MAX(CAST(SUBSTR(code, 4) AS INTEGER)) as max_product_num FROM product WHERE store_id = ?',
     3362                                    'SELECT MAX(CAST(SUBSTRING(code FROM 4) AS INTEGER)) AS max_product_num FROM product WHERE store_id = $1',
    35183363                                    [storeId],
    35193364                                    (err, result) => {
    … …  
    35843429
    35853430                database.database.get(
    3586                     'SELECT store_id FROM product WHERE code = ?',
     3431                    'SELECT store_id FROM product WHERE code = $1',
    35873432                    [productData.code],
    35883433                    (err, product) => {
    … …  
    35943439
    35953440                        database.database.get(
    3596                             'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     3441                            'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    35973442                            [personalId, product.store_id],
    35983443                            (err, ownsStore) => {
    … …  
    37213566
    37223567                        database.database.run(
    3723                             'UPDATE users SET password = ?, force_password_change = 0 WHERE id = ?',
     3568                            'UPDATE users SET password = $1, force_password_change = FALSE WHERE id = $2',
    37243569                            [hashedPassword, userId],
    37253570                            function(err) {
    … …  
    37333578                                // Also update password in personal table if it exists (for admin)
    37343579                                database.database.run(
    3735                                     'UPDATE personal SET password = ? WHERE id = ?',
     3580                                    'UPDATE personal SET password = $1 WHERE id = $2',
    37363581                                    [hashedPassword, userId],
    37373582                                    function(err) {
    … …  
    37723617                                }
    37733618
    3774                                 console.log(`✅ Password changed successfully for user ${userId}, redirecting to ${finalRedirect}`);
    3775                                 console.log(`New session created: ${newSessionId} -> ${sessionUserId}`);
     3619                                console.log(`Password changed successfully for user ${userId}, redirecting to ${finalRedirect}`);
    37763620
    37773621                                database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(),
    37783622                                    `${user.user_type || 'user'} forced password change completed`, ipAddress);
    37793623
    3780                                 // Set the cookie with proper options - extended to 24 hours
     3624                                // Set the cookie with proper options
    37813625                                res.writeHead(200, {
    37823626                                    'Content-Type': 'application/json',
    3783                                     'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`
     3627                                    'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict` // Extended to 24 hours
    37843628                                });
    37853629
    … …  
    38083652
    38093653                                database.database.run(
    3810                                     'UPDATE personal SET password = ? WHERE id = ?',
     3654                                    'UPDATE personal SET password = $1 WHERE id = $2',
    38113655                                    [hashedPassword, userId],
    38123656                                    function(err) {
    … …  
    38203664                                        // Also update in users table if exists
    38213665                                        database.database.run(
    3822                                             'UPDATE users SET password = ?, force_password_change = 0 WHERE email = ?',
     3666                                            'UPDATE users SET password = $1, force_password_change = FALSE WHERE email = $2',
    38233667                                            [hashedPassword, personal.email],
    38243668                                            function(err) {
    … …  
    38313675                                        // Determine user type (boss/owner or employee)
    38323676                                        database.database.get(
    3833                                             'SELECT boss_id FROM boss WHERE boss_id = ?',
     3677                                            'SELECT boss_id FROM boss WHERE boss_id = $1',
    38343678                                            [userId],
    38353679                                            (err, boss) => {
    … …  
    38493693                                                sessions.set(newSessionId, `personal_${userId}`);
    38503694
    3851                                                 console.log(`✅ Password changed successfully for ${userType} ${userId}, redirecting to ${finalRedirect}`);
    3852                                                 console.log(`New session created: ${newSessionId} -> personal_${userId}`);
     3695                                                console.log(`Password changed successfully for ${userType} ${userId}, redirecting to ${finalRedirect}`);
    38533696
    38543697                                                database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(),
    … …  
    39083751
    39093752            database.database.get(
    3910                 'SELECT boss_id FROM boss WHERE boss_id = ?',
     3753                'SELECT boss_id FROM boss WHERE boss_id = $1',
    39113754                [personalId],
    39123755                (err, boss) => {
    … …  
    40143857
    40153858                                        database.database.run(
    4016                                             'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)',
     3859                                            'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES ($1, $2, $3, $4, $5, $6)',
    40173860                                            [
    40183861                                                newPersonalId,
    … …  
    40423885
    40433886                                                database.database.run(
    4044                                                     'INSERT INTO employees (employee_id, date_of_hire) VALUES (?, ?)',
     3887                                                    'INSERT INTO employees (employee_id, date_of_hire) VALUES ($1, $2)',
    40453888                                                    [newPersonalId, dateOfHire],
    40463889                                                    (err) => {
    … …  
    40543897
    40553898                                                        database.database.run(
    4056                                                             'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)',
     3899                                                            'INSERT INTO works_in_store (personal_id, store_id) VALUES ($1, $2)',
    40573900                                                            [newPersonalId, storeId],
    40583901                                                            (err) => {
    … …  
    40663909
    40673910                                                                database.database.run(
    4068                                                                     'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)',
     3911                                                                    'INSERT INTO permissions (personal_id, type, authorisation) VALUES ($1, $2, $3)',
    40693912                                                                    [newPersonalId, 'EMPLOYEE', 'limited_access'],
    40703913                                                                    (err) => {
    … …  
    40753918                                                                        // Also create entry in users table for login with force_password_change = 1
    40763919                                                                        database.database.run(
    4077                                                                             'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)',
     3920                                                                            'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES ($1, $2, $3, $4, $5, $6)',
    40783921                                                                            [
    40793922                                                                                newPersonalId,
    … …  
    41483991
    41493992            database.database.get(
    4150                 'SELECT boss_id FROM boss WHERE boss_id = ?',
     3993                'SELECT boss_id FROM boss WHERE boss_id = $1',
    41513994                [personalId],
    41523995                (err, boss) => {
    … …  
    41714014
    41724015                        database.database.get(
    4173                             'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4016                            'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    41744017                            [personalId, storeId],
    41754018                            (err, bossStore) => {
    … …  
    41814024
    41824025                                database.database.get(
    4183                                     'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4026                                    'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    41844027                                    [employeeId, storeId],
    41854028                                    (err, employeeStore) => {
    … …  
    41914034
    41924035                                        database.database.get(
    4193                                             'SELECT boss_id FROM boss WHERE boss_id = ?',
     4036                                            'SELECT boss_id FROM boss WHERE boss_id = $1',
    41944037                                            [employeeId],
    41954038                                            (err, isBoss) => {
    … …  
    42134056
    42144057                                                    database.database.run(
    4215                                                         'DELETE FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4058                                                        'DELETE FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    42164059                                                        [employeeId, storeId],
    42174060                                                        (err) => {
    … …  
    42254068
    42264069                                                            database.database.run(
    4227                                                                 'DELETE FROM employees WHERE employee_id = ?',
     4070                                                                'DELETE FROM employees WHERE employee_id = $1',
    42284071                                                                [employeeId],
    42294072                                                                (err) => {
    … …  
    42334076
    42344077                                                                    database.database.run(
    4235                                                                         'DELETE FROM permissions WHERE personal_id = ?',
     4078                                                                        'DELETE FROM permissions WHERE personal_id = $1',
    42364079                                                                        [employeeId],
    42374080                                                                        (err) => {
    … …  
    42414084
    42424085                                                                            database.database.run(
    4243                                                                                 'DELETE FROM personal WHERE id = ?',
     4086                                                                                'DELETE FROM personal WHERE id = $1',
    42444087                                                                                [employeeId],
    42454088                                                                                (err) => {
    … …  
    42504093                                                                                    // Also delete from users table
    42514094                                                                                    database.database.run(
    4252                                                                                         'DELETE FROM users WHERE id = ?',
     4095                                                                                        'DELETE FROM users WHERE id = $1',
    42534096                                                                                        [employeeId],
    42544097                                                                                        (err) => {
    … …  
    43174160
    43184161            database.database.get(
    4319                 'SELECT boss_id FROM boss WHERE boss_id = ?',
     4162                'SELECT boss_id FROM boss WHERE boss_id = $1',
    43204163                [personalId],
    43214164                (err, boss) => {
    … …  
    43404183
    43414184                        database.database.get(
    4342                             'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4185                            'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    43434186                            [personalId, storeId],
    43444187                            (err, bossStore) => {
    … …  
    43504193
    43514194                                database.database.get(
    4352                                     'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4195                                    'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    43534196                                    [employeeId, storeId],
    43544197                                    (err, employeeStore) => {
    … …  
    43744217
    43754218                                        database.database.run(
    4376                                             'UPDATE permissions SET type = ?, authorisation = ? WHERE personal_id = ?',
     4219                                            'UPDATE permissions SET type = $1, authorisation = $2 WHERE personal_id = $3',
    43774220                                            [permissionType, authorization, employeeId],
    43784221                                            function(err) {
    … …  
    44234266
    44244267            database.database.get(
    4425                 'SELECT boss_id FROM boss WHERE boss_id = ?',
     4268                'SELECT boss_id FROM boss WHERE boss_id = $1',
    44264269                [personalId],
    44274270                (err, boss) => {
    … …  
    44464289
    44474290                        database.database.get(
    4448                             'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4291                            'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    44494292                            [personalId, storeId],
    44504293                            (err, bossStore) => {
    … …  
    44564299
    44574300                                database.database.get(
    4458                                     'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4301                                    'SELECT personal_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    44594302                                    [employeeId, storeId],
    44604303                                    (err, employeeStore) => {
    … …  
    44694312
    44704313                                        if (firstName) {
    4471                                             updates.push('first_name = ?');
     4314                                            updates.push(`first_name = $${params.length + 1}`);
    44724315                                            params.push(firstName);
    44734316                                        }
    44744317
    44754318                                        if (lastName) {
    4476                                             updates.push('last_name = ?');
     4319                                            updates.push(`last_name = $${params.length + 1}`);
    44774320                                            params.push(lastName);
    44784321                                        }
    … …  
    44844327                                                return;
    44854328                                            }
    4486                                             updates.push('email = ?');
     4329                                            updates.push(`email = $${params.length + 1}`);
    44874330                                            params.push(email);
    44884331                                        }
    … …  
    44974340
    44984341                                        database.database.run(
    4499                                             `UPDATE personal SET ${updates.join(', ')} WHERE id = ?`,
     4342                                            `UPDATE personal SET ${updates.join(', ')} WHERE id = $${params.length}`,
    45004343                                            params,
    45014344                                            function(err) {
    … …  
    45104353                                                if (email) {
    45114354                                                    database.database.run(
    4512                                                         'UPDATE users SET email = ? WHERE id = ?',
     4355                                                        'UPDATE users SET email = $1 WHERE id = $2',
    45134356                                                        [email, employeeId],
    45144357                                                        (err) => {
    … …  
    45224365                                                if (firstName || lastName) {
    45234366                                                    database.database.get(
    4524                                                         'SELECT first_name, last_name FROM personal WHERE id = ?',
     4367                                                        'SELECT first_name, last_name FROM personal WHERE id = $1',
    45254368                                                        [employeeId],
    45264369                                                        (err, personal) => {
    … …  
    45284371                                                                const newUsername = `${personal.first_name} ${personal.last_name}`;
    45294372                                                                database.database.run(
    4530                                                                     'UPDATE users SET username = ? WHERE id = ?',
     4373                                                                    'UPDATE users SET username = $1 WHERE id = $2',
    45314374                                                                    [newUsername, employeeId],
    45324375                                                                    (err) => {
    … …  
    45664409            if (!storeId) {
    45674410                database.database.get(
    4568                     'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
     4411                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
    45694412                    [personalId],
    45704413                    (err, store) => {
    … …  
    45914434
    45924435            database.database.get(
    4593                 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4436                'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    45944437                [personalId, storeId],
    45954438                (err, ownsStore) => {
    … …  
    46204463            if (!storeId) {
    46214464                database.database.get(
    4622                     'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
     4465                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
    46234466                    [personalId],
    46244467                    (err, store) => {
    … …  
    46454488
    46464489            database.database.get(
    4647                 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4490                'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    46484491                [personalId, storeId],
    46494492                (err, ownsStore) => {
    … …  
    46744517            if (!storeId) {
    46754518                database.database.get(
    4676                     'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
     4519                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
    46774520                    [personalId],
    46784521                    (err, store) => {
    … …  
    46994542
    47004543            database.database.get(
    4701                 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4544                'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    47024545                [personalId, storeId],
    47034546                (err, ownsStore) => {
    … …  
    47284571            if (!storeId) {
    47294572                database.database.get(
    4730                     'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
     4573                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
    47314574                    [personalId],
    47324575                    (err, store) => {
    … …  
    47534596
    47544597            database.database.get(
    4755                 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4598                'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    47564599                [personalId, storeId],
    47574600                (err, ownsStore) => {
    … …  
    47824625            if (!storeId) {
    47834626                database.database.get(
    4784                     'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
     4627                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 LIMIT 1',
    47854628                    [personalId],
    47864629                    (err, store) => {
    … …  
    48074650
    48084651            database.database.get(
    4809                 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4652                'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    48104653                [personalId, storeId],
    48114654                (err, ownsStore) => {
    … …  
    49084751
    49094752                database.database.get(
    4910                     'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4753                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    49114754                    [personalId, storeId],
    49124755                    (err, ownsStore) => {
    … …  
    49774820
    49784821                database.database.get(
    4979                     'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
     4822                    'SELECT store_id FROM works_in_store WHERE personal_id = $1 AND store_id = $2',
    49804823                    [personalId, storeId],
    49814824                    (err, ownsStore) => {
    … …  
    49894832
    49904833                        database.database.run(
    4991                             'INSERT INTO report (id, store_id, period, start_date, end_date, type, generated_by, generated_at) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)',
     4834                            'INSERT INTO report (id, store_id, period, start_date, end_date, type, generated_by, generated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, CURRENT_TIMESTAMP)',
    49924835                            [reportId, storeId, period, startDate, endDate, type, personalId],
    49934836                            function(err) {
Note: See TracChangeset for help on using the changeset viewer.