-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
242 lines (204 loc) · 7.74 KB
/
Copy pathserver.js
File metadata and controls
242 lines (204 loc) · 7.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/**
* GasGuard - IoT Gas Leak Detection System
* Main server entry point
*
* Initializes Express server, WebSocket, hardware, and database
*/
require('dotenv').config();
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const path = require('path');
const cron = require('node-cron');
// Application modules
const HardwareManager = require('./src/hardware/hardwareManager');
const AuthMiddleware = require('./src/middleware/auth');
const { errorHandler, notFoundHandler } = require('./src/middleware/errorHandler');
const WebSocketHandler = require('./src/websocket/websocketHandler');
const db = require('./src/database/connection');
const { SensorData, SystemLog } = require('./src/database/models');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
// Middleware configuration
app.use(express.json({ limit: '25mb' }));
app.use(express.urlencoded({ extended: true, limit: '25mb' }));
// Static file serving with caching
app.use(express.static(path.join(__dirname, 'public'), {
maxAge: '1d',
etag: true,
lastModified: true
}));
// Hardware and WebSocket initialization
const hardwareManager = new HardwareManager();
const wsHandler = new WebSocketHandler(io, hardwareManager, db);
// API route configuration
app.use('/api/auth', require('./src/routes/auth')());
app.use('/api/dashboard', require('./src/routes/dashboard')(db, hardwareManager));
app.use('/api/config', require('./src/routes/config')(db, hardwareManager));
app.use('/api/alerts', require('./src/routes/alerts')(db, hardwareManager));
app.use('/api/settings', require('./src/routes/settings')());
app.use('/api/contacts', require('./src/routes/contacts')());
app.use('/api/history', require('./src/routes/history')());
app.use('/api/wifi', require('./src/routes/wifi')(db));
app.use('/api/calibration', require('./src/routes/calibration')());
app.use('/api/logs', require('./src/routes/logs')());
app.use('/api/retention', require('./src/routes/dataRetention')());
// Page routes
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.get('/setup', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'setup.html'));
});
app.get('/dashboard', AuthMiddleware.requireAuth, (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'dashboard.html'));
});
app.get('/settings', AuthMiddleware.requireAuth, AuthMiddleware.requireAdmin, (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'settings.html'));
});
// Error handling
app.use('/api/*', notFoundHandler);
app.use((req, res, next) => {
if (!req.path.startsWith('/api')) {
res.status(404).sendFile(path.join(__dirname, 'public', '404.html'));
} else {
next();
}
});
app.use(errorHandler);
const PORT = process.env.PORT || 3000;
/**
* Initialize application components
* - Database connection and schema
* - Hardware components
* - SMS service
* - Scheduled tasks
*/
async function initialize() {
try {
console.log('Initializing GasGuard System...\n');
const isDevelopment = process.platform !== 'linux';
if (isDevelopment) {
console.log('WARNING: Development mode - Mock hardware enabled');
}
// Initialize database
console.log('Initializing database...');
await db.initialize();
// Check if database schema exists
try {
await db.get('SELECT COUNT(*) as count FROM users');
console.log('Database connected\n');
} catch (error) {
// Initialize database schema
console.log('Creating database schema...');
const DatabaseMigration = require('./src/database/migrate');
const migration = new DatabaseMigration();
await migration.runSchema();
await migration.seedUsers();
await migration.seedSettings();
console.log('Database initialized\n');
}
// Log system startup
await SystemLog.info('system', 'GasGuard system starting up', {
source: 'server.initialize',
data: { mode: isDevelopment ? 'development' : 'production' }
});
// Initialize hardware components
console.log('Initializing hardware...');
await hardwareManager.initialize();
console.log('Hardware initialized\n');
// Initialize SMS service
console.log('Initializing SMS service...');
const textbeeService = require('./src/services/textbeeService');
await textbeeService.initialize();
console.log('');
// Schedule daily data cleanup (2 AM)
cron.schedule('0 2 * * *', async () => {
try {
console.log('Running data retention cleanup...');
const [sensorDeleted, logsDeleted] = await Promise.all([
SensorData.deleteOlderThan(90),
SystemLog.deleteOlderThan(90)
]);
console.log(`Deleted ${sensorDeleted} old sensor records`);
console.log(`Deleted ${logsDeleted} old log records`);
await SystemLog.info('system', 'Data retention cleanup completed', {
source: 'cron.cleanup',
data: { sensorRecords: sensorDeleted, logRecords: logsDeleted }
});
} catch (error) {
console.error('Cleanup job error:', error);
await SystemLog.error('system', 'Data cleanup failed: ' + error.message, {
source: 'cron.cleanup'
});
}
});
console.log('Data retention cleanup scheduled (daily at 2 AM)\n');
// Start HTTP server
server.listen(PORT, '0.0.0.0', () => {
console.log('═══════════════════════════════════════════════════');
console.log(`GasGuard Server running on http://0.0.0.0:${PORT}`);
console.log('═══════════════════════════════════════════════════');
if (isDevelopment) {
console.log('Development Mode: Mock hardware active');
} else {
console.log('Production Mode: Real hardware active');
}
console.log(`\nWeb Interface: http://0.0.0.0:${PORT}/`);
console.log(`Dashboard: http://0.0.0.0:${PORT}/dashboard`);
console.log('═══════════════════════════════════════════════════\n');
});
} catch (error) {
console.error('Failed to initialize system:', error);
try {
await SystemLog.error('system', 'System initialization failed: ' + error.message, {
source: 'server.initialize'
});
} catch (logError) {
// Silently fail if logging is unavailable
}
process.exit(1);
}
}
/**
* Graceful shutdown handler
* Ensures proper cleanup of resources
*/
process.on('SIGINT', async () => {
console.log('\n\nShutting down gracefully...');
try {
await SystemLog.info('system', 'GasGuard system shutting down', {
source: 'server.shutdown'
});
} catch (error) {
console.error('Error logging shutdown:', error);
}
// Close WebSocket connections
console.log('Closing WebSocket connections...');
io.close(() => {
console.log('WebSockets closed');
});
// Close HTTP server
console.log('Closing HTTP server...');
server.close(async () => {
console.log('HTTP server closed');
// Cleanup hardware
console.log('Cleaning up hardware...');
await hardwareManager.cleanup();
console.log('Hardware cleanup complete');
// Close database
console.log('Closing database...');
await db.close();
console.log('Database closed');
console.log('Shutdown complete');
process.exit(0);
});
// Force shutdown after timeout
setTimeout(() => {
console.error('Forced shutdown after timeout');
process.exit(1);
}, 15000);
});
// Start application
initialize();