source: server.js@ 62b2964

finki-main main
Last change on this file since 62b2964 was 81bc7da, checked in by Klimentina Efremova <klimentina08642@โ€ฆ>, 3 months ago

Initial commit

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