119 lines
4.0 KiB
JavaScript
119 lines
4.0 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const path = require('path');
|
|
require('dotenv').config();
|
|
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
|
|
|
// Initialize database connection (auto-creates schema)
|
|
require('./database/db');
|
|
|
|
// Migration runner
|
|
const { runMigrations } = require('./database/runMigrations');
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 5001;
|
|
|
|
// Middleware
|
|
|
|
/**
|
|
* Origins allowed to call this API.
|
|
*
|
|
* Production keeps an explicit list. Development also accepts ANY port on
|
|
* localhost, because Vite is configured for 3000 but silently falls back when
|
|
* that port is taken — 3001, 3002, 3003 and up, depending on what else the
|
|
* developer happens to be running. A hardcoded list meant the app loaded fine
|
|
* on the fallback port while every request failed CORS, which reads as "the
|
|
* data is gone" rather than "the port moved".
|
|
*
|
|
* The loopback-only test is what keeps this safe: it never reflects a remote
|
|
* origin, so `credentials: true` cannot be used by another site.
|
|
*/
|
|
const ALLOWED_ORIGINS = [
|
|
'http://localhost:3000',
|
|
'http://localhost:3001',
|
|
'http://localhost:3002',
|
|
'http://localhost:5173',
|
|
'http://localhost:5174',
|
|
'http://localhost:5002',
|
|
'http://192.168.192.106:5002',
|
|
];
|
|
|
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
|
const LOOPBACK_ORIGIN = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/;
|
|
|
|
app.use(
|
|
cors({
|
|
origin: (origin, callback) => {
|
|
// No Origin header: same-origin, curl, or a server-to-server call.
|
|
if (!origin) return callback(null, true);
|
|
if (ALLOWED_ORIGINS.includes(origin)) return callback(null, true);
|
|
if (isDevelopment && LOOPBACK_ORIGIN.test(origin)) return callback(null, true);
|
|
return callback(new Error(`Origin tidak diizinkan oleh CORS: ${origin}`));
|
|
},
|
|
credentials: true,
|
|
})
|
|
);
|
|
app.use(express.json());
|
|
|
|
// Request logging middleware
|
|
app.use((req, res, next) => {
|
|
next();
|
|
});
|
|
|
|
// Routes
|
|
const cyclesRouter = require('./routes/cycles');
|
|
const mortalityRouter = require('./routes/mortality');
|
|
const chickenCountsRouter = require('./routes/chickenCounts');
|
|
const apiKeysRouter = require('./routes/apiKeys');
|
|
const kandangsRouter = require('./routes/kandangs');
|
|
const feedSackColumnConfigRouter = require('./routes/feedSackColumnConfig');
|
|
const manualFeedSackRouter = require('./routes/manualFeedSack');
|
|
const cycleFeedInitialBalanceRouter = require('./routes/cycleFeedInitialBalance');
|
|
const auditLogsRouter = require('./routes/auditLogs');
|
|
const aiInsightsRouter = require('./routes/aiInsights');
|
|
const dashboardReportSnapshotsRouter = require('./routes/dashboardReportSnapshots');
|
|
|
|
app.use('/api/cycles', cyclesRouter);
|
|
app.use('/api/mortality', mortalityRouter);
|
|
app.use('/api/chicken-counting', chickenCountsRouter);
|
|
app.use('/api/admin/api-keys', apiKeysRouter);
|
|
app.use('/api/kandangs', kandangsRouter);
|
|
app.use('/api/feed-sack-column-config', feedSackColumnConfigRouter);
|
|
app.use('/api/manual-feed-sack', manualFeedSackRouter);
|
|
app.use('/api/cycle-feed-initial-balance', cycleFeedInitialBalanceRouter);
|
|
app.use('/api/audit-logs', auditLogsRouter);
|
|
app.use('/api/ai-insights', aiInsightsRouter);
|
|
app.use('/api/dashboard-report-snapshots', dashboardReportSnapshotsRouter);
|
|
|
|
// Health check endpoint
|
|
app.get('/health', (req, res) => {
|
|
res.json({ status: 'ok', timestamp: new Date().toISOString(), database: 'postgresql' });
|
|
});
|
|
|
|
// 404 handler
|
|
app.use((req, res) => {
|
|
res.status(404).json({ success: false, error: 'Route not found' });
|
|
});
|
|
|
|
// Error handling middleware (MUST be last)
|
|
const { errorHandler } = require('./middleware/errorHandler');
|
|
app.use(errorHandler);
|
|
|
|
// Start server with migrations
|
|
async function startServer() {
|
|
try {
|
|
// Run database migrations first
|
|
await runMigrations();
|
|
|
|
// Then start the server
|
|
app.listen(PORT, () => {});
|
|
} catch (error) {
|
|
console.error('❌ Failed to start server due to migration error');
|
|
console.error(error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Start the server
|
|
startServer();
|