source: server.js@ 79fff4f

finki-main main
Last change on this file since 79fff4f was 79fff4f, checked in by Klimentina Efremova <klimentina08642@โ€ฆ>, 7 months ago

Improved employee logging in and changing password

  • Property mode set to 100644
File size: 248.7 KB
Lineย 
1const http = require('http');
2const url = require('url');
3const database = require('./database.js');
4const fs = require('fs');
5const path = require('path');
6const crypto = require('crypto');
7const nodemailer = require('nodemailer');
8const bcrypt = require('bcryptjs');
9require('dotenv').config();
10
11const port = process.env.PORT || 3000;
12
13const sessions = new Map();
14const verificationCodes = new Map();
15const tempUsers = new Map();
16const tempAdminSessions = new Map();
17const tempStoreRegistrations = new Map();
18
19console.log('๐Ÿ”ง Starting Handcraft Marketplace Server...');
20console.log('๐ŸŽจ Colors: Royal Blue & Pink Theme');
21
22let emailTransporter;
23
24if (process.env.SMTP_USER && process.env.SMTP_PASS) {
25 const emailConfig = {
26 host: process.env.SMTP_HOST || 'smtp.gmail.com',
27 port: parseInt(process.env.SMTP_PORT) || 587,
28 secure: false,
29 auth: {
30 user: process.env.SMTP_USER,
31 pass: process.env.SMTP_PASS
32 }
33 };
34
35 emailTransporter = nodemailer.createTransport(emailConfig);
36
37 emailTransporter.verify(function(error, success) {
38 if (error) {
39 console.log('โŒ Email configuration failed:', error.message);
40 console.log('๐Ÿ“ง Falling back to console display for verification codes');
41 emailTransporter = createMockTransporter();
42 } else {
43 console.log('โœ… Email server is ready to send real emails!');
44 }
45 });
46} else {
47 console.log('๐Ÿ“ง No email credentials found. Verification codes will be shown in console.');
48 emailTransporter = createMockTransporter();
49}
50
51function createMockTransporter() {
52 return {
53 sendMail: function(mailOptions) {
54 return new Promise((resolve, reject) => {
55 const codeMatch = mailOptions.html.match(/\b\d{6}\b/);
56 const code = codeMatch ? codeMatch[0] : 'unknown';
57
58 console.log('');
59 console.log('๐ŸŽฏ ===== VERIFICATION CODE =====');
60 console.log('๐Ÿ“ง For:', mailOptions.to);
61 console.log('๐Ÿ” CODE:', code);
62 console.log('โฐ Expires in: 30 seconds');
63 console.log('๐Ÿ“ Use this code to continue');
64 console.log('================================');
65 console.log('');
66
67 resolve({ messageId: 'dev-' + Date.now() });
68 });
69 }
70 };
71}
72
73function sendVerificationEmail(toEmail, code) {
74 const mailOptions = {
75 from: process.env.SMTP_USER || 'noreply@handcraft-marketplace.com',
76 to: toEmail,
77 subject: 'Your Verification Code - Handcraft Marketplace',
78 html: `
79 <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; background: linear-gradient(135deg, #4169E1 0%, #FF69B4 100%); color: white; padding: 20px; border-radius: 10px;">
80 <h2 style="text-align: center;">๐ŸŽจ Handcraft Marketplace</h2>
81 <div style="background-color: white; color: #333; padding: 20px; border-radius: 8px; margin: 20px 0;">
82 <h3 style="color: #4169E1;">Account Verification</h3>
83 <p>Your verification code is:</p>
84 <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; text-align: center; font-size: 24px; font-weight: bold; letter-spacing: 5px; margin: 20px 0; color: #4169E1;">
85 ${code}
86 </div>
87 <p style="color: #e74c3c; font-weight: bold;">โš ๏ธ This code will expire in 30 seconds</p>
88 <p style="color: #666; font-size: 12px;">If you didn't request this verification, please ignore this email.</p>
89 </div>
90 </div>`
91 };
92
93 console.log('');
94 console.log('๐ŸŽฏ ===== VERIFICATION CODE FOR TESTING =====');
95 console.log('๐Ÿ“ง Email:', toEmail);
96 console.log('๐Ÿ” CODE:', code);
97 console.log('โฐ Expires in: 30 seconds');
98 console.log('==========================================');
99 console.log('');
100
101 return emailTransporter.sendMail(mailOptions);
102}
103
104function send2FACode(toEmail, code) {
105 const mailOptions = {
106 from: process.env.SMTP_USER || 'noreply@handcraft-marketplace.com',
107 to: toEmail,
108 subject: 'Your 2FA Code - Handcraft Marketplace',
109 html: `
110 <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; background: linear-gradient(135deg, #4169E1 0%, #FF69B4 100%); color: white; padding: 20px; border-radius: 10px;">
111 <h2 style="text-align: center;">๐ŸŽจ Handcraft Marketplace</h2>
112 <div style="background-color: white; color: #333; padding: 20px; border-radius: 8px; margin: 20px 0;">
113 <h3 style="color: #4169E1;">Two-Factor Authentication</h3>
114 <p>Your login verification code is:</p>
115 <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; text-align: center; font-size: 24px; font-weight: bold; letter-spacing: 5px; margin: 20px 0; color: #4169E1;">
116 ${code}
117 </div>
118 <p style="color: #e74c3c; font-weight: bold;">โš ๏ธ This code will expire in 30 seconds</p>
119 <p style="color: #666; font-size: 12px;">If you're not trying to login, please secure your account immediately.</p>
120 </div>
121 </div>`
122 };
123
124 console.log('');
125 console.log('๐ŸŽฏ ===== 2FA CODE FOR TESTING =====');
126 console.log('๐Ÿ“ง Email:', toEmail);
127 console.log('๐Ÿ” CODE:', code);
128 console.log('โฐ Expires in: 30 seconds');
129 console.log('==================================');
130 console.log('');
131
132 return emailTransporter.sendMail(mailOptions);
133}
134
135function sendStoreRegistrationEmail(toEmail, code, storeName) {
136 const mailOptions = {
137 from: process.env.SMTP_USER || 'noreply@handcraft-marketplace.com',
138 to: toEmail,
139 subject: 'Store Registration Verification - Handcraft Marketplace',
140 html: `
141 <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; background: linear-gradient(135deg, #4169E1 0%, #FF69B4 100%); color: white; padding: 20px; border-radius: 10px;">
142 <h2 style="text-align: center;">๐ŸŽจ Handcraft Marketplace</h2>
143 <div style="background-color: white; color: #333; padding: 20px; border-radius: 8px; margin: 20px 0;">
144 <h3 style="color: #4169E1;">Store Registration Verification</h3>
145 <p>Thank you for registering your store "<strong>${storeName}</strong>" on Handcraft Marketplace!</p>
146 <p>Your verification code is:</p>
147 <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; text-align: center; font-size: 24px; font-weight: bold; letter-spacing: 5px; margin: 20px 0; color: #4169E1;">
148 ${code}
149 </div>
150 <p style="color: #e74c3c; font-weight: bold;">โš ๏ธ This code will expire in 30 seconds</p>
151 <p style="color: #666; font-size: 12px;">If you didn't request this verification, please ignore this email.</p>
152 </div>
153 </div>`
154 };
155
156 console.log('');
157 console.log('๐ŸŽฏ ===== STORE REGISTRATION VERIFICATION CODE =====');
158 console.log('๐Ÿ“ง For:', toEmail);
159 console.log('๐Ÿช Store:', storeName);
160 console.log('๐Ÿ” CODE:', code);
161 console.log('โฐ Expires in: 30 seconds');
162 console.log('==================================================');
163 console.log('');
164
165 return emailTransporter.sendMail(mailOptions);
166}
167
168function generateVerificationCode() {
169 let code = '';
170 for(let i = 0; i < 6; i++) {
171 code += crypto.randomInt(0, 9);
172 }
173 return code;
174}
175
176function generateSessionId() {
177 return crypto.randomBytes(32).toString('hex');
178}
179
180function serveStaticFile(res, filePath, contentType) {
181 const fullPath = path.join(__dirname, 'interfejs', filePath);
182 fs.readFile(fullPath, (err, data) => {
183 if (err) {
184 console.error('File not found:', fullPath, err);
185 res.writeHead(404, { 'Content-Type': 'text/plain' });
186 res.end('File not found');
187 } else {
188 res.writeHead(200, { 'Content-Type': contentType });
189 res.end(data);
190 }
191 });
192}
193
194function parseCookies(req) {
195 const cookieHeader = req.headers.cookie;
196 const cookies = {};
197 if (cookieHeader) {
198 cookieHeader.split(';').forEach(cookie => {
199 const parts = cookie.split('=');
200 cookies[parts[0].trim()] = parts[1]?.trim();
201 });
202 }
203 return cookies;
204}
205
206function getClientIp(req) {
207 return req.headers['x-forwarded-for'] ||
208 req.connection.remoteAddress ||
209 req.socket.remoteAddress ||
210 (req.connection.socket ? req.connection.socket.remoteAddress : null);
211}
212
213function requireAuth(req, res, callback) {
214 const cookies = parseCookies(req);
215 const sessionId = cookies.sessionId;
216
217 if (!sessionId || !sessions.has(sessionId)) {
218 res.writeHead(302, { 'Location': '/login.html' });
219 res.end();
220 return;
221 }
222
223 const userId = sessions.get(sessionId);
224
225 if (tempAdminSessions.has(sessionId)) {
226 if (!req.url.includes('/change-password') && !req.url.includes('/api/force-change-password')) {
227 res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
228 res.end();
229 return;
230 }
231 }
232
233 callback(userId);
234}
235
236function requireRole(roleName) {
237 return function(req, res, callback) {
238 requireAuth(req, res, (userId) => {
239 database.getUserById(userId, (err, user) => {
240 if (err || !user) {
241 res.writeHead(403, { 'Content-Type': 'application/json' });
242 res.end(JSON.stringify({ success: false, message: 'Access denied' }));
243 return;
244 }
245
246 const hasRole = user.roles && user.roles.some(role => role.name === roleName);
247
248 if (!hasRole) {
249 res.writeHead(403, { 'Content-Type': 'application/json' });
250 res.end(JSON.stringify({ success: false, message: 'Insufficient permissions' }));
251 return;
252 }
253
254 callback(userId, user);
255 });
256 });
257 };
258}
259
260function validateEmail(email) {
261 const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
262 return emailRegex.test(email);
263}
264
265function validatePassword(password) {
266 const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
267 return passwordRegex.test(password);
268}
269
270function cleanupExpiredCodes() {
271 const now = Date.now();
272 let cleanedCount = 0;
273
274 for (const [key, data] of verificationCodes.entries()) {
275 if (now - data.timestamp > 30 * 1000) {
276 verificationCodes.delete(key);
277 cleanedCount++;
278 }
279 }
280
281 for (const [key, data] of tempUsers.entries()) {
282 if (now - data.timestamp > 30 * 1000) {
283 tempUsers.delete(key);
284 cleanedCount++;
285 }
286 }
287
288 for (const [key, data] of tempStoreRegistrations.entries()) {
289 if (now - data.timestamp > 30 * 1000) {
290 tempStoreRegistrations.delete(key);
291 cleanedCount++;
292 }
293 }
294
295 if (cleanedCount > 0) {
296 console.log(`๐Ÿงน Cleaned ${cleanedCount} expired verification codes`);
297 }
298}
299
300setInterval(cleanupExpiredCodes, 10 * 1000);
301
302function requireStoreOwner() {
303 return function(req, res, callback) {
304 requireAuth(req, res, (userId) => {
305 const userIdStr = String(userId);
306
307 // Check if this is the admin user (ID 000000)
308 if (userIdStr === '000000') {
309 // Admin is not a store owner
310 res.writeHead(403, { 'Content-Type': 'application/json' });
311 res.end(JSON.stringify({ success: false, message: 'Access denied - not a store owner' }));
312 return;
313 }
314
315 // Check if it's a personal user
316 if (userIdStr.startsWith('personal_')) {
317 const personalId = userIdStr.replace('personal_', '');
318
319 database.database.get(
320 'SELECT boss_id FROM boss WHERE boss_id = ?',
321 [personalId],
322 (err, boss) => {
323 if (err || !boss) {
324 res.writeHead(403, { 'Content-Type': 'application/json' });
325 res.end(JSON.stringify({ success: false, message: 'Access denied - not a store owner' }));
326 return;
327 }
328
329 callback(personalId);
330 }
331 );
332 } else {
333 // Not a personal user, so not a store owner
334 res.writeHead(403, { 'Content-Type': 'application/json' });
335 res.end(JSON.stringify({ success: false, message: 'Access denied - not a store owner' }));
336 }
337 });
338 };
339}
340
341// Database initialization function
342async function initializeDatabase() {
343 console.log('๐Ÿ” Checking database schema...');
344
345 // List of all required tables
346 const requiredTables = [
347 'client',
348 'store',
349 'category',
350 'users',
351 'personal',
352 'product',
353 'boss',
354 'employees',
355 'works_in_store',
356 'permissions',
357 'order',
358 'order_items',
359 'review',
360 'request',
361 'refund',
362 'report',
363 'audit_log',
364 'color',
365 'image',
366 'delivery_address',
367 'roles',
368 'user_roles'
369 ];
370
371 try {
372 // For SQLite, we need to use a different approach to check tables
373 const result = await new Promise((resolve, reject) => {
374 database.database.all(
375 "SELECT name FROM sqlite_master WHERE type='table'",
376 [],
377 (err, rows) => {
378 if (err) reject(err);
379 else resolve(rows || []);
380 }
381 );
382 });
383
384 const existingTables = result.map(row => row.name);
385 const missingTables = requiredTables.filter(table => !existingTables.includes(table));
386
387 if (missingTables.length > 0) {
388 console.log(`โš ๏ธ Missing tables: ${missingTables.join(', ')}`);
389 console.log('๐Ÿ”„ Recreating entire database...');
390
391 // Drop all tables in correct order (respecting foreign keys)
392 await dropAllTables();
393
394 // Create all tables
395 await createAllTables();
396
397 // Create indexes
398 await createIndexes();
399
400 // Insert initial data
401 await insertInitialData();
402
403 console.log('โœ… Database recreation completed');
404 } else {
405 console.log('โœ… All required tables exist');
406 // Even if tables exist, ensure admin user exists with ID 000000
407 await ensureAdminUser();
408 }
409 } catch (err) {
410 console.error('โŒ Error checking database schema:', err);
411 console.log('โš ๏ธ Attempting to recreate database anyway...');
412
413 try {
414 await dropAllTables();
415 await createAllTables();
416 await createIndexes();
417 await insertInitialData();
418 console.log('โœ… Database recreation completed');
419 } catch (createErr) {
420 console.error('โŒ Failed to recreate database:', createErr);
421 }
422 }
423}
424
425// Function to ensure admin user exists with ID 000000
426function ensureAdminUser() {
427 return new Promise((resolve) => {
428 database.database.get(
429 'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?',
430 ['000000', 'admin', 'admin@handcraft.com'],
431 (err, existingAdmin) => {
432 if (err) {
433 console.error('Error checking for existing admin:', err.message);
434 resolve();
435 return;
436 }
437
438 // Insert admin user if it doesn't exist
439 if (!existingAdmin) {
440 const adminId = '000000';
441 const adminPassword = bcrypt.hashSync('Admin123!', 10);
442
443 // Start a transaction
444 database.database.run('BEGIN TRANSACTION', (err) => {
445 if (err) {
446 console.error('Error beginning transaction:', err);
447 resolve();
448 return;
449 }
450
451 // Insert into users table
452 database.database.run(
453 `INSERT INTO users (id, username, email, password, user_type, force_password_change)
454 VALUES (?, ?, ?, ?, ?, ?)`,
455 [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1],
456 function(err) {
457 if (err) {
458 database.database.run('ROLLBACK');
459 console.error('Error inserting admin user:', err.message);
460 resolve();
461 return;
462 }
463
464 // Insert into personal table (required for boss table)
465 database.database.run(
466 `INSERT INTO personal (id, first_name, last_name, ssn, email, password)
467 VALUES (?, ?, ?, ?, ?, ?)`,
468 [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword],
469 function(err) {
470 if (err) {
471 database.database.run('ROLLBACK');
472 console.error('Error inserting admin personal:', err.message);
473 resolve();
474 return;
475 }
476
477 // Insert into boss table (store owner)
478 database.database.run(
479 `INSERT INTO boss (boss_id, signature)
480 VALUES (?, ?)`,
481 [adminId, 'Admin Signature'],
482 function(err) {
483 if (err) {
484 database.database.run('ROLLBACK');
485 console.error('Error inserting admin boss:', err.message);
486 resolve();
487 return;
488 }
489
490 // Insert into permissions
491 database.database.run(
492 `INSERT INTO permissions (personal_id, type, authorisation)
493 VALUES (?, ?, ?)`,
494 [adminId, 'ADMIN', 'full_access'],
495 function(err) {
496 if (err) {
497 console.error('Error inserting admin permissions:', err.message);
498 // Continue even if this fails
499 }
500
501 // Assign admin role
502 database.database.get(
503 'SELECT role_id FROM roles WHERE name = ?',
504 ['admin'],
505 (err, adminRole) => {
506 if (!err && adminRole) {
507 database.database.run(
508 'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)',
509 [adminId, adminRole.role_id],
510 (err) => {
511 if (err) {
512 console.error('Error assigning admin role:', err.message);
513 }
514 }
515 );
516 }
517
518 database.database.run('COMMIT', (commitErr) => {
519 if (commitErr) {
520 console.error('Error committing transaction:', commitErr);
521 database.database.run('ROLLBACK');
522 } else {
523 console.log('\n');
524 console.log('๐Ÿ” ===== ADMIN CREDENTIALS =====');
525 console.log('๐Ÿ†” ID: 000000');
526 console.log('๐Ÿ‘ค Username: admin');
527 console.log('๐Ÿ“ง Email: admin@handcraft.com');
528 console.log('๐Ÿ”‘ Password: Admin123!');
529 console.log('โš ๏ธ This is a first-time login. You will be required to change your password after 2FA verification.');
530 console.log('================================\n');
531 }
532 resolve();
533 });
534 }
535 );
536 }
537 );
538 }
539 );
540 }
541 );
542 }
543 );
544 });
545 } else {
546 console.log('โœ… Admin user already exists with ID:', existingAdmin.id);
547 resolve();
548 }
549 }
550 );
551 });
552}
553
554function dropAllTables() {
555 return new Promise((resolve, reject) => {
556 console.log('๐Ÿ—‘๏ธ Dropping all tables...');
557
558 // Drop in reverse order of creation (respect foreign keys)
559 const dropQueries = [
560 'DROP TABLE IF EXISTS user_roles',
561 'DROP TABLE IF EXISTS roles',
562 'DROP TABLE IF EXISTS delivery_address',
563 'DROP TABLE IF EXISTS image',
564 'DROP TABLE IF EXISTS color',
565 'DROP TABLE IF EXISTS audit_log',
566 'DROP TABLE IF EXISTS report',
567 'DROP TABLE IF EXISTS refund',
568 'DROP TABLE IF EXISTS request',
569 'DROP TABLE IF EXISTS review',
570 'DROP TABLE IF EXISTS order_items',
571 'DROP TABLE IF EXISTS "order"',
572 'DROP TABLE IF EXISTS permissions',
573 'DROP TABLE IF EXISTS works_in_store',
574 'DROP TABLE IF EXISTS employees',
575 'DROP TABLE IF EXISTS boss',
576 'DROP TABLE IF EXISTS product',
577 'DROP TABLE IF EXISTS personal',
578 'DROP TABLE IF EXISTS users',
579 'DROP TABLE IF EXISTS category',
580 'DROP TABLE IF EXISTS store',
581 'DROP TABLE IF EXISTS client'
582 ];
583
584 let index = 0;
585
586 function runNext() {
587 if (index >= dropQueries.length) {
588 console.log('โœ… All tables dropped');
589 resolve();
590 return;
591 }
592
593 database.database.run(dropQueries[index], [], (err) => {
594 if (err) {
595 console.error(`Error dropping table: ${err.message}`);
596 // Continue anyway
597 }
598 index++;
599 runNext();
600 });
601 }
602
603 runNext();
604 });
605}
606
607function createAllTables() {
608 return new Promise((resolve, reject) => {
609 console.log('๐Ÿ—๏ธ Creating tables...');
610
611 const createQueries = [
612 // Client table (SERIAL ID starting from 1000)
613 `CREATE TABLE IF NOT EXISTS client (
614 client_id INTEGER PRIMARY KEY AUTOINCREMENT,
615 first_name VARCHAR(100) NOT NULL,
616 last_name VARCHAR(100) NOT NULL,
617 email VARCHAR(255) UNIQUE NOT NULL,
618 password VARCHAR(255) NOT NULL,
619 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
620 )`,
621
622 // Store table (VARCHAR ID)
623 `CREATE TABLE IF NOT EXISTS store (
624 store_id VARCHAR(10) PRIMARY KEY,
625 name VARCHAR(255) NOT NULL,
626 date_of_founding DATE NOT NULL,
627 physical_address TEXT NOT NULL,
628 store_email VARCHAR(255) UNIQUE NOT NULL,
629 rating DECIMAL(3,2) DEFAULT 0.0
630 )`,
631
632 // Category table (SERIAL ID starting from 1)
633 `CREATE TABLE IF NOT EXISTS category (
634 category_id INTEGER PRIMARY KEY AUTOINCREMENT,
635 name VARCHAR(100) NOT NULL,
636 description TEXT,
637 parent_category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL
638 )`,
639
640 // Users table (VARCHAR ID)
641 `CREATE TABLE IF NOT EXISTS users (
642 id VARCHAR(50) PRIMARY KEY,
643 username VARCHAR(100) UNIQUE NOT NULL,
644 email VARCHAR(255) UNIQUE NOT NULL,
645 password VARCHAR(255) NOT NULL,
646 user_type VARCHAR(50) NOT NULL,
647 force_password_change INTEGER DEFAULT 0,
648 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
649 )`,
650
651 // Personal table (VARCHAR ID - format: storeId(3) + '001' for owner, storeId(3) + employeeNum(3) for employees)
652 `CREATE TABLE IF NOT EXISTS personal (
653 id VARCHAR(10) PRIMARY KEY,
654 first_name VARCHAR(100) NOT NULL,
655 last_name VARCHAR(100) NOT NULL,
656 ssn VARCHAR(13) UNIQUE NOT NULL,
657 email VARCHAR(255) UNIQUE NOT NULL,
658 password VARCHAR(255) NOT NULL,
659 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
660 )`,
661
662 // Product table (VARCHAR ID)
663 `CREATE TABLE IF NOT EXISTS product (
664 id VARCHAR(50) PRIMARY KEY,
665 code VARCHAR(20) UNIQUE NOT NULL,
666 description TEXT NOT NULL,
667 price DECIMAL(10,2) NOT NULL,
668 availability INTEGER NOT NULL DEFAULT 0,
669 weight DECIMAL(10,2),
670 dimensions VARCHAR(50),
671 production_time INTEGER,
672 category_id INTEGER REFERENCES category(category_id) ON DELETE SET NULL,
673 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
674 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
675 )`,
676
677 // Boss table (VARCHAR ID - references personal.id)
678 `CREATE TABLE IF NOT EXISTS boss (
679 boss_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
680 signature TEXT NOT NULL,
681 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
682 )`,
683
684 // Employees table (VARCHAR ID - references personal.id)
685 `CREATE TABLE IF NOT EXISTS employees (
686 employee_id VARCHAR(10) PRIMARY KEY REFERENCES personal(id) ON DELETE CASCADE,
687 date_of_hire DATE NOT NULL,
688 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
689 )`,
690
691 // Works_in_store table (junction)
692 `CREATE TABLE IF NOT EXISTS works_in_store (
693 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
694 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
695 PRIMARY KEY (personal_id, store_id)
696 )`,
697
698 // Permissions table
699 `CREATE TABLE IF NOT EXISTS permissions (
700 permission_id INTEGER PRIMARY KEY AUTOINCREMENT,
701 personal_id VARCHAR(10) REFERENCES personal(id) ON DELETE CASCADE,
702 type VARCHAR(50) NOT NULL,
703 authorisation TEXT,
704 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
705 )`,
706
707 // Order table (VARCHAR ID)
708 `CREATE TABLE IF NOT EXISTS "order" (
709 order_num VARCHAR(20) PRIMARY KEY,
710 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
711 order_date TIMESTAMP NOT NULL,
712 quantity INTEGER NOT NULL,
713 payment_method VARCHAR(50) NOT NULL,
714 discount DECIMAL(10,2) DEFAULT 0,
715 delivery_address TEXT NOT NULL,
716 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE SET NULL,
717 status VARCHAR(50) DEFAULT 'pending',
718 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
719 )`,
720
721 // Order_items table
722 `CREATE TABLE IF NOT EXISTS order_items (
723 item_id INTEGER PRIMARY KEY AUTOINCREMENT,
724 order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE,
725 product_code VARCHAR(20) REFERENCES product(code) ON DELETE SET NULL,
726 quantity INTEGER NOT NULL,
727 price DECIMAL(10,2) NOT NULL,
728 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
729 )`,
730
731 // Review table (VARCHAR ID)
732 `CREATE TABLE IF NOT EXISTS review (
733 review_id VARCHAR(20) PRIMARY KEY,
734 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
735 product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE,
736 rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
737 comment TEXT,
738 review_date TIMESTAMP NOT NULL,
739 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
740 )`,
741
742 // Request table (VARCHAR ID)
743 `CREATE TABLE IF NOT EXISTS request (
744 request_num VARCHAR(50) PRIMARY KEY,
745 date_and_time TIMESTAMP NOT NULL,
746 problem TEXT NOT NULL,
747 client_id INTEGER REFERENCES client(client_id) ON DELETE SET NULL,
748 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
749 status VARCHAR(50) DEFAULT 'pending',
750 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
751 )`,
752
753 // Refund table (VARCHAR ID)
754 `CREATE TABLE IF NOT EXISTS refund (
755 refund_id VARCHAR(50) PRIMARY KEY,
756 order_num VARCHAR(20) REFERENCES "order"(order_num) ON DELETE CASCADE,
757 amount DECIMAL(10,2) NOT NULL,
758 reason TEXT NOT NULL,
759 status VARCHAR(50) DEFAULT 'pending',
760 request_date TIMESTAMP NOT NULL,
761 processed_date TIMESTAMP,
762 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
763 )`,
764
765 // Report table (VARCHAR ID)
766 `CREATE TABLE IF NOT EXISTS report (
767 id VARCHAR(50) PRIMARY KEY,
768 store_id VARCHAR(10) REFERENCES store(store_id) ON DELETE CASCADE,
769 period VARCHAR(50) NOT NULL,
770 start_date DATE NOT NULL,
771 end_date DATE NOT NULL,
772 type VARCHAR(50) NOT NULL,
773 generated_by VARCHAR(10) REFERENCES personal(id) ON DELETE SET NULL,
774 generated_at TIMESTAMP NOT NULL,
775 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
776 )`,
777
778 // Audit_log table (SERIAL ID)
779 `CREATE TABLE IF NOT EXISTS audit_log (
780 log_id INTEGER PRIMARY KEY AUTOINCREMENT,
781 user_id VARCHAR(50),
782 action VARCHAR(100) NOT NULL,
783 resource_type VARCHAR(50),
784 resource_id VARCHAR(50),
785 details TEXT,
786 ip_address VARCHAR(45),
787 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
788 )`,
789
790 // Color table (SERIAL ID)
791 `CREATE TABLE IF NOT EXISTS color (
792 color_id INTEGER PRIMARY KEY AUTOINCREMENT,
793 name VARCHAR(50) NOT NULL,
794 hex_code VARCHAR(7) NOT NULL,
795 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
796 )`,
797
798 // Image table (SERIAL ID)
799 `CREATE TABLE IF NOT EXISTS image (
800 image_id INTEGER PRIMARY KEY AUTOINCREMENT,
801 product_code VARCHAR(20) REFERENCES product(code) ON DELETE CASCADE,
802 image_url TEXT NOT NULL,
803 is_primary BOOLEAN DEFAULT FALSE,
804 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
805 )`,
806
807 // Delivery_address table (SERIAL ID)
808 `CREATE TABLE IF NOT EXISTS delivery_address (
809 address_id INTEGER PRIMARY KEY AUTOINCREMENT,
810 client_id INTEGER REFERENCES client(client_id) ON DELETE CASCADE,
811 address TEXT NOT NULL,
812 city VARCHAR(100) NOT NULL,
813 postcode VARCHAR(20) NOT NULL,
814 country VARCHAR(100) NOT NULL,
815 is_default BOOLEAN DEFAULT FALSE,
816 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
817 )`,
818
819 // Roles table (SERIAL ID)
820 `CREATE TABLE IF NOT EXISTS roles (
821 role_id INTEGER PRIMARY KEY AUTOINCREMENT,
822 name VARCHAR(50) UNIQUE NOT NULL,
823 description TEXT,
824 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
825 )`,
826
827 // User_roles table (junction)
828 `CREATE TABLE IF NOT EXISTS user_roles (
829 user_id VARCHAR(50) REFERENCES users(id) ON DELETE CASCADE,
830 role_id INTEGER REFERENCES roles(role_id) ON DELETE CASCADE,
831 PRIMARY KEY (user_id, role_id)
832 )`
833 ];
834
835 let index = 0;
836
837 function runNext() {
838 if (index >= createQueries.length) {
839 console.log('โœ… All tables created');
840 resolve();
841 return;
842 }
843
844 const tableName = createQueries[index].split('TABLE')[1].split('(')[0].trim().replace('IF NOT EXISTS', '').trim();
845 console.log(`Creating table: ${tableName}...`);
846
847 database.database.run(createQueries[index], [], (err) => {
848 if (err) {
849 console.error(`Error creating table: ${err.message}`);
850 reject(err);
851 return;
852 }
853 console.log(`โœ… Created table: ${tableName}`);
854 index++;
855 runNext();
856 });
857 }
858
859 runNext();
860 });
861}
862
863function createIndexes() {
864 return new Promise((resolve, reject) => {
865 console.log('๐Ÿ“Š Creating indexes...');
866
867 const indexQueries = [
868 'CREATE INDEX IF NOT EXISTS idx_product_store ON product(store_id)',
869 'CREATE INDEX IF NOT EXISTS idx_product_category ON product(category_id)',
870 'CREATE INDEX IF NOT EXISTS idx_order_client ON "order"(client_id)',
871 'CREATE INDEX IF NOT EXISTS idx_order_store ON "order"(store_id)',
872 'CREATE INDEX IF NOT EXISTS idx_order_date ON "order"(order_date)',
873 'CREATE INDEX IF NOT EXISTS idx_review_client ON review(client_id)',
874 'CREATE INDEX IF NOT EXISTS idx_review_product ON review(product_code)',
875 'CREATE INDEX IF NOT EXISTS idx_request_client ON request(client_id)',
876 'CREATE INDEX IF NOT EXISTS idx_request_store ON request(store_id)',
877 'CREATE INDEX IF NOT EXISTS idx_refund_order ON refund(order_num)',
878 'CREATE INDEX IF NOT EXISTS idx_refund_status ON refund(status)',
879 'CREATE INDEX IF NOT EXISTS idx_personal_email ON personal(email)',
880 'CREATE INDEX IF NOT EXISTS idx_client_email ON client(email)',
881 'CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)',
882 'CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)',
883 'CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id)',
884 'CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action)',
885 'CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at)',
886 'CREATE INDEX IF NOT EXISTS idx_delivery_client ON delivery_address(client_id)',
887 'CREATE INDEX IF NOT EXISTS idx_works_in_store_personal ON works_in_store(personal_id)',
888 'CREATE INDEX IF NOT EXISTS idx_works_in_store_store ON works_in_store(store_id)'
889 ];
890
891 let index = 0;
892
893 function runNext() {
894 if (index >= indexQueries.length) {
895 console.log('โœ… Indexes created');
896 resolve();
897 return;
898 }
899
900 database.database.run(indexQueries[index], [], (err) => {
901 if (err) {
902 console.log(`โš ๏ธ Index creation warning for ${indexQueries[index].substring(0, 50)}...: ${err.message}`);
903 }
904 index++;
905 runNext();
906 });
907 }
908
909 runNext();
910 });
911}
912
913function insertInitialData() {
914 return new Promise((resolve, reject) => {
915 console.log('๐Ÿ“ Inserting initial data...');
916
917 // Insert default roles
918 const roles = [
919 { name: 'admin', description: 'System administrator' },
920 { name: 'store_owner', description: 'Store owner' },
921 { name: 'store_employee', description: 'Store employee' },
922 { name: 'client', description: 'Registered client' },
923 { name: 'guest', description: 'Unregistered guest' }
924 ];
925
926 let rolesInserted = 0;
927
928 roles.forEach(role => {
929 database.database.run(
930 `INSERT INTO roles (name, description)
931 VALUES (?, ?)
932 ON CONFLICT DO NOTHING`,
933 [role.name, role.description],
934 (err) => {
935 if (err) {
936 console.error(`Error inserting role ${role.name}:`, err.message);
937 }
938 rolesInserted++;
939
940 if (rolesInserted === roles.length) {
941 console.log('โœ… Roles inserted');
942 // Create admin user with ID 000000
943 createAdminUser();
944
945 // Ensure General category exists
946 database.ensureGeneralCategory((err) => {
947 if (err) {
948 console.error('Error ensuring General category:', err.message);
949 } else {
950 console.log('โœ… General category checked/created');
951 }
952 resolve();
953 });
954 }
955 }
956 );
957 });
958 });
959}
960
961// Function to create admin user with ID 000000
962function createAdminUser() {
963 const adminId = '000000';
964 const adminPassword = bcrypt.hashSync('Admin123!', 10);
965
966 database.database.get(
967 'SELECT * FROM users WHERE id = ? OR username = ? OR email = ?',
968 [adminId, 'admin', 'admin@handcraft.com'],
969 (err, existingAdmin) => {
970 if (err) {
971 console.error('Error checking for existing admin:', err.message);
972 return;
973 }
974
975 if (!existingAdmin) {
976 // Start a transaction
977 database.database.run('BEGIN TRANSACTION', (err) => {
978 if (err) {
979 console.error('Error beginning transaction:', err);
980 return;
981 }
982
983 // Insert into users table
984 database.database.run(
985 `INSERT INTO users (id, username, email, password, user_type, force_password_change)
986 VALUES (?, ?, ?, ?, ?, ?)`,
987 [adminId, 'admin', 'admin@handcraft.com', adminPassword, 'admin', 1],
988 function(err) {
989 if (err) {
990 database.database.run('ROLLBACK');
991 console.error('Error inserting admin user:', err.message);
992 return;
993 }
994
995 // Insert into personal table (required for boss table)
996 database.database.run(
997 `INSERT INTO personal (id, first_name, last_name, ssn, email, password)
998 VALUES (?, ?, ?, ?, ?, ?)`,
999 [adminId, 'Admin', 'User', '0000000000000', 'admin@handcraft.com', adminPassword],
1000 function(err) {
1001 if (err) {
1002 database.database.run('ROLLBACK');
1003 console.error('Error inserting admin personal:', err.message);
1004 return;
1005 }
1006
1007 // Insert into boss table (store owner)
1008 database.database.run(
1009 `INSERT INTO boss (boss_id, signature)
1010 VALUES (?, ?)`,
1011 [adminId, 'Admin Signature'],
1012 function(err) {
1013 if (err) {
1014 database.database.run('ROLLBACK');
1015 console.error('Error inserting admin boss:', err.message);
1016 return;
1017 }
1018
1019 // Insert into permissions
1020 database.database.run(
1021 `INSERT INTO permissions (personal_id, type, authorisation)
1022 VALUES (?, ?, ?)`,
1023 [adminId, 'ADMIN', 'full_access'],
1024 function(err) {
1025 if (err) {
1026 console.error('Error inserting admin permissions:', err.message);
1027 // Continue even if this fails
1028 }
1029
1030 // Assign admin role
1031 database.database.get(
1032 'SELECT role_id FROM roles WHERE name = ?',
1033 ['admin'],
1034 (err, adminRole) => {
1035 if (!err && adminRole) {
1036 database.database.run(
1037 'INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)',
1038 [adminId, adminRole.role_id],
1039 (err) => {
1040 if (err) {
1041 console.error('Error assigning admin role:', err.message);
1042 }
1043 }
1044 );
1045 }
1046
1047 database.database.run('COMMIT', (commitErr) => {
1048 if (commitErr) {
1049 console.error('Error committing transaction:', commitErr);
1050 database.database.run('ROLLBACK');
1051 } else {
1052 console.log('\n');
1053 console.log('๐Ÿ” ===== ADMIN CREDENTIALS =====');
1054 console.log('๐Ÿ†” ID: 000000');
1055 console.log('๐Ÿ‘ค Username: admin');
1056 console.log('๐Ÿ“ง Email: admin@handcraft.com');
1057 console.log('๐Ÿ”‘ Password: Admin123!');
1058 console.log('โš ๏ธ This is a first-time login. You will be required to change your password after 2FA verification.');
1059 console.log('================================\n');
1060 }
1061 });
1062 }
1063 );
1064 }
1065 );
1066 }
1067 );
1068 }
1069 );
1070 }
1071 );
1072 });
1073 } else {
1074 console.log('โœ… Admin user already exists with ID:', existingAdmin.id);
1075 }
1076 }
1077 );
1078}
1079
1080// Initialize database on startup
1081(async function() {
1082 try {
1083 await initializeDatabase();
1084 console.log('โœ… Database initialization completed');
1085 } catch (err) {
1086 console.error('โŒ Database initialization failed:', err);
1087 }
1088})();
1089
1090const server = http.createServer((req, res) => {
1091 const parsedUrl = url.parse(req.url, true);
1092 const pathname = parsedUrl.pathname;
1093 const ipAddress = getClientIp(req);
1094
1095 console.log('Request:', req.method, pathname);
1096
1097 res.setHeader('Access-Control-Allow-Origin', '*');
1098 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
1099 res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
1100
1101 if (req.method === 'OPTIONS') {
1102 res.writeHead(200);
1103 res.end();
1104 return;
1105 }
1106
1107 if (pathname === '/' || pathname === '/index.html') {
1108 serveStaticFile(res, 'index.html', 'text/html');
1109 } else if (pathname === '/login.html') {
1110 serveStaticFile(res, 'login.html', 'text/html');
1111 } else if (pathname === '/register.html') {
1112 serveStaticFile(res, 'register.html', 'text/html');
1113 } else if (pathname === '/register-store.html') {
1114 serveStaticFile(res, 'register-store.html', 'text/html');
1115 } else if (pathname === '/dashboard.html') {
1116 const cookies = parseCookies(req);
1117 const sessionId = cookies.sessionId;
1118
1119 if (!sessionId || !sessions.has(sessionId)) {
1120 res.writeHead(302, { 'Location': '/login.html' });
1121 res.end();
1122 return;
1123 }
1124
1125 if (tempAdminSessions.has(sessionId)) {
1126 res.writeHead(302, { 'Location': '/change-password.html?forced=true' });
1127 res.end();
1128 return;
1129 }
1130
1131 serveStaticFile(res, 'dashboard.html', 'text/html');
1132 } else if (pathname === '/verify-email.html') {
1133 serveStaticFile(res, 'verify-email.html', 'text/html');
1134 } else if (pathname === '/verify-2fa.html') {
1135 serveStaticFile(res, 'verify-2fa.html', 'text/html');
1136 } else if (pathname === '/admin.html') {
1137 // Check if user is authenticated
1138 const cookies = parseCookies(req);
1139 const sessionId = cookies.sessionId;
1140
1141 if (!sessionId || !sessions.has(sessionId)) {
1142 res.writeHead(302, { 'Location': '/login.html' });
1143 res.end();
1144 return;
1145 }
1146
1147 // Get user from session
1148 const userId = sessions.get(sessionId);
1149
1150 // Check if this is the admin user
1151 if (userId !== '000000') {
1152 // Not admin, redirect to appropriate dashboard
1153 if (userId.startsWith('client_')) {
1154 res.writeHead(302, { 'Location': '/client-dashboard.html' });
1155 } else if (userId.startsWith('personal_')) {
1156 // Check if store owner or employee
1157 const personalId = userId.replace('personal_', '');
1158
1159 database.database.get(
1160 'SELECT boss_id FROM boss WHERE boss_id = ?',
1161 [personalId],
1162 (err, boss) => {
1163 if (boss) {
1164 res.writeHead(302, { 'Location': '/store-owner.html' });
1165 } else {
1166 res.writeHead(302, { 'Location': '/store-employee.html' });
1167 }
1168 res.end();
1169 }
1170 );
1171 return;
1172 } else {
1173 res.writeHead(302, { 'Location': '/dashboard.html' });
1174 }
1175 res.end();
1176 return;
1177 }
1178
1179 serveStaticFile(res, 'admin.html', 'text/html');
1180 } else if (pathname === '/store-owner.html') {
1181 serveStaticFile(res, 'store-owner.html', 'text/html');
1182 } else if (pathname === '/store-employee.html') {
1183 serveStaticFile(res, 'store-employee.html', 'text/html');
1184 } else if (pathname === '/client-dashboard.html') {
1185 serveStaticFile(res, 'client-dashboard.html', 'text/html');
1186 } else if (pathname === '/products.html') {
1187 serveStaticFile(res, 'products.html', 'text/html');
1188 } else if (pathname === '/product-detail.html') {
1189 serveStaticFile(res, 'product-detail.html', 'text/html');
1190 } else if (pathname === '/checkout.html') {
1191 serveStaticFile(res, 'checkout.html', 'text/html');
1192 } else if (pathname === '/orders.html') {
1193 serveStaticFile(res, 'orders.html', 'text/html');
1194 } else if (pathname === '/reviews.html') {
1195 serveStaticFile(res, 'reviews.html', 'text/html');
1196 } else if (pathname === '/change-password.html') {
1197 serveStaticFile(res, 'change-password.html', 'text/html');
1198 } else if (pathname === '/style.css') {
1199 serveStaticFile(res, 'style.css', 'text/css');
1200 } else if (pathname === '/script.js') {
1201 serveStaticFile(res, 'script.js', 'application/javascript');
1202 }
1203
1204 else if (pathname === '/api/register' && req.method === 'POST') {
1205 let body = '';
1206 req.on('data', chunk => {
1207 body += chunk.toString();
1208 });
1209 req.on('end', () => {
1210 const { username, email, password, userType, firstName, lastName } = JSON.parse(body);
1211
1212 if (!username || !email || !password || !userType) {
1213 res.writeHead(400, { 'Content-Type': 'application/json' });
1214 res.end(JSON.stringify({ success: false, message: 'All fields are required' }));
1215 return;
1216 }
1217
1218 if (!validateEmail(email)) {
1219 res.writeHead(400, { 'Content-Type': 'application/json' });
1220 res.end(JSON.stringify({ success: false, message: 'Email is not valid' }));
1221 return;
1222 }
1223
1224 if (!validatePassword(password)) {
1225 res.writeHead(400, { 'Content-Type': 'application/json' });
1226 res.end(JSON.stringify({
1227 success: false,
1228 message: 'Password must have at least 8 characters, including uppercase, lowercase, number and special character'
1229 }));
1230 return;
1231 }
1232
1233 database.getUserByUsername(username, (err, existingUser) => {
1234 if (err) {
1235 console.error('Error checking user:', err);
1236 res.writeHead(500, { 'Content-Type': 'application/json' });
1237 res.end(JSON.stringify({ success: false, message: 'Server error checking user' }));
1238 return;
1239 }
1240
1241 database.getClientByEmail(email, (err, existingClient) => {
1242 if (err) {
1243 console.error('Error checking client:', err);
1244 }
1245
1246 if (existingUser || existingClient) {
1247 res.writeHead(400, { 'Content-Type': 'application/json' });
1248 res.end(JSON.stringify({ success: false, message: 'Username or email is already in use' }));
1249 return;
1250 }
1251
1252 const verificationCode = generateVerificationCode();
1253
1254 const tempUserData = {
1255 username,
1256 email,
1257 password,
1258 timestamp: Date.now(),
1259 userType: userType,
1260 firstName: firstName || '',
1261 lastName: lastName || ''
1262 };
1263
1264 tempUsers.set(verificationCode, tempUserData);
1265 verificationCodes.set(email, { code: verificationCode, timestamp: Date.now() });
1266
1267 console.log(`โฐ Generated verification code for ${email}, expires in 30 seconds`);
1268
1269 sendVerificationEmail(email, verificationCode)
1270 .then(() => {
1271 console.log('โœ… Verification email sent to:', email);
1272 database.logAudit(null, 'REGISTER_ATTEMPT', 'user', null, `Registration attempt for ${email} as ${userType}`, ipAddress);
1273 res.writeHead(200, { 'Content-Type': 'application/json' });
1274 res.end(JSON.stringify({
1275 success: true,
1276 message: 'Verification code sent to your email (expires in 30 seconds)',
1277 email: email
1278 }));
1279 })
1280 .catch(error => {
1281 console.error('Error sending email:', error.message);
1282 res.writeHead(200, { 'Content-Type': 'application/json' });
1283 res.end(JSON.stringify({
1284 success: true,
1285 message: 'Verification code generated (check console, expires in 30 seconds)',
1286 email: email,
1287 developmentCode: verificationCode
1288 }));
1289 });
1290 });
1291 });
1292 });
1293 }
1294
1295 else if (pathname === '/api/register-store' && req.method === 'POST') {
1296 let body = '';
1297 req.on('data', chunk => {
1298 body += chunk.toString();
1299 });
1300 req.on('end', () => {
1301 const formData = JSON.parse(body);
1302
1303 const requiredFields = [
1304 'ownerFirstName', 'ownerLastName', 'ownerSSN', 'ownerEmail',
1305 'storeName', 'storeAddress', 'storeEmail', 'storeFoundingDate',
1306 'password', 'confirmPassword', 'signature'
1307 ];
1308
1309 for (const field of requiredFields) {
1310 if (!formData[field]) {
1311 res.writeHead(400, { 'Content-Type': 'application/json' });
1312 res.end(JSON.stringify({
1313 success: false,
1314 message: `Field ${field} is required`
1315 }));
1316 return;
1317 }
1318 }
1319
1320 if (!/^\d{13}$/.test(formData.ownerSSN)) {
1321 res.writeHead(400, { 'Content-Type': 'application/json' });
1322 res.end(JSON.stringify({
1323 success: false,
1324 message: 'SSN must be exactly 13 digits'
1325 }));
1326 return;
1327 }
1328
1329 const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
1330
1331 if (!emailRegex.test(formData.ownerEmail)) {
1332 res.writeHead(400, { 'Content-Type': 'application/json' });
1333 res.end(JSON.stringify({
1334 success: false,
1335 message: 'Please enter a valid personal email address'
1336 }));
1337 return;
1338 }
1339
1340 if (!emailRegex.test(formData.storeEmail)) {
1341 res.writeHead(400, { 'Content-Type': 'application/json' });
1342 res.end(JSON.stringify({
1343 success: false,
1344 message: 'Please enter a valid store email address'
1345 }));
1346 return;
1347 }
1348
1349 if (formData.password !== formData.confirmPassword) {
1350 res.writeHead(400, { 'Content-Type': 'application/json' });
1351 res.end(JSON.stringify({
1352 success: false,
1353 message: 'Passwords do not match'
1354 }));
1355 return;
1356 }
1357
1358 if (!validatePassword(formData.password)) {
1359 res.writeHead(400, { 'Content-Type': 'application/json' });
1360 res.end(JSON.stringify({
1361 success: false,
1362 message: 'Password must have at least 8 characters, including uppercase, lowercase, number and special character'
1363 }));
1364 return;
1365 }
1366
1367 database.getPersonalByEmail(formData.ownerEmail, (err, existingPersonal) => {
1368 if (err) {
1369 console.error('Error checking personal:', err);
1370 res.writeHead(500, { 'Content-Type': 'application/json' });
1371 res.end(JSON.stringify({ success: false, message: 'Server error checking personal' }));
1372 return;
1373 }
1374
1375 if (existingPersonal) {
1376 res.writeHead(400, { 'Content-Type': 'application/json' });
1377 res.end(JSON.stringify({ success: false, message: 'Personal email is already registered' }));
1378 return;
1379 }
1380
1381 database.database.get(
1382 'SELECT store_id FROM store WHERE store_email = ?',
1383 [formData.storeEmail],
1384 (err, existingStore) => {
1385 if (err) {
1386 console.error('Error checking store:', err);
1387 res.writeHead(500, { 'Content-Type': 'application/json' });
1388 res.end(JSON.stringify({ success: false, message: 'Server error checking store' }));
1389 return;
1390 }
1391
1392 if (existingStore) {
1393 res.writeHead(400, { 'Content-Type': 'application/json' });
1394 res.end(JSON.stringify({ success: false, message: 'Store email is already registered' }));
1395 return;
1396 }
1397
1398 // Get the maximum store_id to determine the next store ID
1399 database.database.get(
1400 'SELECT MAX(store_id) as max_store_num FROM store',
1401 [],
1402 (err, result) => {
1403 if (err) {
1404 console.error('Error getting max store ID:', err);
1405 res.writeHead(500, { 'Content-Type': 'application/json' });
1406 res.end(JSON.stringify({ success: false, message: 'Server error generating store ID' }));
1407 return;
1408 }
1409
1410 // Next store number is max + 1, starting from 1 if no stores exist
1411 let nextStoreNumber = 1;
1412
1413 if (result && result.max_store_num) {
1414 // Extract numeric part from store_id (format: XXX)
1415 const maxNum = parseInt(result.max_store_num, 10);
1416 if (!isNaN(maxNum)) {
1417 nextStoreNumber = maxNum + 1;
1418 }
1419 }
1420
1421 if (nextStoreNumber > 999) {
1422 res.writeHead(400, { 'Content-Type': 'application/json' });
1423 res.end(JSON.stringify({ success: false, message: 'Maximum store limit reached (999)' }));
1424 return;
1425 }
1426
1427 // Store ID is padded to 3 digits (VARCHAR)
1428 const storeIdPadded = nextStoreNumber.toString().padStart(3, '0');
1429
1430 // Personal ID is storeId + '001' (as string for display)
1431 const personalId = storeIdPadded + '001';
1432
1433 const verificationCode = generateVerificationCode();
1434
1435 const tempStoreData = {
1436 personalId: personalId, // VARCHAR for personal table
1437 ownerFirstName: formData.ownerFirstName,
1438 ownerLastName: formData.ownerLastName,
1439 ownerSSN: formData.ownerSSN,
1440 ownerEmail: formData.ownerEmail,
1441 storeId: storeIdPadded, // VARCHAR for store table
1442 storeIdPadded: storeIdPadded,
1443 storeName: formData.storeName,
1444 storeAddress: formData.storeAddress,
1445 storeEmail: formData.storeEmail,
1446 storeFoundingDate: formData.storeFoundingDate,
1447 storeDescription: formData.storeDescription || '',
1448 password: formData.password,
1449 signature: formData.signature,
1450 timestamp: Date.now()
1451 };
1452
1453 tempStoreRegistrations.set(verificationCode, tempStoreData);
1454 verificationCodes.set(formData.ownerEmail, {
1455 code: verificationCode,
1456 timestamp: Date.now(),
1457 storeRegistration: true
1458 });
1459
1460 console.log(`โฐ Generated store registration verification code for ${formData.ownerEmail}, expires in 30 seconds`);
1461 console.log(`๐Ÿช Store ID will be: ${storeIdPadded}`);
1462 console.log(`๐Ÿ‘ค Personal ID will be: ${personalId}`);
1463
1464 sendStoreRegistrationEmail(formData.ownerEmail, verificationCode, formData.storeName)
1465 .then(() => {
1466 console.log('โœ… Store registration email sent to:', formData.ownerEmail);
1467 database.logAudit(null, 'STORE_REGISTER_ATTEMPT', 'store', null, `Store registration attempt: ${formData.storeName}`, ipAddress);
1468 res.writeHead(200, { 'Content-Type': 'application/json' });
1469 res.end(JSON.stringify({
1470 success: true,
1471 message: 'Verification code sent to your email (expires in 30 seconds)',
1472 email: formData.ownerEmail,
1473 storeName: formData.storeName
1474 }));
1475 })
1476 .catch(error => {
1477 console.error('Error sending store registration email:', error.message);
1478 res.writeHead(200, { 'Content-Type': 'application/json' });
1479 res.end(JSON.stringify({
1480 success: true,
1481 message: 'Verification code generated (check console, expires in 30 seconds)',
1482 email: formData.ownerEmail,
1483 storeName: formData.storeName,
1484 developmentCode: verificationCode
1485 }));
1486 });
1487 }
1488 );
1489 }
1490 );
1491 });
1492 });
1493 }
1494
1495 else if (pathname === '/api/client-register' && req.method === 'POST') {
1496 let body = '';
1497 req.on('data', chunk => {
1498 body += chunk.toString();
1499 });
1500 req.on('end', () => {
1501 const { firstName, lastName, email, password, address, city, postcode, country, isDefaultAddress } = JSON.parse(body);
1502
1503 if (!firstName || !lastName || !email || !password) {
1504 res.writeHead(400, { 'Content-Type': 'application/json' });
1505 res.end(JSON.stringify({ success: false, message: 'First name, last name, email and password are required' }));
1506 return;
1507 }
1508
1509 if (!validateEmail(email)) {
1510 res.writeHead(400, { 'Content-Type': 'application/json' });
1511 res.end(JSON.stringify({ success: false, message: 'Email is not valid' }));
1512 return;
1513 }
1514
1515 if (!validatePassword(password)) {
1516 res.writeHead(400, { 'Content-Type': 'application/json' });
1517 res.end(JSON.stringify({
1518 success: false,
1519 message: 'Password must have at least 8 characters, including uppercase, lowercase, number and special character'
1520 }));
1521 return;
1522 }
1523
1524 database.getClientByEmail(email, (err, existingClient) => {
1525 if (err) {
1526 console.error('Error checking client:', err);
1527 res.writeHead(500, { 'Content-Type': 'application/json' });
1528 res.end(JSON.stringify({ success: false, message: 'Server error checking client' }));
1529 return;
1530 }
1531
1532 if (existingClient) {
1533 res.writeHead(400, { 'Content-Type': 'application/json' });
1534 res.end(JSON.stringify({ success: false, message: 'Email is already registered' }));
1535 return;
1536 }
1537
1538 const verificationCode = generateVerificationCode();
1539
1540 const tempUserData = {
1541 username: `${firstName} ${lastName}`,
1542 email,
1543 password,
1544 timestamp: Date.now(),
1545 userType: 'client',
1546 firstName: firstName,
1547 lastName: lastName,
1548 address: address || null,
1549 city: city || null,
1550 postcode: postcode || null,
1551 country: country || null,
1552 isDefaultAddress: isDefaultAddress || false
1553 };
1554
1555 tempUsers.set(verificationCode, tempUserData);
1556 verificationCodes.set(email, { code: verificationCode, timestamp: Date.now() });
1557
1558 console.log(`โฐ Generated verification code for client ${email}, expires in 30 seconds`);
1559
1560 sendVerificationEmail(email, verificationCode)
1561 .then(() => {
1562 console.log('โœ… Verification email sent to:', email);
1563 database.logAudit(null, 'CLIENT_REGISTER_ATTEMPT', 'client', null, `Client registration attempt for ${email}`, ipAddress);
1564 res.writeHead(200, { 'Content-Type': 'application/json' });
1565 res.end(JSON.stringify({
1566 success: true,
1567 message: 'Verification code sent to your email (expires in 30 seconds)',
1568 email: email
1569 }));
1570 })
1571 .catch(error => {
1572 console.error('Error sending email:', error.message);
1573 res.writeHead(200, { 'Content-Type': 'application/json' });
1574 res.end(JSON.stringify({
1575 success: true,
1576 message: 'Verification code generated (check console, expires in 30 seconds)',
1577 email: email,
1578 developmentCode: verificationCode
1579 }));
1580 });
1581 });
1582 });
1583 }
1584
1585 else if (pathname === '/api/resend-verification' && req.method === 'POST') {
1586 let body = '';
1587 req.on('data', chunk => {
1588 body += chunk.toString();
1589 });
1590 req.on('end', () => {
1591 const { email } = JSON.parse(body);
1592
1593 if (!email) {
1594 res.writeHead(400, { 'Content-Type': 'application/json' });
1595 res.end(JSON.stringify({ success: false, message: 'Email is required' }));
1596 return;
1597 }
1598
1599 const existingTempUser = Array.from(tempUsers.values()).find(user => user.email === email);
1600
1601 if (existingTempUser) {
1602 const newVerificationCode = generateVerificationCode();
1603
1604 const tempUserData = {
1605 username: existingTempUser.username,
1606 email: existingTempUser.email,
1607 password: existingTempUser.password,
1608 timestamp: Date.now(),
1609 userType: existingTempUser.userType,
1610 firstName: existingTempUser.firstName || '',
1611 lastName: existingTempUser.lastName || '',
1612 address: existingTempUser.address || null,
1613 city: existingTempUser.city || null,
1614 postcode: existingTempUser.postcode || null,
1615 country: existingTempUser.country || null,
1616 isDefaultAddress: existingTempUser.isDefaultAddress || false
1617 };
1618
1619 tempUsers.forEach((value, key) => {
1620 if (value.email === email) {
1621 tempUsers.delete(key);
1622 }
1623 });
1624
1625 tempUsers.set(newVerificationCode, tempUserData);
1626 verificationCodes.set(email, { code: newVerificationCode, timestamp: Date.now() });
1627
1628 console.log(`๐Ÿ”„ Resent verification code for ${email}, expires in 30 seconds`);
1629
1630 sendVerificationEmail(email, newVerificationCode)
1631 .then(() => {
1632 res.writeHead(200, { 'Content-Type': 'application/json' });
1633 res.end(JSON.stringify({
1634 success: true,
1635 message: 'New verification code sent to your email (expires in 30 seconds)',
1636 email: email
1637 }));
1638 })
1639 .catch(error => {
1640 console.error('Error sending email:', error.message);
1641 res.writeHead(200, { 'Content-Type': 'application/json' });
1642 res.end(JSON.stringify({
1643 success: true,
1644 message: 'New verification code generated (check console, expires in 30 seconds)',
1645 email: email,
1646 developmentCode: newVerificationCode
1647 }));
1648 });
1649
1650 return;
1651 }
1652
1653 const existingTempStore = Array.from(tempStoreRegistrations.values()).find(store => store.ownerEmail === email);
1654
1655 if (existingTempStore) {
1656 const newVerificationCode = generateVerificationCode();
1657
1658 const tempStoreData = {
1659 personalId: existingTempStore.personalId,
1660 ownerFirstName: existingTempStore.ownerFirstName,
1661 ownerLastName: existingTempStore.ownerLastName,
1662 ownerSSN: existingTempStore.ownerSSN,
1663 ownerEmail: existingTempStore.ownerEmail,
1664 storeId: existingTempStore.storeId,
1665 storeIdPadded: existingTempStore.storeIdPadded,
1666 storeName: existingTempStore.storeName,
1667 storeAddress: existingTempStore.storeAddress,
1668 storeEmail: existingTempStore.storeEmail,
1669 storeFoundingDate: existingTempStore.storeFoundingDate,
1670 storeDescription: existingTempStore.storeDescription,
1671 password: existingTempStore.password,
1672 signature: existingTempStore.signature,
1673 timestamp: Date.now()
1674 };
1675
1676 tempStoreRegistrations.forEach((value, key) => {
1677 if (value.ownerEmail === email) {
1678 tempStoreRegistrations.delete(key);
1679 }
1680 });
1681
1682 tempStoreRegistrations.set(newVerificationCode, tempStoreData);
1683 verificationCodes.set(email, {
1684 code: newVerificationCode,
1685 timestamp: Date.now(),
1686 storeRegistration: true
1687 });
1688
1689 console.log(`๐Ÿ”„ Resent store registration verification code for ${email}, expires in 30 seconds`);
1690
1691 sendStoreRegistrationEmail(email, newVerificationCode, existingTempStore.storeName)
1692 .then(() => {
1693 res.writeHead(200, { 'Content-Type': 'application/json' });
1694 res.end(JSON.stringify({
1695 success: true,
1696 message: 'New verification code sent to your email (expires in 30 seconds)',
1697 email: email
1698 }));
1699 })
1700 .catch(error => {
1701 console.error('Error sending store registration email:', error.message);
1702 res.writeHead(200, { 'Content-Type': 'application/json' });
1703 res.end(JSON.stringify({
1704 success: true,
1705 message: 'New verification code generated (check console, expires in 30 seconds)',
1706 email: email,
1707 developmentCode: newVerificationCode
1708 }));
1709 });
1710
1711 return;
1712 }
1713
1714 res.writeHead(400, { 'Content-Type': 'application/json' });
1715 res.end(JSON.stringify({ success: false, message: 'No pending registration found for this email' }));
1716 });
1717 }
1718
1719 else if (pathname === '/api/verify-email' && req.method === 'POST') {
1720 let body = '';
1721 req.on('data', chunk => {
1722 body += chunk.toString();
1723 });
1724 req.on('end', () => {
1725 const { email, code } = JSON.parse(body);
1726
1727 if (!email || !code) {
1728 res.writeHead(400, { 'Content-Type': 'application/json' });
1729 res.end(JSON.stringify({ success: false, message: 'All fields are required' }));
1730 return;
1731 }
1732
1733 const verificationData = verificationCodes.get(email);
1734
1735 if (verificationData && verificationData.storeRegistration) {
1736 const tempStoreData = tempStoreRegistrations.get(code);
1737
1738 if (!tempStoreData || tempStoreData.ownerEmail !== email) {
1739 res.writeHead(400, { 'Content-Type': 'application/json' });
1740 res.end(JSON.stringify({ success: false, message: 'Invalid verification code' }));
1741 return;
1742 }
1743
1744 if (Date.now() - tempStoreData.timestamp > 30 * 1000) {
1745 tempStoreRegistrations.delete(code);
1746 verificationCodes.delete(email);
1747 res.writeHead(400, { 'Content-Type': 'application/json' });
1748 res.end(JSON.stringify({ success: false, message: 'Verification code has expired. Please request a new one.' }));
1749 return;
1750 }
1751
1752 database.database.run('BEGIN TRANSACTION', (err) => {
1753 if (err) {
1754 console.error('Error beginning transaction:', err);
1755 res.writeHead(500, { 'Content-Type': 'application/json' });
1756 res.end(JSON.stringify({ success: false, message: 'Server error during registration' }));
1757 return;
1758 }
1759
1760 // Insert into store table (store_id is VARCHAR)
1761 database.database.run(
1762 'INSERT INTO store (store_id, name, date_of_founding, physical_address, store_email, rating) VALUES (?, ?, ?, ?, ?, ?)',
1763 [
1764 tempStoreData.storeId,
1765 tempStoreData.storeName,
1766 tempStoreData.storeFoundingDate,
1767 tempStoreData.storeAddress,
1768 tempStoreData.storeEmail,
1769 0.0
1770 ],
1771 function(err) {
1772 if (err) {
1773 database.database.run('ROLLBACK');
1774 console.error('Error inserting store:', err);
1775 res.writeHead(400, { 'Content-Type': 'application/json' });
1776 res.end(JSON.stringify({ success: false, message: 'Error registering store' }));
1777 return;
1778 }
1779
1780 // Insert into personal table (id is VARCHAR)
1781 database.database.run(
1782 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)',
1783 [
1784 tempStoreData.personalId,
1785 tempStoreData.ownerFirstName,
1786 tempStoreData.ownerLastName,
1787 tempStoreData.ownerSSN,
1788 tempStoreData.ownerEmail,
1789 bcrypt.hashSync(tempStoreData.password, 10)
1790 ],
1791 function(err) {
1792 if (err) {
1793 database.database.run('ROLLBACK');
1794 console.error('Error inserting personal:', err);
1795
1796 if (err.code === '23505') {
1797 res.writeHead(400, { 'Content-Type': 'application/json' });
1798 res.end(JSON.stringify({
1799 success: false,
1800 message: 'This personal ID is already taken. Please try again.'
1801 }));
1802 } else {
1803 res.writeHead(400, { 'Content-Type': 'application/json' });
1804 res.end(JSON.stringify({ success: false, message: 'Error registering personal information' }));
1805 }
1806 return;
1807 }
1808
1809 // Insert into boss table (boss_id is VARCHAR, references personal.id)
1810 database.database.run(
1811 'INSERT INTO boss (boss_id, signature) VALUES (?, ?)',
1812 [tempStoreData.personalId, tempStoreData.signature],
1813 (err) => {
1814 if (err) {
1815 database.database.run('ROLLBACK');
1816 console.error('Error inserting boss:', err);
1817 res.writeHead(400, { 'Content-Type': 'application/json' });
1818 res.end(JSON.stringify({ success: false, message: 'Error registering as boss' }));
1819 return;
1820 }
1821
1822 // Insert into works_in_store table (personal_id is VARCHAR, store_id is VARCHAR)
1823 database.database.run(
1824 'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)',
1825 [tempStoreData.personalId, tempStoreData.storeId],
1826 (err) => {
1827 if (err) {
1828 database.database.run('ROLLBACK');
1829 console.error('Error inserting works_in_store:', err);
1830 res.writeHead(400, { 'Content-Type': 'application/json' });
1831 res.end(JSON.stringify({ success: false, message: 'Error assigning to store' }));
1832 return;
1833 }
1834
1835 // Insert into permissions table (personal_id is VARCHAR)
1836 database.database.run(
1837 'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)',
1838 [tempStoreData.personalId, 'BOSS', 'full_access'],
1839 (err) => {
1840 if (err) {
1841 console.error('Error inserting permissions:', err);
1842 }
1843
1844 // Also create entry in users table for login with force_password_change = 1
1845 database.database.run(
1846 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)',
1847 [
1848 tempStoreData.personalId,
1849 `${tempStoreData.ownerFirstName} ${tempStoreData.ownerLastName}`,
1850 tempStoreData.ownerEmail,
1851 bcrypt.hashSync(tempStoreData.password, 10),
1852 'store_owner',
1853 1
1854 ],
1855 (err) => {
1856 if (err) {
1857 console.error('Error creating user entry for store owner:', err);
1858 }
1859
1860 database.database.run('COMMIT', (commitErr) => {
1861 if (commitErr) {
1862 console.error('Error committing transaction:', commitErr);
1863 database.database.run('ROLLBACK');
1864 res.writeHead(500, { 'Content-Type': 'application/json' });
1865 res.end(JSON.stringify({ success: false, message: 'Error completing registration' }));
1866 return;
1867 }
1868
1869 tempStoreRegistrations.delete(code);
1870 verificationCodes.delete(email);
1871
1872 console.log(`โœ… Store registration completed successfully:`);
1873 console.log(` Store ID: ${tempStoreData.storeId}`);
1874 console.log(` Store Name: ${tempStoreData.storeName}`);
1875 console.log(` Personal ID: ${tempStoreData.personalId}`);
1876 console.log(` Owner: ${tempStoreData.ownerFirstName} ${tempStoreData.ownerLastName}`);
1877
1878 database.logAudit(tempStoreData.personalId, 'STORE_REGISTER_SUCCESS', 'store', tempStoreData.storeId, `Store registered: ${tempStoreData.storeName}`, ipAddress);
1879
1880 res.writeHead(200, { 'Content-Type': 'application/json' });
1881 res.end(JSON.stringify({
1882 success: true,
1883 message: 'Store registration successful! You can now login.',
1884 storeId: tempStoreData.storeId,
1885 storeIdPadded: tempStoreData.storeIdPadded,
1886 storeName: tempStoreData.storeName,
1887 personalId: tempStoreData.personalId,
1888 userType: 'store_owner',
1889 redirectTo: 'login.html'
1890 }));
1891 });
1892 }
1893 );
1894 }
1895 );
1896 }
1897 );
1898 }
1899 );
1900 }
1901 );
1902 }
1903 );
1904 });
1905
1906 return;
1907 }
1908
1909 const tempUserData = tempUsers.get(code);
1910
1911 if (!tempUserData || tempUserData.email !== email) {
1912 res.writeHead(400, { 'Content-Type': 'application/json' });
1913 res.end(JSON.stringify({ success: false, message: 'Invalid verification code' }));
1914 return;
1915 }
1916
1917 if (Date.now() - tempUserData.timestamp > 30 * 1000) {
1918 tempUsers.delete(code);
1919 verificationCodes.delete(email);
1920 res.writeHead(400, { 'Content-Type': 'application/json' });
1921 res.end(JSON.stringify({ success: false, message: 'Verification code has expired. Please request a new one.' }));
1922 return;
1923 }
1924
1925 if (tempUserData.userType === 'client') {
1926 database.createClient({
1927 first_name: tempUserData.firstName || tempUserData.username.split(' ')[0] || '',
1928 last_name: tempUserData.lastName || tempUserData.username.split(' ')[1] || '',
1929 email: tempUserData.email,
1930 password: tempUserData.password
1931 }, (err, clientId) => {
1932 if (err) {
1933 console.error('Error creating client:', err);
1934 res.writeHead(400, { 'Content-Type': 'application/json' });
1935 res.end(JSON.stringify({ success: false, message: 'Registration failed' }));
1936 } else {
1937 if (tempUserData.address && tempUserData.city && tempUserData.postcode && tempUserData.country) {
1938 database.database.run(
1939 'INSERT INTO delivery_address (client_id, address, city, postcode, country, is_default) VALUES (?, ?, ?, ?, ?, ?)',
1940 [
1941 clientId,
1942 tempUserData.address,
1943 tempUserData.city,
1944 tempUserData.postcode,
1945 tempUserData.country,
1946 tempUserData.isDefaultAddress ? 1 : 0
1947 ],
1948 (err) => {
1949 if (err) {
1950 console.error('Error saving delivery address:', err);
1951 }
1952 }
1953 );
1954 }
1955
1956 tempUsers.delete(code);
1957 verificationCodes.delete(email);
1958
1959 database.logAudit(clientId, 'REGISTER_SUCCESS', 'client', clientId.toString(), 'Client registered', ipAddress);
1960
1961 res.writeHead(200, { 'Content-Type': 'application/json' });
1962 res.end(JSON.stringify({
1963 success: true,
1964 message: 'Successfully registered! You can now login.',
1965 userId: clientId,
1966 userType: 'client',
1967 redirectTo: 'login.html'
1968 }));
1969 }
1970 });
1971 } else {
1972 const userId = 'user_' + Date.now().toString().slice(-8);
1973
1974 database.createUser(userId, tempUserData.username, tempUserData.email, tempUserData.password, tempUserData.userType, (err, userId) => {
1975 if (err) {
1976 console.error('Error creating user:', err);
1977 res.writeHead(400, { 'Content-Type': 'application/json' });
1978 res.end(JSON.stringify({ success: false, message: 'Registration failed' }));
1979 } else {
1980 tempUsers.delete(code);
1981 verificationCodes.delete(email);
1982
1983 database.logAudit(userId, 'REGISTER_SUCCESS', 'user', userId.toString(), `User registered as ${tempUserData.userType}`, ipAddress);
1984
1985 res.writeHead(200, { 'Content-Type': 'application/json' });
1986 res.end(JSON.stringify({
1987 success: true,
1988 message: 'Successfully registered! You can now login.',
1989 userId: userId,
1990 userType: tempUserData.userType,
1991 redirectTo: 'login.html'
1992 }));
1993 }
1994 });
1995 }
1996 });
1997 }
1998
1999 else if (pathname === '/api/login' && req.method === 'POST') {
2000 let body = '';
2001 req.on('data', chunk => {
2002 body += chunk.toString();
2003 });
2004 req.on('end', () => {
2005 const { email, password } = JSON.parse(body);
2006
2007 console.log(`๐Ÿ” Login attempt for email: ${email}`);
2008
2009 // First check if it's the admin user (special case)
2010 if (email === 'admin@handcraft.com') {
2011 database.getUserByUsername('admin', (err, adminUser) => {
2012 if (err || !adminUser) {
2013 console.error('Admin user not found');
2014 database.logAudit(null, 'LOGIN_FAILED', 'auth', null, `Admin login failed - user not found`, ipAddress);
2015 res.writeHead(401, { 'Content-Type': 'application/json' });
2016 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2017 return;
2018 }
2019
2020 if (database.verifyPassword(password, adminUser.password)) {
2021 const isFirstTimeLogin = adminUser.force_password_change === 1;
2022
2023 const twoFACode = generateVerificationCode();
2024 verificationCodes.set(adminUser.email, {
2025 code: twoFACode,
2026 timestamp: Date.now(),
2027 userId: adminUser.id,
2028 isFirstTimeLogin: isFirstTimeLogin,
2029 userType: 'admin',
2030 needsPasswordChange: isFirstTimeLogin
2031 });
2032
2033 console.log(`โฐ Generated 2FA code for admin ${adminUser.email}`);
2034
2035 send2FACode(adminUser.email, twoFACode)
2036 .then(() => {
2037 res.writeHead(200, { 'Content-Type': 'application/json' });
2038 res.end(JSON.stringify({
2039 success: true,
2040 message: 'Two-factor authentication code sent to your email',
2041 requires2FA: true,
2042 email: adminUser.email,
2043 username: adminUser.username,
2044 isFirstTimeLogin: isFirstTimeLogin,
2045 userType: 'admin'
2046 }));
2047 })
2048 .catch(error => {
2049 console.error('Error sending 2FA email:', error);
2050 res.writeHead(200, { 'Content-Type': 'application/json' });
2051 res.end(JSON.stringify({
2052 success: true,
2053 message: 'Two-factor authentication required',
2054 requires2FA: true,
2055 email: adminUser.email,
2056 username: adminUser.username,
2057 isFirstTimeLogin: isFirstTimeLogin,
2058 userType: 'admin',
2059 developmentCode: twoFACode
2060 }));
2061 });
2062 } else {
2063 database.logAudit(adminUser.id, 'LOGIN_FAILED', 'auth', adminUser.id.toString(), 'Invalid password for admin', ipAddress);
2064 res.writeHead(401, { 'Content-Type': 'application/json' });
2065 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2066 }
2067 });
2068
2069 return;
2070 }
2071
2072 // First check if it's a client
2073 database.getClientByEmail(email, (err, client) => {
2074 if (err) {
2075 console.error('Error checking client:', err);
2076 }
2077
2078 if (client) {
2079 console.log(`๐Ÿ” Found client: ${client.email}`);
2080
2081 if (!client.password) {
2082 console.log('โŒ Client has no password set');
2083 database.logAudit(client.client_ID, 'LOGIN_FAILED', 'auth', client.client_ID?.toString() || 'unknown', 'Client has no password', ipAddress);
2084 res.writeHead(401, { 'Content-Type': 'application/json' });
2085 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2086 return;
2087 }
2088
2089 database.verifyClientPassword(password, client.password, (err, isValid) => {
2090 if (err || !isValid) {
2091 const clientId = client.client_ID || 'unknown';
2092 database.logAudit(clientId, 'LOGIN_FAILED', 'auth',
2093 typeof clientId === 'string' ? clientId : String(clientId),
2094 'Invalid password for client', ipAddress);
2095 res.writeHead(401, { 'Content-Type': 'application/json' });
2096 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2097 return;
2098 }
2099
2100 // Clients go directly to dashboard (no 2FA)
2101 const sessionId = generateSessionId();
2102 const clientId = client.client_ID;
2103 sessions.set(sessionId, `client_${clientId}`);
2104
2105 console.log(`โœ… Client login successful. Session: ${sessionId}, User: client_${clientId}`);
2106
2107 database.logAudit(clientId, 'LOGIN_SUCCESS', 'auth',
2108 typeof clientId === 'string' ? clientId : String(clientId),
2109 'Client logged in successfully', ipAddress);
2110
2111 res.writeHead(200, {
2112 'Content-Type': 'application/json',
2113 'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict`
2114 });
2115
2116 res.end(JSON.stringify({
2117 success: true,
2118 message: 'Successfully logged in',
2119 user: {
2120 id: clientId,
2121 firstName: client.first_name,
2122 lastName: client.last_name,
2123 email: client.email,
2124 userType: 'client'
2125 },
2126 redirectTo: 'client-dashboard.html'
2127 }));
2128 });
2129
2130 return;
2131 }
2132
2133 // If not client, check personal table
2134 database.getPersonalByEmail(email, (err, personal) => {
2135 if (err) {
2136 console.error('Error checking personal:', err);
2137 }
2138
2139 if (personal) {
2140 console.log(`๐Ÿ” Found personal user: ${personal.email}`);
2141
2142 if (!personal.password) {
2143 console.log('โŒ Personal has no password set');
2144 database.logAudit(personal.id, 'LOGIN_FAILED', 'auth', personal.id, 'Personal has no password', ipAddress);
2145 res.writeHead(401, { 'Content-Type': 'application/json' });
2146 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2147 return;
2148 }
2149
2150 database.verifyClientPassword(password, personal.password, (err, isValid) => {
2151 if (err || !isValid) {
2152 database.logAudit(personal.id, 'LOGIN_FAILED', 'auth', personal.id, 'Invalid password for personal', ipAddress);
2153 res.writeHead(401, { 'Content-Type': 'application/json' });
2154 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2155 return;
2156 }
2157
2158 // Check if this is a boss (store owner)
2159 database.database.get(
2160 'SELECT boss_id FROM boss WHERE boss_id = ?',
2161 [personal.id],
2162 (err, boss) => {
2163 if (err) {
2164 console.error('Error checking boss status:', err);
2165 }
2166
2167 if (boss) {
2168 // This is a store owner
2169 // Check if first time login from users table
2170 database.database.get(
2171 'SELECT force_password_change FROM users WHERE email = ?',
2172 [email],
2173 (err, user) => {
2174 const isFirstTimeLogin = user && user.force_password_change === 1;
2175
2176 const twoFACode = generateVerificationCode();
2177 verificationCodes.set(personal.email, {
2178 code: twoFACode,
2179 timestamp: Date.now(),
2180 userId: personal.id,
2181 isFirstTimeLogin: isFirstTimeLogin,
2182 userType: 'store_owner',
2183 needsPasswordChange: isFirstTimeLogin
2184 });
2185
2186 console.log(`โฐ Generated 2FA code for store owner ${personal.email}`);
2187
2188 send2FACode(personal.email, twoFACode)
2189 .then(() => {
2190 res.writeHead(200, { 'Content-Type': 'application/json' });
2191 res.end(JSON.stringify({
2192 success: true,
2193 message: 'Two-factor authentication code sent to your email',
2194 requires2FA: true,
2195 email: personal.email,
2196 isFirstTimeLogin: isFirstTimeLogin,
2197 userType: 'store_owner'
2198 }));
2199 })
2200 .catch(error => {
2201 console.error('Error sending 2FA email:', error);
2202 res.writeHead(200, { 'Content-Type': 'application/json' });
2203 res.end(JSON.stringify({
2204 success: true,
2205 message: 'Two-factor authentication required',
2206 requires2FA: true,
2207 email: personal.email,
2208 isFirstTimeLogin: isFirstTimeLogin,
2209 userType: 'store_owner',
2210 developmentCode: twoFACode
2211 }));
2212 });
2213 }
2214 );
2215
2216 return;
2217 }
2218
2219 // Check if this is an employee
2220 database.database.get(
2221 'SELECT employee_id FROM employees WHERE employee_id = ?',
2222 [personal.id],
2223 (err, employee) => {
2224 if (err) {
2225 console.error('Error checking employee status:', err);
2226 }
2227
2228 if (employee) {
2229 // This is an employee
2230 database.database.get(
2231 'SELECT force_password_change FROM users WHERE email = ?',
2232 [email],
2233 (err, user) => {
2234 const isFirstTimeLogin = user && user.force_password_change === 1;
2235
2236 const twoFACode = generateVerificationCode();
2237 verificationCodes.set(personal.email, {
2238 code: twoFACode,
2239 timestamp: Date.now(),
2240 userId: personal.id,
2241 isFirstTimeLogin: isFirstTimeLogin,
2242 userType: 'store_employee',
2243 needsPasswordChange: isFirstTimeLogin
2244 });
2245
2246 console.log(`โฐ Generated 2FA code for employee ${personal.email}`);
2247
2248 send2FACode(personal.email, twoFACode)
2249 .then(() => {
2250 res.writeHead(200, { 'Content-Type': 'application/json' });
2251 res.end(JSON.stringify({
2252 success: true,
2253 message: 'Two-factor authentication code sent to your email',
2254 requires2FA: true,
2255 email: personal.email,
2256 isFirstTimeLogin: isFirstTimeLogin,
2257 userType: 'store_employee'
2258 }));
2259 })
2260 .catch(error => {
2261 console.error('Error sending 2FA email:', error);
2262 res.writeHead(200, { 'Content-Type': 'application/json' });
2263 res.end(JSON.stringify({
2264 success: true,
2265 message: 'Two-factor authentication required',
2266 requires2FA: true,
2267 email: personal.email,
2268 isFirstTimeLogin: isFirstTimeLogin,
2269 userType: 'store_employee',
2270 developmentCode: twoFACode
2271 }));
2272 });
2273 }
2274 );
2275
2276 return;
2277 }
2278
2279 // If we get here, it's a personal record without boss/employee status
2280 // Treat as regular user
2281 database.database.get(
2282 'SELECT * FROM users WHERE email = ?',
2283 [email],
2284 (err, user) => {
2285 if (err || !user) {
2286 database.getUserByUsername(email, (err, userByUsername) => {
2287 if (err || !userByUsername) {
2288 database.logAudit(null, 'LOGIN_FAILED', 'auth', null, `Failed login attempt for email: ${email}`, ipAddress);
2289 res.writeHead(401, { 'Content-Type': 'application/json' });
2290 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2291 return;
2292 }
2293
2294 if (database.verifyPassword(password, userByUsername.password)) {
2295 const isFirstTimeLogin = userByUsername.force_password_change === 1;
2296
2297 const twoFACode = generateVerificationCode();
2298 verificationCodes.set(userByUsername.email, {
2299 code: twoFACode,
2300 timestamp: Date.now(),
2301 userId: userByUsername.id,
2302 isFirstTimeLogin: isFirstTimeLogin,
2303 userType: userByUsername.user_type,
2304 needsPasswordChange: isFirstTimeLogin
2305 });
2306
2307 send2FACode(userByUsername.email, twoFACode)
2308 .then(() => {
2309 res.writeHead(200, { 'Content-Type': 'application/json' });
2310 res.end(JSON.stringify({
2311 success: true,
2312 message: 'Two-factor authentication code sent to your email',
2313 requires2FA: true,
2314 email: userByUsername.email,
2315 username: userByUsername.username,
2316 isFirstTimeLogin: isFirstTimeLogin,
2317 userType: userByUsername.user_type
2318 }));
2319 })
2320 .catch(error => {
2321 console.error('Error sending 2FA email:', error);
2322 res.writeHead(200, { 'Content-Type': 'application/json' });
2323 res.end(JSON.stringify({
2324 success: true,
2325 message: 'Two-factor authentication required',
2326 requires2FA: true,
2327 email: userByUsername.email,
2328 username: userByUsername.username,
2329 isFirstTimeLogin: isFirstTimeLogin,
2330 userType: userByUsername.user_type,
2331 developmentCode: twoFACode
2332 }));
2333 });
2334 } else {
2335 database.logAudit(userByUsername.id, 'LOGIN_FAILED', 'auth', userByUsername.id.toString(), 'Invalid password', ipAddress);
2336 res.writeHead(401, { 'Content-Type': 'application/json' });
2337 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2338 }
2339 });
2340
2341 return;
2342 }
2343
2344 if (database.verifyPassword(password, user.password)) {
2345 const isFirstTimeLogin = user.force_password_change === 1;
2346
2347 const twoFACode = generateVerificationCode();
2348 verificationCodes.set(user.email, {
2349 code: twoFACode,
2350 timestamp: Date.now(),
2351 userId: user.id,
2352 isFirstTimeLogin: isFirstTimeLogin,
2353 userType: user.user_type,
2354 needsPasswordChange: isFirstTimeLogin
2355 });
2356
2357 send2FACode(user.email, twoFACode)
2358 .then(() => {
2359 res.writeHead(200, { 'Content-Type': 'application/json' });
2360 res.end(JSON.stringify({
2361 success: true,
2362 message: 'Two-factor authentication code sent to your email',
2363 requires2FA: true,
2364 email: user.email,
2365 username: user.username,
2366 isFirstTimeLogin: isFirstTimeLogin,
2367 userType: user.user_type
2368 }));
2369 })
2370 .catch(error => {
2371 console.error('Error sending 2FA email:', error);
2372 res.writeHead(200, { 'Content-Type': 'application/json' });
2373 res.end(JSON.stringify({
2374 success: true,
2375 message: 'Two-factor authentication required',
2376 requires2FA: true,
2377 email: user.email,
2378 username: user.username,
2379 isFirstTimeLogin: isFirstTimeLogin,
2380 userType: user.user_type,
2381 developmentCode: twoFACode
2382 }));
2383 });
2384 } else {
2385 database.logAudit(user.id, 'LOGIN_FAILED', 'auth', user.id.toString(), 'Invalid password', ipAddress);
2386 res.writeHead(401, { 'Content-Type': 'application/json' });
2387 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2388 }
2389 }
2390 );
2391 }
2392 );
2393 }
2394 );
2395 });
2396
2397 return;
2398 }
2399
2400 // No user found in any table
2401 database.logAudit(null, 'LOGIN_FAILED', 'auth', null, `Failed login attempt for email: ${email}`, ipAddress);
2402 res.writeHead(401, { 'Content-Type': 'application/json' });
2403 res.end(JSON.stringify({ success: false, message: 'Invalid email or password' }));
2404 });
2405 });
2406 });
2407 }
2408
2409 else if (pathname === '/api/resend-2fa' && req.method === 'POST') {
2410 let body = '';
2411 req.on('data', chunk => {
2412 body += chunk.toString();
2413 });
2414 req.on('end', () => {
2415 const { email } = JSON.parse(body);
2416
2417 if (!email) {
2418 res.writeHead(400, { 'Content-Type': 'application/json' });
2419 res.end(JSON.stringify({ success: false, message: 'Email is required' }));
2420 return;
2421 }
2422
2423 database.database.get(
2424 'SELECT * FROM users WHERE email = ?',
2425 [email],
2426 (err, user) => {
2427 if (err || !user) {
2428 database.getUserByUsername(email, (err, userByUsername) => {
2429 if (err || !userByUsername) {
2430 res.writeHead(400, { 'Content-Type': 'application/json' });
2431 res.end(JSON.stringify({ success: false, message: 'User not found' }));
2432 return;
2433 }
2434
2435 const newTwoFACode = generateVerificationCode();
2436 verificationCodes.set(userByUsername.email, {
2437 code: newTwoFACode,
2438 timestamp: Date.now(),
2439 userId: userByUsername.id,
2440 isFirstTimeLogin: userByUsername.force_password_change === 1,
2441 needsPasswordChange: userByUsername.force_password_change === 1,
2442 userType: userByUsername.user_type
2443 });
2444
2445 console.log(`๐Ÿ”„ Resent 2FA code for ${userByUsername.email}, expires in 30 seconds`);
2446
2447 send2FACode(userByUsername.email, newTwoFACode)
2448 .then(() => {
2449 res.writeHead(200, { 'Content-Type': 'application/json' });
2450 res.end(JSON.stringify({
2451 success: true,
2452 message: 'New two-factor authentication code sent to your email (expires in 30 seconds)',
2453 email: userByUsername.email
2454 }));
2455 })
2456 .catch(error => {
2457 console.error('Error sending 2FA email:', error.message);
2458 res.writeHead(200, { 'Content-Type': 'application/json' });
2459 res.end(JSON.stringify({
2460 success: true,
2461 message: 'New two-factor authentication code generated (check console, expires in 30 seconds)',
2462 email: userByUsername.email,
2463 developmentCode: newTwoFACode
2464 }));
2465 });
2466 });
2467
2468 return;
2469 }
2470
2471 const newTwoFACode = generateVerificationCode();
2472 verificationCodes.set(user.email, {
2473 code: newTwoFACode,
2474 timestamp: Date.now(),
2475 userId: user.id,
2476 isFirstTimeLogin: user.force_password_change === 1,
2477 needsPasswordChange: user.force_password_change === 1,
2478 userType: user.user_type
2479 });
2480
2481 console.log(`๐Ÿ”„ Resent 2FA code for ${user.email}, expires in 30 seconds`);
2482
2483 send2FACode(user.email, newTwoFACode)
2484 .then(() => {
2485 res.writeHead(200, { 'Content-Type': 'application/json' });
2486 res.end(JSON.stringify({
2487 success: true,
2488 message: 'New two-factor authentication code sent to your email (expires in 30 seconds)',
2489 email: user.email
2490 }));
2491 })
2492 .catch(error => {
2493 console.error('Error sending 2FA email:', error.message);
2494 res.writeHead(200, { 'Content-Type': 'application/json' });
2495 res.end(JSON.stringify({
2496 success: true,
2497 message: 'New two-factor authentication code generated (check console, expires in 30 seconds)',
2498 email: user.email,
2499 developmentCode: newTwoFACode
2500 }));
2501 });
2502 }
2503 );
2504 });
2505 }
2506
2507 else if (pathname === '/api/verify-2fa' && req.method === 'POST') {
2508 let body = '';
2509 req.on('data', chunk => {
2510 body += chunk.toString();
2511 });
2512 req.on('end', () => {
2513 const { email, code } = JSON.parse(body);
2514
2515 if (!email || !code) {
2516 res.writeHead(400, { 'Content-Type': 'application/json' });
2517 res.end(JSON.stringify({ success: false, message: 'All fields are required' }));
2518 return;
2519 }
2520
2521 const verificationData = verificationCodes.get(email);
2522
2523 if (!verificationData || verificationData.code !== code) {
2524 res.writeHead(400, { 'Content-Type': 'application/json' });
2525 res.end(JSON.stringify({ success: false, message: 'Invalid two-factor authentication code' }));
2526 return;
2527 }
2528
2529 if (Date.now() - verificationData.timestamp > 30 * 1000) {
2530 verificationCodes.delete(email);
2531 res.writeHead(400, { 'Content-Type': 'application/json' });
2532 res.end(JSON.stringify({ success: false, message: 'Two-factor authentication code has expired. Please request a new one.' }));
2533 return;
2534 }
2535
2536 // Check if this is a first-time login that requires password change
2537 if (verificationData.needsPasswordChange) {
2538 const tempSessionId = generateSessionId();
2539 tempAdminSessions.set(tempSessionId, verificationData.userId);
2540
2541 database.logAudit(verificationData.userId, 'LOGIN_2FA_SUCCESS_PASSWORD_CHANGE_REQUIRED', 'auth', verificationData.userId.toString(),
2542 `${verificationData.userType} first login, password change required`, ipAddress);
2543
2544 verificationCodes.delete(email);
2545
2546 // Determine redirect based on user type
2547 let redirectTo = 'change-password.html?forced=true';
2548 if (verificationData.userType === 'store_owner') {
2549 redirectTo = 'change-password.html?forced=true&redirect=store-owner.html';
2550 } else if (verificationData.userType === 'store_employee') {
2551 redirectTo = 'change-password.html?forced=true&redirect=store-employee.html';
2552 } else if (verificationData.userType === 'admin') {
2553 redirectTo = 'change-password.html?forced=true&redirect=admin.html';
2554 } else if (verificationData.userType === 'client') {
2555 redirectTo = 'change-password.html?forced=true&redirect=client-dashboard.html';
2556 }
2557
2558 res.writeHead(200, {
2559 'Content-Type': 'application/json',
2560 'Set-Cookie': `sessionId=${tempSessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict`
2561 });
2562
2563 res.end(JSON.stringify({
2564 success: true,
2565 message: 'Two-factor authentication successful. Password change required.',
2566 requiresPasswordChange: true,
2567 userType: verificationData.userType,
2568 redirectTo: redirectTo
2569 }));
2570
2571 return;
2572 }
2573
2574 // Regular login - create session and redirect based on user type
2575 const sessionId = generateSessionId();
2576
2577 // Determine how to store the user ID in session
2578 if (verificationData.userType === 'client') {
2579 sessions.set(sessionId, `client_${verificationData.userId}`);
2580 } else if (verificationData.userType === 'store_owner' || verificationData.userType === 'store_employee') {
2581 sessions.set(sessionId, `personal_${verificationData.userId}`);
2582 } else {
2583 sessions.set(sessionId, verificationData.userId.toString());
2584 }
2585
2586 verificationCodes.delete(email);
2587
2588 database.logAudit(verificationData.userId, 'LOGIN_SUCCESS', 'auth', verificationData.userId.toString(),
2589 `${verificationData.userType} logged in successfully`, ipAddress);
2590
2591 // Determine redirect based on user type
2592 let redirectTo = '';
2593
2594 switch(verificationData.userType) {
2595 case 'client':
2596 redirectTo = 'client-dashboard.html';
2597 break;
2598 case 'store_owner':
2599 redirectTo = 'store-owner.html';
2600 break;
2601 case 'store_employee':
2602 redirectTo = 'store-employee.html';
2603 break;
2604 case 'admin':
2605 redirectTo = 'admin.html';
2606 break;
2607 default:
2608 redirectTo = 'dashboard.html';
2609 }
2610
2611 console.log(`โœ… ${verificationData.userType} login successful. Redirecting to: ${redirectTo}`);
2612
2613 res.writeHead(200, {
2614 'Content-Type': 'application/json',
2615 'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict`
2616 });
2617
2618 res.end(JSON.stringify({
2619 success: true,
2620 message: 'Successfully logged in',
2621 userType: verificationData.userType,
2622 redirectTo: redirectTo
2623 }));
2624 });
2625 }
2626
2627 else if (pathname === '/api/logout' && req.method === 'POST') {
2628 const cookies = parseCookies(req);
2629 const sessionId = cookies.sessionId;
2630
2631 if (sessionId) {
2632 const userId = sessions.get(sessionId);
2633 if (userId) {
2634 database.logAudit(userId, 'LOGOUT', 'auth', userId.toString(), 'User logged out', ipAddress);
2635 }
2636 sessions.delete(sessionId);
2637 tempAdminSessions.delete(sessionId);
2638 }
2639
2640 res.writeHead(200, {
2641 'Content-Type': 'application/json',
2642 'Set-Cookie': 'sessionId=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict'
2643 });
2644
2645 res.end(JSON.stringify({ success: true, message: 'Successfully logged out' }));
2646 }
2647
2648 else if (pathname === '/api/user' && req.method === 'GET') {
2649 requireAuth(req, res, (userId) => {
2650 const cookies = parseCookies(req);
2651 const sessionId = cookies.sessionId;
2652
2653 if (tempAdminSessions.has(sessionId)) {
2654 // This is a temporary session (password change required)
2655 // Get user info to determine type
2656 database.getUserById(userId, (err, user) => {
2657 if (err || !user) {
2658 // Check if it's a personal user
2659 database.getPersonalById(userId, (err, personal) => {
2660 if (err || !personal) {
2661 res.writeHead(200, { 'Content-Type': 'application/json' });
2662 res.end(JSON.stringify({
2663 success: true,
2664 user: {
2665 id: userId,
2666 username: 'admin',
2667 userType: 'admin',
2668 needsPasswordChange: true
2669 },
2670 isTempSession: true
2671 }));
2672 } else {
2673 // Personal user (store owner/employee)
2674 database.database.get(
2675 'SELECT boss_id FROM boss WHERE boss_id = ?',
2676 [userId],
2677 (err, boss) => {
2678 let userType = 'store_employee';
2679 if (boss) {
2680 userType = 'store_owner';
2681 }
2682
2683 res.writeHead(200, { 'Content-Type': 'application/json' });
2684 res.end(JSON.stringify({
2685 success: true,
2686 user: {
2687 id: personal.id,
2688 firstName: personal.first_name,
2689 lastName: personal.last_name,
2690 email: personal.email,
2691 userType: userType,
2692 needsPasswordChange: true
2693 },
2694 isTempSession: true
2695 }));
2696 }
2697 );
2698 }
2699 });
2700 } else {
2701 // Regular user (admin)
2702 res.writeHead(200, { 'Content-Type': 'application/json' });
2703 res.end(JSON.stringify({
2704 success: true,
2705 user: {
2706 id: user.id,
2707 username: user.username,
2708 email: user.email,
2709 userType: user.user_type || 'admin',
2710 needsPasswordChange: true
2711 },
2712 isTempSession: true
2713 }));
2714 }
2715 });
2716
2717 return;
2718 }
2719
2720 // Regular session
2721 const userIdStr = String(userId);
2722
2723 if (userIdStr === '000000') {
2724 // Admin user
2725 database.getUserById(userIdStr, (err, user) => {
2726 if (err || !user) {
2727 res.writeHead(404, { 'Content-Type': 'application/json' });
2728 res.end(JSON.stringify({ success: false, message: 'User not found' }));
2729 } else {
2730 res.writeHead(200, { 'Content-Type': 'application/json' });
2731 res.end(JSON.stringify({
2732 success: true,
2733 user: {
2734 id: user.id,
2735 username: user.username,
2736 email: user.email,
2737 userType: 'admin'
2738 }
2739 }));
2740 }
2741 });
2742 }
2743 else if (userIdStr.startsWith('client_')) {
2744 const clientId = parseInt(userIdStr.replace('client_', ''));
2745
2746 database.getClientById(clientId, (err, client) => {
2747 if (err || !client) {
2748 res.writeHead(404, { 'Content-Type': 'application/json' });
2749 res.end(JSON.stringify({ success: false, message: 'User not found' }));
2750 } else {
2751 res.writeHead(200, { 'Content-Type': 'application/json' });
2752 res.end(JSON.stringify({
2753 success: true,
2754 user: {
2755 id: client.client_ID,
2756 firstName: client.first_name,
2757 lastName: client.last_name,
2758 email: client.email,
2759 userType: 'client'
2760 }
2761 }));
2762 }
2763 });
2764 }
2765 else if (userIdStr.startsWith('personal_')) {
2766 const personalId = userIdStr.replace('personal_', '');
2767
2768 database.getPersonalById(personalId, (err, personal) => {
2769 if (err || !personal) {
2770 res.writeHead(404, { 'Content-Type': 'application/json' });
2771 res.end(JSON.stringify({ success: false, message: 'User not found' }));
2772 return;
2773 }
2774
2775 database.database.get(
2776 'SELECT boss_id FROM boss WHERE boss_id = ?',
2777 [personalId],
2778 (err, boss) => {
2779 if (err) {
2780 console.error('Error checking boss:', err);
2781 }
2782
2783 if (boss) {
2784 database.database.all(
2785 `SELECT s.* FROM store s
2786 JOIN works_in_store w ON s.store_id = w.store_id
2787 WHERE w.personal_id = ?`,
2788 [personalId],
2789 (err, stores) => {
2790 if (err) {
2791 console.error('Error getting stores:', err);
2792 stores = [];
2793 }
2794
2795 res.writeHead(200, { 'Content-Type': 'application/json' });
2796 res.end(JSON.stringify({
2797 success: true,
2798 user: {
2799 id: personal.id,
2800 firstName: personal.first_name,
2801 lastName: personal.last_name,
2802 email: personal.email,
2803 userType: 'store_owner',
2804 stores: stores
2805 }
2806 }));
2807 }
2808 );
2809 } else {
2810 database.database.get(
2811 'SELECT employee_id FROM employees WHERE employee_id = ?',
2812 [personalId],
2813 (err, employee) => {
2814 if (err) {
2815 console.error('Error checking employee:', err);
2816 }
2817
2818 if (employee) {
2819 database.database.all(
2820 `SELECT s.* FROM store s
2821 JOIN works_in_store w ON s.store_id = w.store_id
2822 WHERE w.personal_id = ?`,
2823 [personalId],
2824 (err, stores) => {
2825 if (err) {
2826 console.error('Error getting stores:', err);
2827 stores = [];
2828 }
2829
2830 res.writeHead(200, { 'Content-Type': 'application/json' });
2831 res.end(JSON.stringify({
2832 success: true,
2833 user: {
2834 id: personal.id,
2835 firstName: personal.first_name,
2836 lastName: personal.last_name,
2837 email: personal.email,
2838 userType: 'store_employee',
2839 stores: stores
2840 }
2841 }));
2842 }
2843 );
2844 } else {
2845 res.writeHead(404, { 'Content-Type': 'application/json' });
2846 res.end(JSON.stringify({ success: false, message: 'User type not recognized' }));
2847 }
2848 }
2849 );
2850 }
2851 }
2852 );
2853 });
2854 } else {
2855 database.getUserById(userIdStr, (err, user) => {
2856 if (err || !user) {
2857 res.writeHead(404, { 'Content-Type': 'application/json' });
2858 res.end(JSON.stringify({ success: false, message: 'User not found' }));
2859 } else {
2860 res.writeHead(200, { 'Content-Type': 'application/json' });
2861 res.end(JSON.stringify({ success: true, user }));
2862 }
2863 });
2864 }
2865 });
2866 }
2867
2868 else if (pathname === '/api/products' && req.method === 'GET') {
2869 const query = parsedUrl.query;
2870 const categoryId = query.category;
2871 const searchTerm = query.search;
2872
2873 database.getProducts(categoryId, searchTerm, (err, products) => {
2874 if (err) {
2875 res.writeHead(500, { 'Content-Type': 'application/json' });
2876 res.end(JSON.stringify({ success: false, message: 'Error fetching products' }));
2877 } else {
2878 res.writeHead(200, { 'Content-Type': 'application/json' });
2879 res.end(JSON.stringify({ success: true, products }));
2880 }
2881 });
2882 }
2883
2884 else if (pathname === '/api/product' && req.method === 'GET') {
2885 const productId = parsedUrl.query.id;
2886
2887 if (!productId) {
2888 res.writeHead(400, { 'Content-Type': 'application/json' });
2889 res.end(JSON.stringify({ success: false, message: 'Product ID is required' }));
2890 return;
2891 }
2892
2893 database.getProductById(productId, (err, product) => {
2894 if (err) {
2895 res.writeHead(500, { 'Content-Type': 'application/json' });
2896 res.end(JSON.stringify({ success: false, message: 'Error fetching product' }));
2897 } else if (!product) {
2898 res.writeHead(404, { 'Content-Type': 'application/json' });
2899 res.end(JSON.stringify({ success: false, message: 'Product not found' }));
2900 } else {
2901 res.writeHead(200, { 'Content-Type': 'application/json' });
2902 res.end(JSON.stringify({ success: true, product }));
2903 }
2904 });
2905 }
2906
2907 else if (pathname === '/api/create-category' && req.method === 'POST') {
2908 requireStoreOwner()(req, res, (personalId) => {
2909 let body = '';
2910 req.on('data', chunk => {
2911 body += chunk.toString();
2912 });
2913 req.on('end', () => {
2914 const categoryData = JSON.parse(body);
2915
2916 if (!categoryData.name || !categoryData.name.trim()) {
2917 res.writeHead(400, { 'Content-Type': 'application/json' });
2918 res.end(JSON.stringify({ success: false, message: 'Category name is required' }));
2919 return;
2920 }
2921
2922 const dbCategoryData = {
2923 name: categoryData.name.trim(),
2924 description: (categoryData.description || '').trim(),
2925 parent_id: categoryData.parentId ? parseInt(categoryData.parentId) : null
2926 };
2927
2928 database.createCategory(dbCategoryData, (err, category) => {
2929 if (err) {
2930 console.error('Error creating category:', err);
2931 res.writeHead(500, { 'Content-Type': 'application/json' });
2932 res.end(JSON.stringify({ success: false, message: 'Error creating category: ' + err.message }));
2933 } else if (!category) {
2934 res.writeHead(500, { 'Content-Type': 'application/json' });
2935 res.end(JSON.stringify({ success: false, message: 'Failed to create category' }));
2936 } else {
2937 database.logAudit(personalId, 'CATEGORY_CREATED', 'category', category.id.toString(), `New category created: ${category.name}`, ipAddress);
2938
2939 res.writeHead(200, { 'Content-Type': 'application/json' });
2940 res.end(JSON.stringify({
2941 success: true,
2942 message: 'Category created successfully',
2943 category: {
2944 id: category.id,
2945 name: category.name,
2946 parent_id: category.parent_id,
2947 description: category.description
2948 }
2949 }));
2950 }
2951 });
2952 });
2953 });
2954 }
2955
2956 else if (pathname === '/api/categories' && req.method === 'GET') {
2957 database.getCategoriesWithParents((err, categories) => {
2958 if (err) {
2959 console.error('Error fetching categories:', err);
2960 database.getCategories((err, categories) => {
2961 if (err) {
2962 console.error('Error fetching categories (fallback):', err);
2963 res.writeHead(500, { 'Content-Type': 'application/json' });
2964 res.end(JSON.stringify({ success: false, message: 'Error fetching categories' }));
2965 } else {
2966 res.writeHead(200, { 'Content-Type': 'application/json' });
2967 res.end(JSON.stringify({ success: true, categories: categories || [] }));
2968 }
2969 });
2970 } else {
2971 res.writeHead(200, { 'Content-Type': 'application/json' });
2972 res.end(JSON.stringify({ success: true, categories: categories || [] }));
2973 }
2974 });
2975 }
2976
2977 else if (pathname === '/api/stores' && req.method === 'GET') {
2978 database.getStores((err, stores) => {
2979 if (err) {
2980 res.writeHead(500, { 'Content-Type': 'application/json' });
2981 res.end(JSON.stringify({ success: false, message: 'Error fetching stores' }));
2982 } else {
2983 res.writeHead(200, { 'Content-Type': 'application/json' });
2984 res.end(JSON.stringify({ success: true, stores }));
2985 }
2986 });
2987 }
2988
2989 else if (pathname === '/api/create-order' && req.method === 'POST') {
2990 requireAuth(req, res, (userId) => {
2991 let body = '';
2992 req.on('data', chunk => {
2993 body += chunk.toString();
2994 });
2995 req.on('end', () => {
2996 const orderData = JSON.parse(body);
2997 const userIdStr = String(userId);
2998
2999 if (userIdStr.startsWith('client_')) {
3000 const clientId = parseInt(userIdStr.replace('client_', ''));
3001 const storeId = orderData.storeId;
3002
3003 if (!storeId) {
3004 res.writeHead(400, { 'Content-Type': 'application/json' });
3005 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
3006 return;
3007 }
3008
3009 const year = new Date().getFullYear().toString().slice(-3);
3010
3011 database.database.get(
3012 'SELECT COUNT(*) as order_count FROM "order" WHERE store_id = ? AND strftime("%Y", order_date) = ?',
3013 [storeId, new Date().getFullYear().toString()],
3014 (err, result) => {
3015 if (err) {
3016 console.error('Error counting orders:', err);
3017 res.writeHead(500, { 'Content-Type': 'application/json' });
3018 res.end(JSON.stringify({ success: false, message: 'Error generating order ID' }));
3019 return;
3020 }
3021
3022 const orderCount = result ? result.order_count + 1 : 1;
3023 const orderNumPadded = orderCount.toString().padStart(5, '0');
3024
3025 // Format order number: storeId + year (3 digits) + orderNum (5 digits)
3026 const orderNum = storeId + year + orderNumPadded;
3027
3028 const newOrderData = {
3029 order_num: orderNum,
3030 client_id: clientId,
3031 store_id: storeId,
3032 quantity: orderData.items.reduce((sum, item) => sum + item.quantity, 0),
3033 payment_method: orderData.paymentMethod || 'credit card',
3034 discount: orderData.discount || 0,
3035 delivery_address: orderData.deliveryAddress || 'Not specified',
3036 items: orderData.items.map(item => ({
3037 product_code: item.productCode,
3038 quantity: item.quantity,
3039 price: item.price
3040 }))
3041 };
3042
3043 database.createOrderNew(newOrderData, (err, orderId) => {
3044 if (err) {
3045 res.writeHead(500, { 'Content-Type': 'application/json' });
3046 res.end(JSON.stringify({ success: false, message: 'Error creating order' }));
3047 } else {
3048 database.logAudit(clientId, 'ORDER_CREATED', 'order', orderId.toString(), 'New order created', ipAddress);
3049 res.writeHead(200, { 'Content-Type': 'application/json' });
3050 res.end(JSON.stringify({ success: true, orderId, message: 'Order created successfully' }));
3051 }
3052 });
3053 }
3054 );
3055 } else {
3056 res.writeHead(403, { 'Content-Type': 'application/json' });
3057 res.end(JSON.stringify({ success: false, message: 'Only clients can create orders' }));
3058 }
3059 });
3060 });
3061 }
3062
3063 else if (pathname === '/api/user-orders' && req.method === 'GET') {
3064 requireAuth(req, res, (userId) => {
3065 const userIdStr = String(userId);
3066
3067 if (userIdStr.startsWith('client_')) {
3068 const clientId = parseInt(userIdStr.replace('client_', ''));
3069
3070 database.getOrdersByClient(clientId, (err, orders) => {
3071 if (err) {
3072 res.writeHead(500, { 'Content-Type': 'application/json' });
3073 res.end(JSON.stringify({ success: false, message: 'Error fetching orders' }));
3074 } else {
3075 res.writeHead(200, { 'Content-Type': 'application/json' });
3076 res.end(JSON.stringify({ success: true, orders }));
3077 }
3078 });
3079 } else {
3080 res.writeHead(403, { 'Content-Type': 'application/json' });
3081 res.end(JSON.stringify({ success: false, message: 'Only clients can view orders' }));
3082 }
3083 });
3084 }
3085
3086 else if (pathname === '/api/create-review' && req.method === 'POST') {
3087 requireAuth(req, res, (userId) => {
3088 let body = '';
3089 req.on('data', chunk => {
3090 body += chunk.toString();
3091 });
3092 req.on('end', () => {
3093 const reviewData = JSON.parse(body);
3094 const userIdStr = String(userId);
3095
3096 if (userIdStr.startsWith('client_')) {
3097 const clientId = parseInt(userIdStr.replace('client_', ''));
3098 reviewData.client_id = clientId;
3099
3100 database.createReviewNew(reviewData, (err, reviewId) => {
3101 if (err) {
3102 res.writeHead(500, { 'Content-Type': 'application/json' });
3103 res.end(JSON.stringify({ success: false, message: 'Error creating review' }));
3104 } else {
3105 database.logAudit(clientId, 'REVIEW_CREATED', 'review', reviewId.toString(), 'New review created', ipAddress);
3106 res.writeHead(200, { 'Content-Type': 'application/json' });
3107 res.end(JSON.stringify({ success: true, reviewId, message: 'Review created successfully' }));
3108 }
3109 });
3110 } else {
3111 res.writeHead(403, { 'Content-Type': 'application/json' });
3112 res.end(JSON.stringify({ success: false, message: 'Only clients can create reviews' }));
3113 }
3114 });
3115 });
3116 }
3117
3118 else if (pathname === '/api/create-request' && req.method === 'POST') {
3119 requireAuth(req, res, (userId) => {
3120 let body = '';
3121 req.on('data', chunk => {
3122 body += chunk.toString();
3123 });
3124 req.on('end', () => {
3125 const requestData = JSON.parse(body);
3126 const userIdStr = String(userId);
3127
3128 if (userIdStr.startsWith('client_')) {
3129 const clientId = parseInt(userIdStr.replace('client_', ''));
3130 const storeId = requestData.storeId;
3131
3132 if (!storeId) {
3133 res.writeHead(400, { 'Content-Type': 'application/json' });
3134 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
3135 return;
3136 }
3137
3138 const now = new Date();
3139 const month = (now.getMonth() + 1).toString().padStart(2, '0');
3140 const year = now.getFullYear().toString().slice(-3);
3141
3142 database.database.get(
3143 'SELECT COUNT(*) as request_count FROM request WHERE store_id = ? AND strftime("%Y", date_and_time) = ? AND strftime("%m", date_and_time) = ?',
3144 [storeId, now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')],
3145 (err, result) => {
3146 if (err) {
3147 console.error('Error counting requests:', err);
3148 res.writeHead(500, { 'Content-Type': 'application/json' });
3149 res.end(JSON.stringify({ success: false, message: 'Error generating request ID' }));
3150 return;
3151 }
3152
3153 const requestCount = result ? result.request_count + 1 : 1;
3154 const requestSeqPadded = requestCount.toString().padStart(2, '0');
3155
3156 // Format request number: storeId + month (2 digits) + year (3 digits) + clientId + seq (2 digits)
3157 const requestNum = storeId + month + year + clientId + requestSeqPadded;
3158
3159 const newRequestData = {
3160 request_num: requestNum,
3161 date_and_time: now.toISOString(),
3162 problem: requestData.problem,
3163 client_id: clientId,
3164 store_id: storeId
3165 };
3166
3167 database.createRequest(newRequestData, (err, requestId) => {
3168 if (err) {
3169 res.writeHead(500, { 'Content-Type': 'application/json' });
3170 res.end(JSON.stringify({ success: false, message: 'Error creating request' }));
3171 } else {
3172 database.logAudit(clientId, 'REQUEST_CREATED', 'request', requestId.toString(), 'New request created', ipAddress);
3173 res.writeHead(200, { 'Content-Type': 'application/json' });
3174 res.end(JSON.stringify({ success: true, requestId, message: 'Request created successfully' }));
3175 }
3176 });
3177 }
3178 );
3179 } else {
3180 res.writeHead(403, { 'Content-Type': 'application/json' });
3181 res.end(JSON.stringify({ success: false, message: 'Only clients can create requests' }));
3182 }
3183 });
3184 });
3185 }
3186
3187 else if (pathname === '/api/create-refund' && req.method === 'POST') {
3188 requireAuth(req, res, (userId) => {
3189 let body = '';
3190 req.on('data', chunk => {
3191 body += chunk.toString();
3192 });
3193 req.on('end', () => {
3194 const refundData = JSON.parse(body);
3195 const userIdStr = String(userId);
3196
3197 if (userIdStr.startsWith('client_')) {
3198 const clientId = parseInt(userIdStr.replace('client_', ''));
3199
3200 database.database.get(
3201 'SELECT store_id FROM "order" WHERE order_num = ?',
3202 [refundData.order_num],
3203 (err, result) => {
3204 if (err || !result) {
3205 res.writeHead(404, { 'Content-Type': 'application/json' });
3206 res.end(JSON.stringify({ success: false, message: 'Order not found' }));
3207 return;
3208 }
3209
3210 const storeId = result.store_id;
3211 const now = new Date();
3212 const month = (now.getMonth() + 1).toString().padStart(2, '0');
3213 const year = now.getFullYear().toString().slice(-3);
3214
3215 database.database.get(
3216 'SELECT COUNT(*) as refund_count FROM refund WHERE strftime("%Y", request_date) = ? AND strftime("%m", request_date) = ?',
3217 [now.getFullYear().toString(), (now.getMonth() + 1).toString().padStart(2, '0')],
3218 (err, result) => {
3219 if (err) {
3220 console.error('Error counting refunds:', err);
3221 res.writeHead(500, { 'Content-Type': 'application/json' });
3222 res.end(JSON.stringify({ success: false, message: 'Error generating refund ID' }));
3223 return;
3224 }
3225
3226 const refundCount = result ? result.refund_count + 1 : 1;
3227 const refundSeqPadded = refundCount.toString().padStart(2, '0');
3228
3229 // Format refund ID: storeId + month (2 digits) + year (3 digits) + seq (2 digits)
3230 const refundId = storeId + month + year + refundSeqPadded;
3231
3232 refundData.refund_id = refundId;
3233
3234 database.createRefund(refundData, (err, refundId) => {
3235 if (err) {
3236 res.writeHead(500, { 'Content-Type': 'application/json' });
3237 res.end(JSON.stringify({ success: false, message: 'Error creating refund' }));
3238 } else {
3239 database.logAudit(clientId, 'REFUND_CREATED', 'refund', refundId.toString(), 'New refund requested', ipAddress);
3240 res.writeHead(200, { 'Content-Type': 'application/json' });
3241 res.end(JSON.stringify({ success: true, refundId, message: 'Refund requested successfully' }));
3242 }
3243 });
3244 }
3245 );
3246 }
3247 );
3248 } else {
3249 res.writeHead(403, { 'Content-Type': 'application/json' });
3250 res.end(JSON.stringify({ success: false, message: 'Only clients can request refunds' }));
3251 }
3252 });
3253 });
3254 }
3255
3256 else if (pathname === '/api/add-product' && req.method === 'POST') {
3257 requireStoreOwner()(req, res, (personalId) => {
3258 let body = '';
3259 req.on('data', chunk => {
3260 body += chunk.toString();
3261 });
3262 req.on('end', () => {
3263 const productData = JSON.parse(body);
3264
3265 database.database.get(
3266 'SELECT store_id FROM works_in_store WHERE personal_id = ?',
3267 [personalId],
3268 (err, bossStore) => {
3269 if (err || !bossStore) {
3270 res.writeHead(403, { 'Content-Type': 'application/json' });
3271 res.end(JSON.stringify({ success: false, message: 'Store not found for this owner' }));
3272 return;
3273 }
3274
3275 const storeId = productData.storeId || bossStore.store_id;
3276
3277 if (!storeId) {
3278 res.writeHead(400, { 'Content-Type': 'application/json' });
3279 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
3280 return;
3281 }
3282
3283 database.database.get(
3284 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
3285 [personalId, storeId],
3286 (err, ownsStore) => {
3287 if (err || !ownsStore) {
3288 res.writeHead(403, { 'Content-Type': 'application/json' });
3289 res.end(JSON.stringify({ success: false, message: 'You are not authorized to add products to this store' }));
3290 return;
3291 }
3292
3293 // FIXED: Changed SQL syntax from SUBSTRING(code FROM 4) to SUBSTR(code, 4) for SQLite compatibility
3294 database.database.get(
3295 'SELECT MAX(CAST(SUBSTR(code, 4) AS INTEGER)) as max_product_num FROM product WHERE store_id = ?',
3296 [storeId],
3297 (err, result) => {
3298 if (err) {
3299 console.error('Error getting max product number:', err);
3300 res.writeHead(500, { 'Content-Type': 'application/json' });
3301 res.end(JSON.stringify({ success: false, message: 'Error generating product code' }));
3302 return;
3303 }
3304
3305 const maxProductNum = result?.max_product_num || 0;
3306 let nextProductNum = maxProductNum + 1;
3307
3308 // Ensure product number doesn't end with 0000
3309 while (nextProductNum % 10000 === 0) {
3310 nextProductNum++;
3311 }
3312
3313 // Format product code: storeId + productNum (4 digits, padded)
3314 const productNumPadded = nextProductNum.toString().padStart(4, '0');
3315 productData.code = storeId + productNumPadded;
3316 productData.store_id = storeId;
3317
3318 database.addProduct(personalId, productData, (err, productId) => {
3319 if (err) {
3320 console.error('Error adding product:', err);
3321 res.writeHead(500, { 'Content-Type': 'application/json' });
3322 res.end(JSON.stringify({
3323 success: false,
3324 message: 'Error adding product: ' + (err.message || 'Unknown error'),
3325 details: err.toString()
3326 }));
3327 } else {
3328 database.logAudit(personalId, 'PRODUCT_ADDED', 'product', productId.toString(), 'New product added', ipAddress);
3329 res.writeHead(200, { 'Content-Type': 'application/json' });
3330 res.end(JSON.stringify({
3331 success: true,
3332 productId,
3333 message: 'Product added successfully',
3334 productCode: productData.code
3335 }));
3336 }
3337 });
3338 }
3339 );
3340 }
3341 );
3342 }
3343 );
3344 });
3345 });
3346 }
3347
3348 else if (pathname === '/api/update-product' && req.method === 'POST') {
3349 requireStoreOwner()(req, res, (personalId) => {
3350 let body = '';
3351 req.on('data', chunk => {
3352 body += chunk.toString();
3353 });
3354 req.on('end', () => {
3355 const productData = JSON.parse(body);
3356
3357 if (!productData.code) {
3358 res.writeHead(400, { 'Content-Type': 'application/json' });
3359 res.end(JSON.stringify({ success: false, message: 'Product code is required' }));
3360 return;
3361 }
3362
3363 database.database.get(
3364 'SELECT store_id FROM product WHERE code = ?',
3365 [productData.code],
3366 (err, product) => {
3367 if (err || !product) {
3368 res.writeHead(404, { 'Content-Type': 'application/json' });
3369 res.end(JSON.stringify({ success: false, message: 'Product not found' }));
3370 return;
3371 }
3372
3373 database.database.get(
3374 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
3375 [personalId, product.store_id],
3376 (err, ownsStore) => {
3377 if (err || !ownsStore) {
3378 res.writeHead(403, { 'Content-Type': 'application/json' });
3379 res.end(JSON.stringify({ success: false, message: 'You are not authorized to update products in this store' }));
3380 return;
3381 }
3382
3383 database.updateProduct(personalId, productData, (err, changes) => {
3384 if (err) {
3385 console.error('Error updating product:', err);
3386 res.writeHead(500, { 'Content-Type': 'application/json' });
3387 res.end(JSON.stringify({ success: false, message: 'Error updating product: ' + err.message }));
3388 } else if (changes === 0) {
3389 res.writeHead(404, { 'Content-Type': 'application/json' });
3390 res.end(JSON.stringify({ success: false, message: 'Product not found or no changes made' }));
3391 } else {
3392 database.logAudit(personalId, 'PRODUCT_UPDATED', 'product', productData.code, 'Product updated', ipAddress);
3393 res.writeHead(200, { 'Content-Type': 'application/json' });
3394 res.end(JSON.stringify({ success: true, message: 'Product updated successfully' }));
3395 }
3396 });
3397 }
3398 );
3399 }
3400 );
3401 });
3402 });
3403 }
3404
3405 else if (pathname === '/api/store-reports' && req.method === 'GET') {
3406 requireRole('store_owner')(req, res, (userId, user) => {
3407 database.getStoreReports(userId, (err, reports) => {
3408 if (err) {
3409 res.writeHead(500, { 'Content-Type': 'application/json' });
3410 res.end(JSON.stringify({ success: false, message: 'Error fetching reports' }));
3411 } else {
3412 res.writeHead(200, { 'Content-Type': 'application/json' });
3413 res.end(JSON.stringify({ success: true, reports }));
3414 }
3415 });
3416 });
3417 }
3418
3419 else if (pathname === '/api/all-users' && req.method === 'GET') {
3420 requireRole('admin')(req, res, (userId, user) => {
3421 database.getAllUsers((err, users) => {
3422 if (err) {
3423 res.writeHead(500, { 'Content-Type': 'application/json' });
3424 res.end(JSON.stringify({ success: false, message: 'Error fetching users' }));
3425 } else {
3426 res.writeHead(200, { 'Content-Type': 'application/json' });
3427 res.end(JSON.stringify({ success: true, users }));
3428 }
3429 });
3430 });
3431 }
3432
3433 else if (pathname === '/api/all-orders' && req.method === 'GET') {
3434 requireRole('admin')(req, res, (userId, user) => {
3435 database.getAllOrders((err, orders) => {
3436 if (err) {
3437 res.writeHead(500, { 'Content-Type': 'application/json' });
3438 res.end(JSON.stringify({ success: false, message: 'Error fetching orders' }));
3439 } else {
3440 res.writeHead(200, { 'Content-Type': 'application/json' });
3441 res.end(JSON.stringify({ success: true, orders }));
3442 }
3443 });
3444 });
3445 }
3446
3447 // Updated /api/force-change-password endpoint with redirect handling
3448 else if (pathname === '/api/force-change-password' && req.method === 'POST') {
3449 const cookies = parseCookies(req);
3450 const sessionId = cookies.sessionId;
3451 const userId = tempAdminSessions.get(sessionId);
3452
3453 if (!userId) {
3454 res.writeHead(401, { 'Content-Type': 'application/json' });
3455 res.end(JSON.stringify({ success: false, message: 'Not authenticated or invalid session' }));
3456 return;
3457 }
3458
3459 let body = '';
3460 req.on('data', chunk => {
3461 body += chunk.toString();
3462 });
3463 req.on('end', () => {
3464 try {
3465 const { newPassword, confirmPassword, redirectTo } = JSON.parse(body);
3466
3467 if (!newPassword || !confirmPassword) {
3468 res.writeHead(400, { 'Content-Type': 'application/json' });
3469 res.end(JSON.stringify({ success: false, message: 'All fields are required' }));
3470 return;
3471 }
3472
3473 if (newPassword !== confirmPassword) {
3474 res.writeHead(400, { 'Content-Type': 'application/json' });
3475 res.end(JSON.stringify({ success: false, message: 'New passwords do not match' }));
3476 return;
3477 }
3478
3479 if (!validatePassword(newPassword)) {
3480 res.writeHead(400, { 'Content-Type': 'application/json' });
3481 res.end(JSON.stringify({
3482 success: false,
3483 message: 'Password must have at least 8 characters, including uppercase, lowercase, number and special character'
3484 }));
3485 return;
3486 }
3487
3488 // First, try to find the user in the users table (for admin)
3489 database.getUserById(userId, (err, user) => {
3490 if (err) {
3491 console.error('Error finding user by ID:', err);
3492 }
3493
3494 if (user) {
3495 // Found in users table (admin or regular user)
3496 console.log('Found user in users table:', user);
3497
3498 const hashedPassword = bcrypt.hashSync(newPassword, 10);
3499
3500 database.database.run(
3501 'UPDATE users SET password = ?, force_password_change = 0 WHERE id = ?',
3502 [hashedPassword, userId],
3503 function(err) {
3504 if (err) {
3505 console.error('Error updating password:', err);
3506 res.writeHead(500, { 'Content-Type': 'application/json' });
3507 res.end(JSON.stringify({ success: false, message: 'Failed to update password' }));
3508 return;
3509 }
3510
3511 // Also update password in personal table if it exists (for admin)
3512 database.database.run(
3513 'UPDATE personal SET password = ? WHERE id = ?',
3514 [hashedPassword, userId],
3515 function(err) {
3516 if (err) {
3517 console.log('No personal record to update for ID:', userId);
3518 }
3519 }
3520 );
3521
3522 // Clear temp session
3523 tempAdminSessions.delete(sessionId);
3524
3525 // Create new permanent session
3526 const newSessionId = generateSessionId();
3527
3528 // Determine how to store the user ID based on user type
3529 let sessionUserId = String(userId);
3530
3531 if (user.user_type === 'store_owner' || user.user_type === 'store_employee') {
3532 sessionUserId = `personal_${userId}`;
3533 }
3534
3535 sessions.set(newSessionId, sessionUserId);
3536
3537 // Determine redirect based on user type or provided redirectTo
3538 let finalRedirect = redirectTo || 'dashboard.html';
3539
3540 if (!redirectTo) {
3541 if (user.username === 'admin' || user.user_type === 'admin') {
3542 finalRedirect = 'admin.html';
3543 } else if (user.user_type === 'store_owner') {
3544 finalRedirect = 'store-owner.html';
3545 } else if (user.user_type === 'store_employee') {
3546 finalRedirect = 'store-employee.html';
3547 } else if (user.user_type === 'client') {
3548 finalRedirect = 'client-dashboard.html';
3549 }
3550 }
3551
3552 console.log(`Password changed successfully for user ${userId}, redirecting to ${finalRedirect}`);
3553
3554 database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(),
3555 `${user.user_type || 'user'} forced password change completed`, ipAddress);
3556
3557 // Set the cookie with proper options
3558 res.writeHead(200, {
3559 'Content-Type': 'application/json',
3560 'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict` // Extended to 24 hours
3561 });
3562
3563 res.end(JSON.stringify({
3564 success: true,
3565 message: 'Password changed successfully.',
3566 redirectTo: finalRedirect,
3567 userType: user.user_type || 'user'
3568 }));
3569 }
3570 );
3571 } else {
3572 // Not found in users table, check personal table (for store owners/employees)
3573 console.log('User not found in users table, checking personal table for ID:', userId);
3574
3575 database.getPersonalById(userId, (err, personal) => {
3576 if (err) {
3577 console.error('Error finding personal by ID:', err);
3578 }
3579
3580 if (personal) {
3581 console.log('Found user in personal table:', personal);
3582
3583 // Update password in personal table
3584 const hashedPassword = bcrypt.hashSync(newPassword, 10);
3585
3586 database.database.run(
3587 'UPDATE personal SET password = ? WHERE id = ?',
3588 [hashedPassword, userId],
3589 function(err) {
3590 if (err) {
3591 console.error('Error updating personal password:', err);
3592 res.writeHead(500, { 'Content-Type': 'application/json' });
3593 res.end(JSON.stringify({ success: false, message: 'Failed to update password' }));
3594 return;
3595 }
3596
3597 // Also update in users table if exists
3598 database.database.run(
3599 'UPDATE users SET password = ?, force_password_change = 0 WHERE email = ?',
3600 [hashedPassword, personal.email],
3601 function(err) {
3602 if (err) {
3603 console.log('No users record to update for email:', personal.email);
3604 }
3605 }
3606 );
3607
3608 // Determine user type (boss/owner or employee)
3609 database.database.get(
3610 'SELECT boss_id FROM boss WHERE boss_id = ?',
3611 [userId],
3612 (err, boss) => {
3613 let userType = 'store_employee';
3614 let finalRedirect = redirectTo || 'store-employee.html';
3615
3616 if (boss) {
3617 userType = 'store_owner';
3618 finalRedirect = redirectTo || 'store-owner.html';
3619 }
3620
3621 // Clear temp session
3622 tempAdminSessions.delete(sessionId);
3623
3624 // Create new permanent session with personal_ prefix
3625 const newSessionId = generateSessionId();
3626 sessions.set(newSessionId, `personal_${userId}`);
3627
3628 console.log(`Password changed successfully for ${userType} ${userId}, redirecting to ${finalRedirect}`);
3629
3630 database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(),
3631 `${userType} forced password change completed`, ipAddress);
3632
3633 // Set the cookie with proper options - extended to 24 hours
3634 res.writeHead(200, {
3635 'Content-Type': 'application/json',
3636 'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict`
3637 });
3638
3639 res.end(JSON.stringify({
3640 success: true,
3641 message: 'Password changed successfully.',
3642 redirectTo: finalRedirect,
3643 userType: userType
3644 }));
3645 }
3646 );
3647 }
3648 );
3649 } else {
3650 // User not found in any table
3651 console.error('User not found in any table with ID:', userId);
3652 res.writeHead(404, { 'Content-Type': 'application/json' });
3653 res.end(JSON.stringify({ success: false, message: 'User not found' }));
3654 }
3655 });
3656 }
3657 });
3658 } catch (parseError) {
3659 console.error('JSON parse error:', parseError);
3660 res.writeHead(400, { 'Content-Type': 'application/json' });
3661 res.end(JSON.stringify({ success: false, message: 'Invalid request format' }));
3662 }
3663 });
3664 }
3665
3666 else if (pathname === '/api/register-employee' && req.method === 'POST') {
3667 requireAuth(req, res, (userId) => {
3668 const userIdStr = String(userId);
3669
3670 // Check if this is the admin user
3671 if (userIdStr === '000000') {
3672 res.writeHead(403, { 'Content-Type': 'application/json' });
3673 res.end(JSON.stringify({ success: false, message: 'Only store owners can register employees' }));
3674 return;
3675 }
3676
3677 if (!userIdStr.startsWith('personal_')) {
3678 res.writeHead(403, { 'Content-Type': 'application/json' });
3679 res.end(JSON.stringify({ success: false, message: 'Only store owners can register employees' }));
3680 return;
3681 }
3682
3683 const personalId = userIdStr.replace('personal_', '');
3684
3685 database.database.get(
3686 'SELECT boss_id FROM boss WHERE boss_id = ?',
3687 [personalId],
3688 (err, boss) => {
3689 if (err || !boss) {
3690 res.writeHead(403, { 'Content-Type': 'application/json' });
3691 res.end(JSON.stringify({ success: false, message: 'Only store owners can register employees' }));
3692 return;
3693 }
3694
3695 let body = '';
3696 req.on('data', chunk => {
3697 body += chunk.toString();
3698 });
3699 req.on('end', () => {
3700 const { firstName, lastName, ssn, email, password, storeId, dateOfHire } = JSON.parse(body);
3701
3702 if (!firstName || !lastName || !ssn || !email || !password || !storeId || !dateOfHire) {
3703 res.writeHead(400, { 'Content-Type': 'application/json' });
3704 res.end(JSON.stringify({ success: false, message: 'All fields are required' }));
3705 return;
3706 }
3707
3708 if (!/^\d{13}$/.test(ssn)) {
3709 res.writeHead(400, { 'Content-Type': 'application/json' });
3710 res.end(JSON.stringify({ success: false, message: 'SSN must be exactly 13 digits' }));
3711 return;
3712 }
3713
3714 if (!validateEmail(email)) {
3715 res.writeHead(400, { 'Content-Type': 'application/json' });
3716 res.end(JSON.stringify({ success: false, message: 'Invalid email format' }));
3717 return;
3718 }
3719
3720 if (!validatePassword(password)) {
3721 res.writeHead(400, { 'Content-Type': 'application/json' });
3722 res.end(JSON.stringify({
3723 success: false,
3724 message: 'Password must have at least 8 characters, including uppercase, lowercase, number and special character'
3725 }));
3726 return;
3727 }
3728
3729 database.getPersonalByEmail(email, (err, existingPersonal) => {
3730 if (err) {
3731 console.error('Error checking personal:', err);
3732 res.writeHead(500, { 'Content-Type': 'application/json' });
3733 res.end(JSON.stringify({ success: false, message: 'Server error checking personal' }));
3734 return;
3735 }
3736
3737 if (existingPersonal) {
3738 res.writeHead(400, { 'Content-Type': 'application/json' });
3739 res.end(JSON.stringify({ success: false, message: 'Email is already registered' }));
3740 return;
3741 }
3742
3743 // Find the next available employee number for this store
3744 database.database.all(
3745 "SELECT id FROM personal WHERE id LIKE '" + storeId + "%' ORDER BY id",
3746 [],
3747 (err, existingEmployees) => {
3748 if (err) {
3749 console.error('Error getting employees:', err);
3750 res.writeHead(500, { 'Content-Type': 'application/json' });
3751 res.end(JSON.stringify({ success: false, message: 'Server error generating employee ID' }));
3752 return;
3753 }
3754
3755 // Find the first available employee number from 001 to 999
3756 let nextEmployeeNum = 1;
3757 const existingNumbers = (existingEmployees || [])
3758 .map(e => {
3759 const num = e.id.substring(3);
3760 return parseInt(num, 10);
3761 })
3762 .filter(num => !isNaN(num));
3763
3764 existingNumbers.sort((a, b) => a - b);
3765
3766 // Find the first gap in the sequence
3767 for (let i = 1; i <= 999; i++) {
3768 if (!existingNumbers.includes(i)) {
3769 nextEmployeeNum = i;
3770 break;
3771 }
3772 }
3773
3774 if (nextEmployeeNum > 999) {
3775 res.writeHead(400, { 'Content-Type': 'application/json' });
3776 res.end(JSON.stringify({ success: false, message: 'Maximum employees reached for this store' }));
3777 return;
3778 }
3779
3780 const employeeNumPadded = nextEmployeeNum.toString().padStart(3, '0');
3781 const newPersonalId = storeId + employeeNumPadded;
3782
3783 database.database.run('BEGIN TRANSACTION', (err) => {
3784 if (err) {
3785 console.error('Error beginning transaction:', err);
3786 res.writeHead(500, { 'Content-Type': 'application/json' });
3787 res.end(JSON.stringify({ success: false, message: 'Server error during registration' }));
3788 return;
3789 }
3790
3791 database.database.run(
3792 'INSERT INTO personal (id, first_name, last_name, ssn, email, password) VALUES (?, ?, ?, ?, ?, ?)',
3793 [
3794 newPersonalId,
3795 firstName,
3796 lastName,
3797 ssn,
3798 email,
3799 bcrypt.hashSync(password, 10)
3800 ],
3801 function(err) {
3802 if (err) {
3803 database.database.run('ROLLBACK');
3804 console.error('Error inserting personal:', err);
3805
3806 if (err.code === '23505') {
3807 res.writeHead(400, { 'Content-Type': 'application/json' });
3808 res.end(JSON.stringify({
3809 success: false,
3810 message: 'This personal ID is already taken. Please try again.'
3811 }));
3812 } else {
3813 res.writeHead(400, { 'Content-Type': 'application/json' });
3814 res.end(JSON.stringify({ success: false, message: 'Error registering employee' }));
3815 }
3816 return;
3817 }
3818
3819 database.database.run(
3820 'INSERT INTO employees (employee_id, date_of_hire) VALUES (?, ?)',
3821 [newPersonalId, dateOfHire],
3822 (err) => {
3823 if (err) {
3824 database.database.run('ROLLBACK');
3825 console.error('Error inserting employee:', err);
3826 res.writeHead(400, { 'Content-Type': 'application/json' });
3827 res.end(JSON.stringify({ success: false, message: 'Error registering as employee' }));
3828 return;
3829 }
3830
3831 database.database.run(
3832 'INSERT INTO works_in_store (personal_id, store_id) VALUES (?, ?)',
3833 [newPersonalId, storeId],
3834 (err) => {
3835 if (err) {
3836 database.database.run('ROLLBACK');
3837 console.error('Error inserting works_in_store:', err);
3838 res.writeHead(400, { 'Content-Type': 'application/json' });
3839 res.end(JSON.stringify({ success: false, message: 'Error assigning employee to store' }));
3840 return;
3841 }
3842
3843 database.database.run(
3844 'INSERT INTO permissions (personal_id, type, authorisation) VALUES (?, ?, ?)',
3845 [newPersonalId, 'EMPLOYEE', 'limited_access'],
3846 (err) => {
3847 if (err) {
3848 console.error('Error inserting permissions:', err);
3849 }
3850
3851 // Also create entry in users table for login with force_password_change = 1
3852 database.database.run(
3853 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)',
3854 [
3855 newPersonalId,
3856 `${firstName} ${lastName}`,
3857 email,
3858 bcrypt.hashSync(password, 10),
3859 'store_employee',
3860 1
3861 ],
3862 (err) => {
3863 if (err) {
3864 console.error('Error creating user entry for employee:', err);
3865 }
3866
3867 database.database.run('COMMIT', (err) => {
3868 if (err) {
3869 database.database.run('ROLLBACK');
3870 console.error('Error committing transaction:', err);
3871 res.writeHead(500, { 'Content-Type': 'application/json' });
3872 res.end(JSON.stringify({ success: false, message: 'Error completing registration' }));
3873 return;
3874 }
3875
3876 database.logAudit(personalId, 'EMPLOYEE_REGISTERED', 'employee', newPersonalId, `Employee registered: ${firstName} ${lastName}`, ipAddress);
3877
3878 res.writeHead(200, { 'Content-Type': 'application/json' });
3879 res.end(JSON.stringify({
3880 success: true,
3881 message: 'Employee registered successfully!',
3882 employeeId: newPersonalId,
3883 name: `${firstName} ${lastName}`
3884 }));
3885 });
3886 }
3887 );
3888 }
3889 );
3890 }
3891 );
3892 }
3893 );
3894 }
3895 );
3896 });
3897 }
3898 );
3899 });
3900 });
3901 }
3902 );
3903 });
3904 }
3905
3906 else if (pathname === '/api/delete-employee' && req.method === 'POST') {
3907 requireAuth(req, res, (userId) => {
3908 const userIdStr = String(userId);
3909
3910 // Check if this is the admin user
3911 if (userIdStr === '000000') {
3912 res.writeHead(403, { 'Content-Type': 'application/json' });
3913 res.end(JSON.stringify({ success: false, message: 'Only store owners can delete employees' }));
3914 return;
3915 }
3916
3917 if (!userIdStr.startsWith('personal_')) {
3918 res.writeHead(403, { 'Content-Type': 'application/json' });
3919 res.end(JSON.stringify({ success: false, message: 'Only store owners can delete employees' }));
3920 return;
3921 }
3922
3923 const personalId = userIdStr.replace('personal_', '');
3924
3925 database.database.get(
3926 'SELECT boss_id FROM boss WHERE boss_id = ?',
3927 [personalId],
3928 (err, boss) => {
3929 if (err || !boss) {
3930 res.writeHead(403, { 'Content-Type': 'application/json' });
3931 res.end(JSON.stringify({ success: false, message: 'Only store owners can delete employees' }));
3932 return;
3933 }
3934
3935 let body = '';
3936 req.on('data', chunk => {
3937 body += chunk.toString();
3938 });
3939 req.on('end', () => {
3940 const { employeeId, storeId } = JSON.parse(body);
3941
3942 if (!employeeId || !storeId) {
3943 res.writeHead(400, { 'Content-Type': 'application/json' });
3944 res.end(JSON.stringify({ success: false, message: 'Employee ID and Store ID are required' }));
3945 return;
3946 }
3947
3948 database.database.get(
3949 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
3950 [personalId, storeId],
3951 (err, bossStore) => {
3952 if (err || !bossStore) {
3953 res.writeHead(403, { 'Content-Type': 'application/json' });
3954 res.end(JSON.stringify({ success: false, message: 'You are not authorized to manage employees in this store' }));
3955 return;
3956 }
3957
3958 database.database.get(
3959 'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
3960 [employeeId, storeId],
3961 (err, employeeStore) => {
3962 if (err || !employeeStore) {
3963 res.writeHead(404, { 'Content-Type': 'application/json' });
3964 res.end(JSON.stringify({ success: false, message: 'Employee not found in this store' }));
3965 return;
3966 }
3967
3968 database.database.get(
3969 'SELECT boss_id FROM boss WHERE boss_id = ?',
3970 [employeeId],
3971 (err, isBoss) => {
3972 if (err) {
3973 console.error('Error checking if employee is boss:', err);
3974 }
3975
3976 if (isBoss) {
3977 res.writeHead(403, { 'Content-Type': 'application/json' });
3978 res.end(JSON.stringify({ success: false, message: 'Cannot delete store owners' }));
3979 return;
3980 }
3981
3982 database.database.run('BEGIN TRANSACTION', (err) => {
3983 if (err) {
3984 console.error('Error beginning transaction:', err);
3985 res.writeHead(500, { 'Content-Type': 'application/json' });
3986 res.end(JSON.stringify({ success: false, message: 'Server error during deletion' }));
3987 return;
3988 }
3989
3990 database.database.run(
3991 'DELETE FROM works_in_store WHERE personal_id = ? AND store_id = ?',
3992 [employeeId, storeId],
3993 (err) => {
3994 if (err) {
3995 database.database.run('ROLLBACK');
3996 console.error('Error deleting from works_in_store:', err);
3997 res.writeHead(500, { 'Content-Type': 'application/json' });
3998 res.end(JSON.stringify({ success: false, message: 'Error removing employee from store' }));
3999 return;
4000 }
4001
4002 database.database.run(
4003 'DELETE FROM employees WHERE employee_id = ?',
4004 [employeeId],
4005 (err) => {
4006 if (err) {
4007 console.error('Error deleting from employees:', err);
4008 }
4009
4010 database.database.run(
4011 'DELETE FROM permissions WHERE personal_id = ?',
4012 [employeeId],
4013 (err) => {
4014 if (err) {
4015 console.error('Error deleting from permissions:', err);
4016 }
4017
4018 database.database.run(
4019 'DELETE FROM personal WHERE id = ?',
4020 [employeeId],
4021 (err) => {
4022 if (err) {
4023 console.error('Error deleting from personal:', err);
4024 }
4025
4026 // Also delete from users table
4027 database.database.run(
4028 'DELETE FROM users WHERE id = ?',
4029 [employeeId],
4030 (err) => {
4031 if (err) {
4032 console.error('Error deleting from users:', err);
4033 }
4034
4035 database.database.run('COMMIT', (commitErr) => {
4036 if (commitErr) {
4037 database.database.run('ROLLBACK');
4038 console.error('Error committing transaction:', commitErr);
4039 res.writeHead(500, { 'Content-Type': 'application/json' });
4040 res.end(JSON.stringify({ success: false, message: 'Error completing deletion' }));
4041 return;
4042 }
4043
4044 database.logAudit(personalId, 'EMPLOYEE_DELETED', 'employee', employeeId, `Employee deleted from store ${storeId}`, ipAddress);
4045
4046 res.writeHead(200, { 'Content-Type': 'application/json' });
4047 res.end(JSON.stringify({
4048 success: true,
4049 message: 'Employee deleted successfully'
4050 }));
4051 });
4052 }
4053 );
4054 }
4055 );
4056 }
4057 );
4058 }
4059 );
4060 }
4061 );
4062 });
4063 }
4064 );
4065 }
4066 );
4067 }
4068 );
4069 });
4070 }
4071 );
4072 });
4073 }
4074
4075 else if (pathname === '/api/update-employee-status' && req.method === 'POST') {
4076 requireAuth(req, res, (userId) => {
4077 const userIdStr = String(userId);
4078
4079 // Check if this is the admin user
4080 if (userIdStr === '000000') {
4081 res.writeHead(403, { 'Content-Type': 'application/json' });
4082 res.end(JSON.stringify({ success: false, message: 'Only store owners can update employee status' }));
4083 return;
4084 }
4085
4086 if (!userIdStr.startsWith('personal_')) {
4087 res.writeHead(403, { 'Content-Type': 'application/json' });
4088 res.end(JSON.stringify({ success: false, message: 'Only store owners can update employee status' }));
4089 return;
4090 }
4091
4092 const personalId = userIdStr.replace('personal_', '');
4093
4094 database.database.get(
4095 'SELECT boss_id FROM boss WHERE boss_id = ?',
4096 [personalId],
4097 (err, boss) => {
4098 if (err || !boss) {
4099 res.writeHead(403, { 'Content-Type': 'application/json' });
4100 res.end(JSON.stringify({ success: false, message: 'Only store owners can update employee status' }));
4101 return;
4102 }
4103
4104 let body = '';
4105 req.on('data', chunk => {
4106 body += chunk.toString();
4107 });
4108 req.on('end', () => {
4109 const { employeeId, storeId, status } = JSON.parse(body);
4110
4111 if (!employeeId || !storeId || !status) {
4112 res.writeHead(400, { 'Content-Type': 'application/json' });
4113 res.end(JSON.stringify({ success: false, message: 'Employee ID, Store ID and Status are required' }));
4114 return;
4115 }
4116
4117 database.database.get(
4118 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4119 [personalId, storeId],
4120 (err, bossStore) => {
4121 if (err || !bossStore) {
4122 res.writeHead(403, { 'Content-Type': 'application/json' });
4123 res.end(JSON.stringify({ success: false, message: 'You are not authorized to manage employees in this store' }));
4124 return;
4125 }
4126
4127 database.database.get(
4128 'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4129 [employeeId, storeId],
4130 (err, employeeStore) => {
4131 if (err || !employeeStore) {
4132 res.writeHead(404, { 'Content-Type': 'application/json' });
4133 res.end(JSON.stringify({ success: false, message: 'Employee not found in this store' }));
4134 return;
4135 }
4136
4137 let permissionType = 'EMPLOYEE';
4138 let authorization = 'limited_access';
4139
4140 if (status === 'promoted') {
4141 permissionType = 'MANAGER';
4142 authorization = 'extended_access';
4143 } else if (status === 'suspended') {
4144 permissionType = 'SUSPENDED';
4145 authorization = 'no_access';
4146 } else if (status === 'active') {
4147 permissionType = 'EMPLOYEE';
4148 authorization = 'limited_access';
4149 }
4150
4151 database.database.run(
4152 'UPDATE permissions SET type = ?, authorisation = ? WHERE personal_id = ?',
4153 [permissionType, authorization, employeeId],
4154 function(err) {
4155 if (err) {
4156 console.error('Error updating employee status:', err);
4157 res.writeHead(500, { 'Content-Type': 'application/json' });
4158 res.end(JSON.stringify({ success: false, message: 'Error updating employee status' }));
4159 return;
4160 }
4161
4162 database.logAudit(personalId, 'EMPLOYEE_STATUS_UPDATED', 'employee', employeeId, `Employee status updated to: ${status}`, ipAddress);
4163
4164 res.writeHead(200, { 'Content-Type': 'application/json' });
4165 res.end(JSON.stringify({
4166 success: true,
4167 message: `Employee status updated to ${status} successfully`
4168 }));
4169 }
4170 );
4171 }
4172 );
4173 }
4174 );
4175 });
4176 }
4177 );
4178 });
4179 }
4180
4181 else if (pathname === '/api/update-employee' && req.method === 'POST') {
4182 requireAuth(req, res, (userId) => {
4183 const userIdStr = String(userId);
4184
4185 // Check if this is the admin user
4186 if (userIdStr === '000000') {
4187 res.writeHead(403, { 'Content-Type': 'application/json' });
4188 res.end(JSON.stringify({ success: false, message: 'Only store owners can update employees' }));
4189 return;
4190 }
4191
4192 if (!userIdStr.startsWith('personal_')) {
4193 res.writeHead(403, { 'Content-Type': 'application/json' });
4194 res.end(JSON.stringify({ success: false, message: 'Only store owners can update employees' }));
4195 return;
4196 }
4197
4198 const personalId = userIdStr.replace('personal_', '');
4199
4200 database.database.get(
4201 'SELECT boss_id FROM boss WHERE boss_id = ?',
4202 [personalId],
4203 (err, boss) => {
4204 if (err || !boss) {
4205 res.writeHead(403, { 'Content-Type': 'application/json' });
4206 res.end(JSON.stringify({ success: false, message: 'Only store owners can update employees' }));
4207 return;
4208 }
4209
4210 let body = '';
4211 req.on('data', chunk => {
4212 body += chunk.toString();
4213 });
4214 req.on('end', () => {
4215 const { employeeId, storeId, firstName, lastName, email } = JSON.parse(body);
4216
4217 if (!employeeId || !storeId) {
4218 res.writeHead(400, { 'Content-Type': 'application/json' });
4219 res.end(JSON.stringify({ success: false, message: 'Employee ID and Store ID are required' }));
4220 return;
4221 }
4222
4223 database.database.get(
4224 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4225 [personalId, storeId],
4226 (err, bossStore) => {
4227 if (err || !bossStore) {
4228 res.writeHead(403, { 'Content-Type': 'application/json' });
4229 res.end(JSON.stringify({ success: false, message: 'You are not authorized to manage employees in this store' }));
4230 return;
4231 }
4232
4233 database.database.get(
4234 'SELECT personal_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4235 [employeeId, storeId],
4236 (err, employeeStore) => {
4237 if (err || !employeeStore) {
4238 res.writeHead(404, { 'Content-Type': 'application/json' });
4239 res.end(JSON.stringify({ success: false, message: 'Employee not found in this store' }));
4240 return;
4241 }
4242
4243 const updates = [];
4244 const params = [];
4245
4246 if (firstName) {
4247 updates.push('first_name = ?');
4248 params.push(firstName);
4249 }
4250
4251 if (lastName) {
4252 updates.push('last_name = ?');
4253 params.push(lastName);
4254 }
4255
4256 if (email) {
4257 if (!validateEmail(email)) {
4258 res.writeHead(400, { 'Content-Type': 'application/json' });
4259 res.end(JSON.stringify({ success: false, message: 'Invalid email format' }));
4260 return;
4261 }
4262 updates.push('email = ?');
4263 params.push(email);
4264 }
4265
4266 if (updates.length === 0) {
4267 res.writeHead(400, { 'Content-Type': 'application/json' });
4268 res.end(JSON.stringify({ success: false, message: 'No fields to update' }));
4269 return;
4270 }
4271
4272 params.push(employeeId);
4273
4274 database.database.run(
4275 `UPDATE personal SET ${updates.join(', ')} WHERE id = ?`,
4276 params,
4277 function(err) {
4278 if (err) {
4279 console.error('Error updating employee:', err);
4280 res.writeHead(500, { 'Content-Type': 'application/json' });
4281 res.end(JSON.stringify({ success: false, message: 'Error updating employee information' }));
4282 return;
4283 }
4284
4285 // Also update in users table if email was changed
4286 if (email) {
4287 database.database.run(
4288 'UPDATE users SET email = ? WHERE id = ?',
4289 [email, employeeId],
4290 (err) => {
4291 if (err) {
4292 console.error('Error updating user email:', err);
4293 }
4294 }
4295 );
4296 }
4297
4298 if (firstName || lastName) {
4299 database.database.get(
4300 'SELECT first_name, last_name FROM personal WHERE id = ?',
4301 [employeeId],
4302 (err, personal) => {
4303 if (!err && personal) {
4304 const newUsername = `${personal.first_name} ${personal.last_name}`;
4305 database.database.run(
4306 'UPDATE users SET username = ? WHERE id = ?',
4307 [newUsername, employeeId],
4308 (err) => {
4309 if (err) {
4310 console.error('Error updating user username:', err);
4311 }
4312 }
4313 );
4314 }
4315 }
4316 );
4317 }
4318
4319 database.logAudit(personalId, 'EMPLOYEE_UPDATED', 'employee', employeeId, `Employee information updated`, ipAddress);
4320
4321 res.writeHead(200, { 'Content-Type': 'application/json' });
4322 res.end(JSON.stringify({
4323 success: true,
4324 message: 'Employee information updated successfully'
4325 }));
4326 }
4327 );
4328 }
4329 );
4330 }
4331 );
4332 });
4333 }
4334 );
4335 });
4336 }
4337
4338 else if (pathname === '/api/store-products' && req.method === 'GET') {
4339 requireStoreOwner()(req, res, (personalId) => {
4340 const storeId = parsedUrl.query.storeId;
4341
4342 if (!storeId) {
4343 database.database.get(
4344 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
4345 [personalId],
4346 (err, store) => {
4347 if (err || !store) {
4348 res.writeHead(400, { 'Content-Type': 'application/json' });
4349 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
4350 return;
4351 }
4352
4353 database.getStoreProducts(store.store_id, (err, products) => {
4354 if (err) {
4355 res.writeHead(500, { 'Content-Type': 'application/json' });
4356 res.end(JSON.stringify({ success: false, message: 'Error fetching store products' }));
4357 } else {
4358 res.writeHead(200, { 'Content-Type': 'application/json' });
4359 res.end(JSON.stringify({ success: true, products }));
4360 }
4361 });
4362 }
4363 );
4364
4365 return;
4366 }
4367
4368 database.database.get(
4369 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4370 [personalId, storeId],
4371 (err, ownsStore) => {
4372 if (err || !ownsStore) {
4373 res.writeHead(403, { 'Content-Type': 'application/json' });
4374 res.end(JSON.stringify({ success: false, message: 'You are not authorized to view products in this store' }));
4375 return;
4376 }
4377
4378 database.getStoreProducts(storeId, (err, products) => {
4379 if (err) {
4380 res.writeHead(500, { 'Content-Type': 'application/json' });
4381 res.end(JSON.stringify({ success: false, message: 'Error fetching store products' }));
4382 } else {
4383 res.writeHead(200, { 'Content-Type': 'application/json' });
4384 res.end(JSON.stringify({ success: true, products }));
4385 }
4386 });
4387 }
4388 );
4389 });
4390 }
4391
4392 else if (pathname === '/api/store-orders' && req.method === 'GET') {
4393 requireStoreOwner()(req, res, (personalId) => {
4394 const storeId = parsedUrl.query.storeId;
4395
4396 if (!storeId) {
4397 database.database.get(
4398 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
4399 [personalId],
4400 (err, store) => {
4401 if (err || !store) {
4402 res.writeHead(400, { 'Content-Type': 'application/json' });
4403 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
4404 return;
4405 }
4406
4407 database.getStoreOrders(store.store_id, (err, orders) => {
4408 if (err) {
4409 res.writeHead(500, { 'Content-Type': 'application/json' });
4410 res.end(JSON.stringify({ success: false, message: 'Error fetching store orders' }));
4411 } else {
4412 res.writeHead(200, { 'Content-Type': 'application/json' });
4413 res.end(JSON.stringify({ success: true, orders }));
4414 }
4415 });
4416 }
4417 );
4418
4419 return;
4420 }
4421
4422 database.database.get(
4423 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4424 [personalId, storeId],
4425 (err, ownsStore) => {
4426 if (err || !ownsStore) {
4427 res.writeHead(403, { 'Content-Type': 'application/json' });
4428 res.end(JSON.stringify({ success: false, message: 'You are not authorized to view orders in this store' }));
4429 return;
4430 }
4431
4432 database.getStoreOrders(storeId, (err, orders) => {
4433 if (err) {
4434 res.writeHead(500, { 'Content-Type': 'application/json' });
4435 res.end(JSON.stringify({ success: false, message: 'Error fetching store orders' }));
4436 } else {
4437 res.writeHead(200, { 'Content-Type': 'application/json' });
4438 res.end(JSON.stringify({ success: true, orders }));
4439 }
4440 });
4441 }
4442 );
4443 });
4444 }
4445
4446 else if (pathname === '/api/store-employees' && req.method === 'GET') {
4447 requireStoreOwner()(req, res, (personalId) => {
4448 const storeId = parsedUrl.query.storeId;
4449
4450 if (!storeId) {
4451 database.database.get(
4452 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
4453 [personalId],
4454 (err, store) => {
4455 if (err || !store) {
4456 res.writeHead(400, { 'Content-Type': 'application/json' });
4457 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
4458 return;
4459 }
4460
4461 database.getStoreEmployees(store.store_id, (err, employees) => {
4462 if (err) {
4463 res.writeHead(500, { 'Content-Type': 'application/json' });
4464 res.end(JSON.stringify({ success: false, message: 'Error fetching store employees' }));
4465 } else {
4466 res.writeHead(200, { 'Content-Type': 'application/json' });
4467 res.end(JSON.stringify({ success: true, employees }));
4468 }
4469 });
4470 }
4471 );
4472
4473 return;
4474 }
4475
4476 database.database.get(
4477 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4478 [personalId, storeId],
4479 (err, ownsStore) => {
4480 if (err || !ownsStore) {
4481 res.writeHead(403, { 'Content-Type': 'application/json' });
4482 res.end(JSON.stringify({ success: false, message: 'You are not authorized to view employees in this store' }));
4483 return;
4484 }
4485
4486 database.getStoreEmployees(storeId, (err, employees) => {
4487 if (err) {
4488 res.writeHead(500, { 'Content-Type': 'application/json' });
4489 res.end(JSON.stringify({ success: false, message: 'Error fetching store employees' }));
4490 } else {
4491 res.writeHead(200, { 'Content-Type': 'application/json' });
4492 res.end(JSON.stringify({ success: true, employees }));
4493 }
4494 });
4495 }
4496 );
4497 });
4498 }
4499
4500 else if (pathname === '/api/store-reports' && req.method === 'GET') {
4501 requireStoreOwner()(req, res, (personalId) => {
4502 const storeId = parsedUrl.query.storeId;
4503
4504 if (!storeId) {
4505 database.database.get(
4506 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
4507 [personalId],
4508 (err, store) => {
4509 if (err || !store) {
4510 res.writeHead(400, { 'Content-Type': 'application/json' });
4511 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
4512 return;
4513 }
4514
4515 database.getStoreReports(store.store_id, (err, reports) => {
4516 if (err) {
4517 res.writeHead(500, { 'Content-Type': 'application/json' });
4518 res.end(JSON.stringify({ success: false, message: 'Error fetching store reports' }));
4519 } else {
4520 res.writeHead(200, { 'Content-Type': 'application/json' });
4521 res.end(JSON.stringify({ success: true, reports }));
4522 }
4523 });
4524 }
4525 );
4526
4527 return;
4528 }
4529
4530 database.database.get(
4531 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4532 [personalId, storeId],
4533 (err, ownsStore) => {
4534 if (err || !ownsStore) {
4535 res.writeHead(403, { 'Content-Type': 'application/json' });
4536 res.end(JSON.stringify({ success: false, message: 'You are not authorized to view reports in this store' }));
4537 return;
4538 }
4539
4540 database.getStoreReports(storeId, (err, reports) => {
4541 if (err) {
4542 res.writeHead(500, { 'Content-Type': 'application/json' });
4543 res.end(JSON.stringify({ success: false, message: 'Error fetching store reports' }));
4544 } else {
4545 res.writeHead(200, { 'Content-Type': 'application/json' });
4546 res.end(JSON.stringify({ success: true, reports }));
4547 }
4548 });
4549 }
4550 );
4551 });
4552 }
4553
4554 else if (pathname === '/api/store-stats' && req.method === 'GET') {
4555 requireStoreOwner()(req, res, (personalId) => {
4556 const storeId = parsedUrl.query.storeId;
4557
4558 if (!storeId) {
4559 database.database.get(
4560 'SELECT store_id FROM works_in_store WHERE personal_id = ? LIMIT 1',
4561 [personalId],
4562 (err, store) => {
4563 if (err || !store) {
4564 res.writeHead(400, { 'Content-Type': 'application/json' });
4565 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
4566 return;
4567 }
4568
4569 database.getStoreStats(store.store_id, (err, stats) => {
4570 if (err) {
4571 res.writeHead(500, { 'Content-Type': 'application/json' });
4572 res.end(JSON.stringify({ success: false, message: 'Error fetching store statistics' }));
4573 } else {
4574 res.writeHead(200, { 'Content-Type': 'application/json' });
4575 res.end(JSON.stringify({ success: true, stats }));
4576 }
4577 });
4578 }
4579 );
4580
4581 return;
4582 }
4583
4584 database.database.get(
4585 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4586 [personalId, storeId],
4587 (err, ownsStore) => {
4588 if (err || !ownsStore) {
4589 res.writeHead(403, { 'Content-Type': 'application/json' });
4590 res.end(JSON.stringify({ success: false, message: 'You are not authorized to view statistics in this store' }));
4591 return;
4592 }
4593
4594 database.getStoreStats(storeId, (err, stats) => {
4595 if (err) {
4596 res.writeHead(500, { 'Content-Type': 'application/json' });
4597 res.end(JSON.stringify({ success: false, message: 'Error fetching store statistics' }));
4598 } else {
4599 res.writeHead(200, { 'Content-Type': 'application/json' });
4600 res.end(JSON.stringify({ success: true, stats }));
4601 }
4602 });
4603 }
4604 );
4605 });
4606 }
4607
4608 else if (pathname === '/api/employee-tasks' && req.method === 'GET') {
4609 requireAuth(req, res, (userId) => {
4610 const userIdStr = String(userId);
4611
4612 // Check if this is the admin user
4613 if (userIdStr === '000000') {
4614 res.writeHead(403, { 'Content-Type': 'application/json' });
4615 res.end(JSON.stringify({ success: false, message: 'Only store employees can access this endpoint' }));
4616 return;
4617 }
4618
4619 if (!userIdStr.startsWith('personal_')) {
4620 res.writeHead(403, { 'Content-Type': 'application/json' });
4621 res.end(JSON.stringify({ success: false, message: 'Only store employees can access this endpoint' }));
4622 return;
4623 }
4624
4625 const personalId = userIdStr.replace('personal_', '');
4626 const storeId = parsedUrl.query.storeId;
4627
4628 if (!storeId) {
4629 res.writeHead(400, { 'Content-Type': 'application/json' });
4630 res.end(JSON.stringify({ success: false, message: 'Store ID is required' }));
4631 return;
4632 }
4633
4634 database.getEmployeeTasks(personalId, storeId, (err, tasks) => {
4635 if (err) {
4636 res.writeHead(500, { 'Content-Type': 'application/json' });
4637 res.end(JSON.stringify({ success: false, message: 'Error fetching employee tasks' }));
4638 } else {
4639 res.writeHead(200, { 'Content-Type': 'application/json' });
4640 res.end(JSON.stringify({ success: true, tasks }));
4641 }
4642 });
4643 });
4644 }
4645
4646 else if (pathname === '/api/client-stats' && req.method === 'GET') {
4647 requireAuth(req, res, (userId) => {
4648 const userIdStr = String(userId);
4649
4650 if (!userIdStr.startsWith('client_')) {
4651 res.writeHead(403, { 'Content-Type': 'application/json' });
4652 res.end(JSON.stringify({ success: false, message: 'Only clients can access this endpoint' }));
4653 return;
4654 }
4655
4656 const clientId = parseInt(userIdStr.replace('client_', ''));
4657
4658 database.getClientStats(clientId, (err, stats) => {
4659 if (err) {
4660 res.writeHead(500, { 'Content-Type': 'application/json' });
4661 res.end(JSON.stringify({ success: false, message: 'Error fetching client statistics' }));
4662 } else {
4663 res.writeHead(200, { 'Content-Type': 'application/json' });
4664 res.end(JSON.stringify({ success: true, stats }));
4665 }
4666 });
4667 });
4668 }
4669
4670 else if (pathname === '/api/delete-product' && req.method === 'POST') {
4671 requireStoreOwner()(req, res, (personalId) => {
4672 let body = '';
4673 req.on('data', chunk => {
4674 body += chunk.toString();
4675 });
4676 req.on('end', () => {
4677 const { productCode, storeId } = JSON.parse(body);
4678
4679 if (!productCode || !storeId) {
4680 res.writeHead(400, { 'Content-Type': 'application/json' });
4681 res.end(JSON.stringify({ success: false, message: 'Product code and store ID are required' }));
4682 return;
4683 }
4684
4685 database.database.get(
4686 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4687 [personalId, storeId],
4688 (err, ownsStore) => {
4689 if (err || !ownsStore) {
4690 res.writeHead(403, { 'Content-Type': 'application/json' });
4691 res.end(JSON.stringify({ success: false, message: 'You are not authorized to delete products from this store' }));
4692 return;
4693 }
4694
4695 database.deleteProduct(productCode, storeId, personalId, (err) => {
4696 if (err) {
4697 console.error('Error deleting product:', err);
4698 res.writeHead(500, { 'Content-Type': 'application/json' });
4699 res.end(JSON.stringify({ success: false, message: 'Error deleting product: ' + err.message }));
4700 } else {
4701 database.logAudit(personalId, 'PRODUCT_DELETED', 'product', productCode, 'Product deleted', ipAddress);
4702 res.writeHead(200, { 'Content-Type': 'application/json' });
4703 res.end(JSON.stringify({ success: true, message: 'Product deleted successfully' }));
4704 }
4705 });
4706 }
4707 );
4708 });
4709 });
4710 }
4711
4712 else if (pathname === '/api/product-by-code' && req.method === 'GET') {
4713 requireAuth(req, res, (userId) => {
4714 const parsedUrl = url.parse(req.url, true);
4715 const productCode = parsedUrl.query.code;
4716
4717 if (!productCode) {
4718 res.writeHead(400, { 'Content-Type': 'application/json' });
4719 res.end(JSON.stringify({ success: false, message: 'Product code is required' }));
4720 return;
4721 }
4722
4723 database.getProductByCode(productCode, (err, product) => {
4724 if (err) {
4725 console.error('Error fetching product:', err);
4726 res.writeHead(500, { 'Content-Type': 'application/json' });
4727 res.end(JSON.stringify({ success: false, message: 'Error fetching product' }));
4728 } else if (!product) {
4729 res.writeHead(404, { 'Content-Type': 'application/json' });
4730 res.end(JSON.stringify({ success: false, message: 'Product not found' }));
4731 } else {
4732 res.writeHead(200, { 'Content-Type': 'application/json' });
4733 res.end(JSON.stringify({ success: true, product }));
4734 }
4735 });
4736 });
4737 }
4738
4739 else if (pathname === '/api/generate-report' && req.method === 'POST') {
4740 requireStoreOwner()(req, res, (personalId) => {
4741 let body = '';
4742 req.on('data', chunk => {
4743 body += chunk.toString();
4744 });
4745 req.on('end', () => {
4746 const { storeId, period, startDate, endDate, type } = JSON.parse(body);
4747
4748 if (!storeId || !period || !startDate || !endDate || !type) {
4749 res.writeHead(400, { 'Content-Type': 'application/json' });
4750 res.end(JSON.stringify({ success: false, message: 'All fields are required' }));
4751 return;
4752 }
4753
4754 database.database.get(
4755 'SELECT store_id FROM works_in_store WHERE personal_id = ? AND store_id = ?',
4756 [personalId, storeId],
4757 (err, ownsStore) => {
4758 if (err || !ownsStore) {
4759 res.writeHead(403, { 'Content-Type': 'application/json' });
4760 res.end(JSON.stringify({ success: false, message: 'You are not authorized to generate reports for this store' }));
4761 return;
4762 }
4763
4764 const reportId = 'RPT' + Date.now().toString().slice(-6);
4765
4766 database.database.run(
4767 'INSERT INTO report (id, store_id, period, start_date, end_date, type, generated_by, generated_at) VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)',
4768 [reportId, storeId, period, startDate, endDate, type, personalId],
4769 function(err) {
4770 if (err) {
4771 console.error('Error generating report:', err);
4772 res.writeHead(500, { 'Content-Type': 'application/json' });
4773 res.end(JSON.stringify({ success: false, message: 'Error generating report: ' + err.message }));
4774 } else {
4775 database.logAudit(personalId, 'REPORT_GENERATED', 'report', reportId, `Report generated: ${type} for ${period}`, ipAddress);
4776
4777 res.writeHead(200, { 'Content-Type': 'application/json' });
4778 res.end(JSON.stringify({
4779 success: true,
4780 message: 'Report generated successfully',
4781 reportId: reportId,
4782 report: {
4783 id: reportId,
4784 storeId: storeId,
4785 period: period,
4786 startDate: startDate,
4787 endDate: endDate,
4788 type: type,
4789 generatedBy: personalId,
4790 generatedAt: new Date().toISOString()
4791 }
4792 }));
4793 }
4794 }
4795 );
4796 }
4797 );
4798 });
4799 });
4800 }
4801
4802 else {
4803 res.writeHead(404, { 'Content-Type': 'text/plain' });
4804 res.end('Page not found');
4805 }
4806});
4807
4808server.listen(port, () => {
4809 console.log(`๐ŸŽจ Handcraft Marketplace running at http://localhost:${port}`);
4810 console.log('๐Ÿ‘ฅ Roles: Admin, Store Owner, Store Employee, Registered Client, Unregistered Guest');
4811 console.log('๐ŸŽฏ Features: Product browsing, ordering, reviews, store management');
4812 console.log('๐Ÿช Store Registration: Available at /register-store.html');
4813 console.log('๐Ÿ‘ค Client Registration: Available at /register.html');
4814});
Note: See TracBrowser for help on using the repository browser.