113 lines
3.8 KiB
JavaScript
113 lines
3.8 KiB
JavaScript
// server.js
|
|
import express from 'express';
|
|
import cors from 'cors';
|
|
import https from 'https';
|
|
import http from 'http';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import dotenv from 'dotenv';
|
|
import mysql from 'mysql2/promise';
|
|
import managerRoutes from './managerRoutes.js';
|
|
import workerRoutes from './workerRoutes.js';
|
|
|
|
async function startServer() {
|
|
dotenv.config({ path: path.join(path.dirname(fileURLToPath(import.meta.url)), '.env') });
|
|
|
|
const app = express();
|
|
|
|
const db = mysql.createPool({
|
|
host: process.env.DB_HOST,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
database: process.env.DB_NAME,
|
|
port: process.env.DB_PORT,
|
|
waitForConnections: true,
|
|
connectionLimit: 10,
|
|
queueLimit: 0,
|
|
timezone: 'Z',
|
|
dateStrings: true
|
|
});
|
|
|
|
try {
|
|
const connection = await db.getConnection();
|
|
console.log('Database connected successfully!');
|
|
connection.release();
|
|
} catch (error) {
|
|
console.error('!!! DATABASE CONNECTION FAILED !!!');
|
|
console.error('Error:', error.message);
|
|
process.exit(1);
|
|
}
|
|
|
|
const allowedOriginsFromEnv = (process.env.CORS_ALLOWED_ORIGINS || '').split(',').filter(Boolean);
|
|
const defaultAllowedOrigins = ['http://localhost:5173', 'https://localhost:5173', 'capacitor://localhost', 'ionic://localhost', 'http://localhost', 'https://localhost'];
|
|
const allowedOrigins = [...new Set([...defaultAllowedOrigins, ...allowedOriginsFromEnv])];
|
|
|
|
const corsOptions = {
|
|
origin: (origin, callback) => {
|
|
// Allow requests with no origin (like mobile apps or curl requests)
|
|
if (!origin || allowedOrigins.includes(origin) || origin.startsWith('capacitor://') || origin.startsWith('ionic://')) {
|
|
callback(null, true);
|
|
} else {
|
|
console.log('CORS blocked origin:', origin);
|
|
callback(new Error('Not allowed by CORS'));
|
|
}
|
|
},
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'ngrok-skip-browser-warning'],
|
|
exposedHeaders: ['Content-Range', 'X-Content-Range'],
|
|
};
|
|
|
|
app.use(cors(corsOptions));
|
|
app.use(express.json());
|
|
// --- Public server time endpoints (no auth, no cache) ---
|
|
const timeHandler = (req, res) => {
|
|
const now = new Date();
|
|
const ymdKL = new Intl.DateTimeFormat('en-CA', {
|
|
timeZone: 'Asia/Kuala_Lumpur',
|
|
year: 'numeric', month: '2-digit', day: '2-digit'
|
|
}).format(now); // "YYYY-MM-DD"
|
|
|
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
res.set('Pragma', 'no-cache');
|
|
res.set('Expires', '0');
|
|
|
|
res.json({ nowIso: now.toISOString(), tz: 'Asia/Kuala_Lumpur', ymdKL });
|
|
};
|
|
app.get('/time', timeHandler); // public path
|
|
app.get('/api/time', timeHandler); // also under /api
|
|
|
|
app.use('/api/managers', managerRoutes(db));
|
|
app.use('/api', workerRoutes(db));
|
|
|
|
const httpPort = process.env.HTTP_PORT || 3000;
|
|
const httpsPort = process.env.HTTPS_PORT || 3443;
|
|
const sslEnabled = process.env.SSL_ENABLED === 'true';
|
|
|
|
if (sslEnabled) {
|
|
try {
|
|
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const keyPath = path.join(currentDir, 'key.pem');
|
|
const certPath = path.join(currentDir, 'cert.pem');
|
|
|
|
const httpsOptions = {
|
|
key: fs.readFileSync(keyPath),
|
|
cert: fs.readFileSync(certPath),
|
|
};
|
|
|
|
https.createServer(httpsOptions, app).listen(httpsPort, '0.0.0.0', () => {
|
|
console.log(`🔒 HTTPS Server is running on https://localhost:${httpsPort}`);
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Failed to start HTTPS server:', error.message);
|
|
}
|
|
}
|
|
|
|
http.createServer(app).listen(httpPort, '0.0.0.0', () => {
|
|
console.log(`🌐 HTTP Server is running on http://localhost:${httpPort}`);
|
|
});
|
|
}
|
|
|
|
startServer();
|