// Main Server - MySQL Backend for Absensi Siswa import express from 'express'; import cors from 'cors'; import path from 'path'; import { fileURLToPath } from 'url'; import dotenv from 'dotenv'; // Load environment variables dotenv.config(); // Import database and schema import pool, { testConnection } from './backend/db.js'; import createTables from './backend/schema.js'; // Import routes import usersRoutes from './backend/routes/users.js'; import attendanceRoutes from './backend/routes/attendance.js'; import registrationsRoutes from './backend/routes/registrations.js'; import settingsRoutes from './backend/routes/settings.js'; import authRoutes from './backend/routes/auth.js'; import staffRoutes from './backend/routes/staff.js'; import leaveRequestsRoutes from './backend/routes/leaveRequests.js'; import cron from 'node-cron'; import { runAutoRekapAlfa } from './backend/routes/attendance.js'; // Define __dirname for ES Modules const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); const port = process.env.PORT || 3010; // Middleware app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] })); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); // API Routes app.use('/api/users', usersRoutes); app.use('/api/attendance', attendanceRoutes); app.use('/api/registrations', registrationsRoutes); app.use('/api/settings', settingsRoutes); app.use('/api/auth', authRoutes); app.use('/api/staff', staffRoutes); app.use('/api/leave-requests', leaveRequestsRoutes); // Health check endpoint app.get('/api/health', async (req, res) => { try { // Ping database await pool.query('SELECT 1'); res.json({ status: 'ok', timestamp: new Date().toISOString(), database: 'connected', dbName: process.env.DB_NAME, port: port }); } catch (error) { console.error('Health Check Failed:', error); res.status(500).json({ status: 'error', database: 'disconnected', error: error.message }); } }); // Serve static files from the 'dist' directory (Vite build output) app.use(express.static(path.join(__dirname, 'dist'))); // Handle SPA routing: return index.html for any unknown routes app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'dist', 'index.html')); }); // Initialize database and start server const startServer = async () => { try { // 1. Test database connection await testConnection(); // 2. Initialize database schema await createTables(); // 3. Start server app.listen(port, async () => { console.log(`\nšŸš€ Server is running on port ${port}`); console.log(`šŸ“ Local: http://localhost:${port}`); console.log(`šŸ“Š API: http://localhost:${port}/api/health`); console.log(`šŸ—ƒļø Database: ${process.env.DB_NAME}@${process.env.DB_HOST}`); // 4. Initialize Scheduler (Cron Job) - Read time from database try { const pool = (await import('./backend/db.js')).default; const [timeRow] = await pool.query("SELECT setting_value FROM settings WHERE setting_key = 'AUTO_REKAP_ALFA_TIME'"); const rekapTime = timeRow.length > 0 ? timeRow[0].setting_value : '19:00'; const [hour, minute] = rekapTime.split(':'); cron.schedule(`${minute} ${hour} * * *`, async () => { console.log(`ā° [Cron] Running scheduled Alfa rekap at ${rekapTime}...`); await runAutoRekapAlfa(); }, { timezone: "Asia/Makassar" // WITA }); console.log(`ā° Scheduler initialized (Auto-Rekap Alfa at ${rekapTime} WITA)`); } catch (cronError) { console.error('ā° Scheduler initialization failed:', cronError.message); } }); } catch (error) { console.error('āŒ Server startup failed:', error.message); process.exit(1); } }; startServer();