source: server.js@ 4dff800

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

Fixed admin logging in and force change password

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