- Timestamp:
- 09/19/26 10:30:30 (11 days ago)
- Branches:
- finki-main, main
- Children:
- 06ebe74
- Parents:
- 62b2964
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
server.js
r62b2964 r33517cc 1 1 const http = require('http'); 2 2 const url = require('url'); 3 const database = require('./database.js');3 const { Pool } = require('pg'); 4 4 const fs = require('fs'); 5 5 const path = require('path'); … … 211 211 } 212 212 213 // ===== FIXED: requireAuth function to check both sessions and tempAdminSessions =====214 213 function requireAuth(req, res, callback) { 215 214 const cookies = parseCookies(req); 216 215 const sessionId = cookies.sessionId; 217 216 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)) { 225 218 res.writeHead(302, { 'Location': '/login.html' }); 226 219 res.end(); … … 228 221 } 229 222 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 239 225 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')) { 258 227 res.writeHead(302, { 'Location': '/change-password.html?forced=true' }); 259 228 res.end(); 260 229 return; 261 230 } 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); 271 234 } 272 235 … … 355 318 356 319 database.database.get( 357 'SELECT boss_id FROM boss WHERE boss_id = ?',320 'SELECT boss_id FROM boss WHERE boss_id = $1', 358 321 [personalId], 359 322 (err, boss) => { … … 376 339 } 377 340 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 343 const 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 354 let transactionClient = null; 355 356 function 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)); 460 361 } 461 362 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(); 363 const 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); 472 392 return; 473 393 } 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); 581 402 }); 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; 585 409 } 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 }); 627 422 return; 628 423 } 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; 634 428 } 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 } 637 450 }); 638 451 } 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, 663 465 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, 680 668 username VARCHAR(100) UNIQUE NOT NULL, 681 669 email VARCHAR(255) UNIQUE NOT NULL, 682 670 password VARCHAR(255) NOT NULL, 683 671 user_type VARCHAR(50) NOT NULL, 684 force_password_change INTEGER DEFAULT 0,672 force_password_change BOOLEAN DEFAULT FALSE, 685 673 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), 819 688 action VARCHAR(100) NOT NULL, 820 689 resource_type VARCHAR(50), … … 823 692 ip_address VARCHAR(45), 824 693 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'] 870 719 ]; 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 ); 894 725 } 895 726 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'] 926 942 ]; 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 } 944 948 } 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 ); 988 1015 } 989 resolve(); 990 }); 991 } 1016 } 1017 ); 992 1018 } 993 1019 ); 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 () => { 1119 1148 try { 1120 await initializeDatabase();1149 await database.initializeDatabase(); 1121 1150 console.log('✅ Database initialization completed'); 1122 1151 } catch (err) { 1123 1152 console.error('❌ Database initialization failed:', err); 1153 process.exitCode = 1; 1124 1154 } 1125 1155 })(); … … 1154 1184 const sessionId = cookies.sessionId; 1155 1185 1156 if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {1186 if (!sessionId || !sessions.has(sessionId)) { 1157 1187 res.writeHead(302, { 'Location': '/login.html' }); 1158 1188 res.end(); … … 1176 1206 const sessionId = cookies.sessionId; 1177 1207 1178 if (!sessionId || (!sessions.has(sessionId) && !tempAdminSessions.has(sessionId))) {1208 if (!sessionId || !sessions.has(sessionId)) { 1179 1209 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' });1186 1210 res.end(); 1187 1211 return; … … 1201 1225 1202 1226 database.database.get( 1203 'SELECT boss_id FROM boss WHERE boss_id = ?',1227 'SELECT boss_id FROM boss WHERE boss_id = $1', 1204 1228 [personalId], 1205 1229 (err, boss) => { … … 1222 1246 serveStaticFile(res, 'admin.html', 'text/html'); 1223 1247 } 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'); 1282 1249 } 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'); 1353 1251 } 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'); 1397 1253 } else if (pathname === '/products.html') { 1398 1254 serveStaticFile(res, 'products.html', 'text/html'); … … 1591 1447 1592 1448 database.database.get( 1593 'SELECT store_id FROM store WHERE store_email = ?',1449 'SELECT store_id FROM store WHERE store_email = $1', 1594 1450 [formData.storeEmail], 1595 1451 (err, existingStore) => { … … 1971 1827 // Insert into store table (store_id is VARCHAR) 1972 1828 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)', 1974 1830 [ 1975 1831 tempStoreData.storeId, … … 1991 1847 // Insert into personal table (id is VARCHAR) 1992 1848 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)', 1994 1850 [ 1995 1851 tempStoreData.personalId, … … 2020 1876 // Insert into boss table (boss_id is VARCHAR, references personal.id) 2021 1877 database.database.run( 2022 'INSERT INTO boss (boss_id, signature) VALUES ( ?, ?)',1878 'INSERT INTO boss (boss_id, signature) VALUES ($1, $2)', 2023 1879 [tempStoreData.personalId, tempStoreData.signature], 2024 1880 (err) => { … … 2033 1889 // Insert into works_in_store table (personal_id is VARCHAR, store_id is VARCHAR) 2034 1890 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)', 2036 1892 [tempStoreData.personalId, tempStoreData.storeId], 2037 1893 (err) => { … … 2046 1902 // Insert into permissions table (personal_id is VARCHAR) 2047 1903 database.database.run( 2048 'INSERT INTO permissions (personal_id, type, authorisation) VALUES ( ?, ?, ?)',1904 'INSERT INTO permissions (personal_id, type, authorisation) VALUES ($1, $2, $3)', 2049 1905 [tempStoreData.personalId, 'BOSS', 'full_access'], 2050 1906 (err) => { … … 2055 1911 // Also create entry in users table for login with force_password_change = 1 2056 1912 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)', 2058 1914 [ 2059 1915 tempStoreData.personalId, … … 2148 2004 if (tempUserData.address && tempUserData.city && tempUserData.postcode && tempUserData.country) { 2149 2005 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)', 2151 2007 [ 2152 2008 clientId, … … 2322 2178 res.writeHead(200, { 2323 2179 '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` 2325 2181 }); 2326 2182 … … 2369 2225 // Check if this is a boss (store owner) 2370 2226 database.database.get( 2371 'SELECT boss_id FROM boss WHERE boss_id = ?',2227 'SELECT boss_id FROM boss WHERE boss_id = $1', 2372 2228 [personal.id], 2373 2229 (err, boss) => { … … 2380 2236 // Check if first time login from users table 2381 2237 database.database.get( 2382 'SELECT force_password_change FROM users WHERE email = ?',2238 'SELECT force_password_change FROM users WHERE email = $1', 2383 2239 [email], 2384 2240 (err, user) => { … … 2430 2286 // Check if this is an employee 2431 2287 database.database.get( 2432 'SELECT employee_id FROM employees WHERE employee_id = ?',2288 'SELECT employee_id FROM employees WHERE employee_id = $1', 2433 2289 [personal.id], 2434 2290 (err, employee) => { … … 2440 2296 // This is an employee 2441 2297 database.database.get( 2442 'SELECT force_password_change FROM users WHERE email = ?',2298 'SELECT force_password_change FROM users WHERE email = $1', 2443 2299 [email], 2444 2300 (err, user) => { … … 2491 2347 // Treat as regular user 2492 2348 database.database.get( 2493 'SELECT * FROM users WHERE email = ?',2349 'SELECT * FROM users WHERE email = $1', 2494 2350 [email], 2495 2351 (err, user) => { … … 2633 2489 2634 2490 database.database.get( 2635 'SELECT * FROM users WHERE email = ?',2491 'SELECT * FROM users WHERE email = $1', 2636 2492 [email], 2637 2493 (err, user) => { … … 2716 2572 } 2717 2573 2718 // ===== FIXED: /api/verify-2fa endpoint with proper redirect handling =====2719 2574 else if (pathname === '/api/verify-2fa' && req.method === 'POST') { 2720 2575 let body = ''; … … 2756 2611 verificationCodes.delete(email); 2757 2612 2758 // Determine redirect based on user type - all go to change-password.html with appropriate query parameters2613 // Determine redirect based on user type 2759 2614 let redirectTo = 'change-password.html?forced=true'; 2760 2761 // Add redirect parameter to know where to go after password change2762 2615 if (verificationData.userType === 'store_owner') { 2763 2616 redirectTo = 'change-password.html?forced=true&redirect=store-owner.html'; … … 2768 2621 } else if (verificationData.userType === 'client') { 2769 2622 redirectTo = 'change-password.html?forced=true&redirect=client-dashboard.html'; 2770 } else {2771 redirectTo = 'change-password.html?forced=true&redirect=dashboard.html';2772 2623 } 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`);2777 2624 2778 2625 res.writeHead(200, { … … 2829 2676 } 2830 2677 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}`); 2833 2679 2834 2680 res.writeHead(200, { 2835 2681 '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` 2837 2683 }); 2838 2684 … … 2851 2697 2852 2698 if (sessionId) { 2853 const userId = sessions.get(sessionId) || tempAdminSessions.get(sessionId);2699 const userId = sessions.get(sessionId); 2854 2700 if (userId) { 2855 2701 database.logAudit(userId, 'LOGOUT', 'auth', userId.toString(), 'User logged out', ipAddress); … … 2872 2718 const sessionId = cookies.sessionId; 2873 2719 2874 // Check if this is a temp session2875 2720 if (tempAdminSessions.has(sessionId)) { 2876 2721 // This is a temporary session (password change required) … … 2895 2740 // Personal user (store owner/employee) 2896 2741 database.database.get( 2897 'SELECT boss_id FROM boss WHERE boss_id = ?',2742 'SELECT boss_id FROM boss WHERE boss_id = $1', 2898 2743 [userId], 2899 2744 (err, boss) => { … … 2996 2841 2997 2842 database.database.get( 2998 'SELECT boss_id FROM boss WHERE boss_id = ?',2843 'SELECT boss_id FROM boss WHERE boss_id = $1', 2999 2844 [personalId], 3000 2845 (err, boss) => { … … 3006 2851 database.database.all( 3007 2852 `SELECT s.* FROM store s 3008 JOIN works_in_store w ON s.store_id = w.store_id3009 WHERE w.personal_id = ?`,2853 JOIN works_in_store w ON s.store_id = w.store_id 2854 WHERE w.personal_id = $1`, 3010 2855 [personalId], 3011 2856 (err, stores) => { … … 3031 2876 } else { 3032 2877 database.database.get( 3033 'SELECT employee_id FROM employees WHERE employee_id = ?',2878 'SELECT employee_id FROM employees WHERE employee_id = $1', 3034 2879 [personalId], 3035 2880 (err, employee) => { … … 3041 2886 database.database.all( 3042 2887 `SELECT s.* FROM store s 3043 JOIN works_in_store w ON s.store_id = w.store_id3044 WHERE w.personal_id = ?`,2888 JOIN works_in_store w ON s.store_id = w.store_id 2889 WHERE w.personal_id = $1`, 3045 2890 [personalId], 3046 2891 (err, stores) => { … … 3232 3077 3233 3078 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', 3235 3080 [storeId, new Date().getFullYear().toString()], 3236 3081 (err, result) => { … … 3363 3208 3364 3209 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', 3366 3211 [storeId, now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')], 3367 3212 (err, result) => { … … 3421 3266 3422 3267 database.database.get( 3423 'SELECT store_id FROM "order" WHERE order_num = ?',3268 'SELECT store_id FROM "order" WHERE order_num = $1', 3424 3269 [refundData.order_num], 3425 3270 (err, result) => { … … 3436 3281 3437 3282 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', 3439 3284 [now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')], 3440 3285 (err, result) => { … … 3486 3331 3487 3332 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', 3489 3334 [personalId], 3490 3335 (err, bossStore) => { … … 3504 3349 3505 3350 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', 3507 3352 [personalId, storeId], 3508 3353 (err, ownsStore) => { … … 3513 3358 } 3514 3359 3515 // FIXED: Changed SQL syntax from SUBSTRING(code FROM 4) to SUBSTR(code, 4) for SQLite compatibility3360 // PostgreSQL: extract the numeric product sequence from the store-prefixed product code 3516 3361 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', 3518 3363 [storeId], 3519 3364 (err, result) => { … … 3584 3429 3585 3430 database.database.get( 3586 'SELECT store_id FROM product WHERE code = ?',3431 'SELECT store_id FROM product WHERE code = $1', 3587 3432 [productData.code], 3588 3433 (err, product) => { … … 3594 3439 3595 3440 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', 3597 3442 [personalId, product.store_id], 3598 3443 (err, ownsStore) => { … … 3721 3566 3722 3567 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', 3724 3569 [hashedPassword, userId], 3725 3570 function(err) { … … 3733 3578 // Also update password in personal table if it exists (for admin) 3734 3579 database.database.run( 3735 'UPDATE personal SET password = ? WHERE id = ?',3580 'UPDATE personal SET password = $1 WHERE id = $2', 3736 3581 [hashedPassword, userId], 3737 3582 function(err) { … … 3772 3617 } 3773 3618 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}`); 3776 3620 3777 3621 database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(), 3778 3622 `${user.user_type || 'user'} forced password change completed`, ipAddress); 3779 3623 3780 // Set the cookie with proper options - extended to 24 hours3624 // Set the cookie with proper options 3781 3625 res.writeHead(200, { 3782 3626 '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 3784 3628 }); 3785 3629 … … 3808 3652 3809 3653 database.database.run( 3810 'UPDATE personal SET password = ? WHERE id = ?',3654 'UPDATE personal SET password = $1 WHERE id = $2', 3811 3655 [hashedPassword, userId], 3812 3656 function(err) { … … 3820 3664 // Also update in users table if exists 3821 3665 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', 3823 3667 [hashedPassword, personal.email], 3824 3668 function(err) { … … 3831 3675 // Determine user type (boss/owner or employee) 3832 3676 database.database.get( 3833 'SELECT boss_id FROM boss WHERE boss_id = ?',3677 'SELECT boss_id FROM boss WHERE boss_id = $1', 3834 3678 [userId], 3835 3679 (err, boss) => { … … 3849 3693 sessions.set(newSessionId, `personal_${userId}`); 3850 3694 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}`); 3853 3696 3854 3697 database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(), … … 3908 3751 3909 3752 database.database.get( 3910 'SELECT boss_id FROM boss WHERE boss_id = ?',3753 'SELECT boss_id FROM boss WHERE boss_id = $1', 3911 3754 [personalId], 3912 3755 (err, boss) => { … … 4014 3857 4015 3858 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)', 4017 3860 [ 4018 3861 newPersonalId, … … 4042 3885 4043 3886 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)', 4045 3888 [newPersonalId, dateOfHire], 4046 3889 (err) => { … … 4054 3897 4055 3898 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)', 4057 3900 [newPersonalId, storeId], 4058 3901 (err) => { … … 4066 3909 4067 3910 database.database.run( 4068 'INSERT INTO permissions (personal_id, type, authorisation) VALUES ( ?, ?, ?)',3911 'INSERT INTO permissions (personal_id, type, authorisation) VALUES ($1, $2, $3)', 4069 3912 [newPersonalId, 'EMPLOYEE', 'limited_access'], 4070 3913 (err) => { … … 4075 3918 // Also create entry in users table for login with force_password_change = 1 4076 3919 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)', 4078 3921 [ 4079 3922 newPersonalId, … … 4148 3991 4149 3992 database.database.get( 4150 'SELECT boss_id FROM boss WHERE boss_id = ?',3993 'SELECT boss_id FROM boss WHERE boss_id = $1', 4151 3994 [personalId], 4152 3995 (err, boss) => { … … 4171 4014 4172 4015 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', 4174 4017 [personalId, storeId], 4175 4018 (err, bossStore) => { … … 4181 4024 4182 4025 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', 4184 4027 [employeeId, storeId], 4185 4028 (err, employeeStore) => { … … 4191 4034 4192 4035 database.database.get( 4193 'SELECT boss_id FROM boss WHERE boss_id = ?',4036 'SELECT boss_id FROM boss WHERE boss_id = $1', 4194 4037 [employeeId], 4195 4038 (err, isBoss) => { … … 4213 4056 4214 4057 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', 4216 4059 [employeeId, storeId], 4217 4060 (err) => { … … 4225 4068 4226 4069 database.database.run( 4227 'DELETE FROM employees WHERE employee_id = ?',4070 'DELETE FROM employees WHERE employee_id = $1', 4228 4071 [employeeId], 4229 4072 (err) => { … … 4233 4076 4234 4077 database.database.run( 4235 'DELETE FROM permissions WHERE personal_id = ?',4078 'DELETE FROM permissions WHERE personal_id = $1', 4236 4079 [employeeId], 4237 4080 (err) => { … … 4241 4084 4242 4085 database.database.run( 4243 'DELETE FROM personal WHERE id = ?',4086 'DELETE FROM personal WHERE id = $1', 4244 4087 [employeeId], 4245 4088 (err) => { … … 4250 4093 // Also delete from users table 4251 4094 database.database.run( 4252 'DELETE FROM users WHERE id = ?',4095 'DELETE FROM users WHERE id = $1', 4253 4096 [employeeId], 4254 4097 (err) => { … … 4317 4160 4318 4161 database.database.get( 4319 'SELECT boss_id FROM boss WHERE boss_id = ?',4162 'SELECT boss_id FROM boss WHERE boss_id = $1', 4320 4163 [personalId], 4321 4164 (err, boss) => { … … 4340 4183 4341 4184 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', 4343 4186 [personalId, storeId], 4344 4187 (err, bossStore) => { … … 4350 4193 4351 4194 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', 4353 4196 [employeeId, storeId], 4354 4197 (err, employeeStore) => { … … 4374 4217 4375 4218 database.database.run( 4376 'UPDATE permissions SET type = ?, authorisation = ? WHERE personal_id = ?',4219 'UPDATE permissions SET type = $1, authorisation = $2 WHERE personal_id = $3', 4377 4220 [permissionType, authorization, employeeId], 4378 4221 function(err) { … … 4423 4266 4424 4267 database.database.get( 4425 'SELECT boss_id FROM boss WHERE boss_id = ?',4268 'SELECT boss_id FROM boss WHERE boss_id = $1', 4426 4269 [personalId], 4427 4270 (err, boss) => { … … 4446 4289 4447 4290 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', 4449 4292 [personalId, storeId], 4450 4293 (err, bossStore) => { … … 4456 4299 4457 4300 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', 4459 4302 [employeeId, storeId], 4460 4303 (err, employeeStore) => { … … 4469 4312 4470 4313 if (firstName) { 4471 updates.push( 'first_name = ?');4314 updates.push(`first_name = $${params.length + 1}`); 4472 4315 params.push(firstName); 4473 4316 } 4474 4317 4475 4318 if (lastName) { 4476 updates.push( 'last_name = ?');4319 updates.push(`last_name = $${params.length + 1}`); 4477 4320 params.push(lastName); 4478 4321 } … … 4484 4327 return; 4485 4328 } 4486 updates.push( 'email = ?');4329 updates.push(`email = $${params.length + 1}`); 4487 4330 params.push(email); 4488 4331 } … … 4497 4340 4498 4341 database.database.run( 4499 `UPDATE personal SET ${updates.join(', ')} WHERE id = ?`,4342 `UPDATE personal SET ${updates.join(', ')} WHERE id = $${params.length}`, 4500 4343 params, 4501 4344 function(err) { … … 4510 4353 if (email) { 4511 4354 database.database.run( 4512 'UPDATE users SET email = ? WHERE id = ?',4355 'UPDATE users SET email = $1 WHERE id = $2', 4513 4356 [email, employeeId], 4514 4357 (err) => { … … 4522 4365 if (firstName || lastName) { 4523 4366 database.database.get( 4524 'SELECT first_name, last_name FROM personal WHERE id = ?',4367 'SELECT first_name, last_name FROM personal WHERE id = $1', 4525 4368 [employeeId], 4526 4369 (err, personal) => { … … 4528 4371 const newUsername = `${personal.first_name} ${personal.last_name}`; 4529 4372 database.database.run( 4530 'UPDATE users SET username = ? WHERE id = ?',4373 'UPDATE users SET username = $1 WHERE id = $2', 4531 4374 [newUsername, employeeId], 4532 4375 (err) => { … … 4566 4409 if (!storeId) { 4567 4410 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', 4569 4412 [personalId], 4570 4413 (err, store) => { … … 4591 4434 4592 4435 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', 4594 4437 [personalId, storeId], 4595 4438 (err, ownsStore) => { … … 4620 4463 if (!storeId) { 4621 4464 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', 4623 4466 [personalId], 4624 4467 (err, store) => { … … 4645 4488 4646 4489 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', 4648 4491 [personalId, storeId], 4649 4492 (err, ownsStore) => { … … 4674 4517 if (!storeId) { 4675 4518 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', 4677 4520 [personalId], 4678 4521 (err, store) => { … … 4699 4542 4700 4543 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', 4702 4545 [personalId, storeId], 4703 4546 (err, ownsStore) => { … … 4728 4571 if (!storeId) { 4729 4572 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', 4731 4574 [personalId], 4732 4575 (err, store) => { … … 4753 4596 4754 4597 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', 4756 4599 [personalId, storeId], 4757 4600 (err, ownsStore) => { … … 4782 4625 if (!storeId) { 4783 4626 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', 4785 4628 [personalId], 4786 4629 (err, store) => { … … 4807 4650 4808 4651 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', 4810 4653 [personalId, storeId], 4811 4654 (err, ownsStore) => { … … 4908 4751 4909 4752 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', 4911 4754 [personalId, storeId], 4912 4755 (err, ownsStore) => { … … 4977 4820 4978 4821 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', 4980 4823 [personalId, storeId], 4981 4824 (err, ownsStore) => { … … 4989 4832 4990 4833 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)', 4992 4835 [reportId, storeId, period, startDate, endDate, type, personalId], 4993 4836 function(err) {
Note:
See TracChangeset
for help on using the changeset viewer.
