@@ -39,16 +39,25 @@ app.locals.appVersion = process.env.APP_VERSION || `v${pkg.version}`;
3939
4040// =================== Security & Middleware ===================
4141
42+ // Disable ETag to reduce server overhead
43+ app . set ( "etag" , false ) ;
44+
45+ // Global connection optimization
46+ app . use ( ( req , res , next ) => {
47+ res . setHeader ( "Connection" , "keep-alive" ) ;
48+ next ( ) ;
49+ } ) ;
50+
4251// =================== Global Performance & Security ===================
4352app . use ( compression ( ) ) ; // Compress all responses
4453
4554// Optimize nonce generation: Only for HTML/EJS requests to reduce crypto overhead
4655app . use ( ( req , res , next ) => {
47- const isHtml = req . accepts ( ' html' ) ;
56+ const isHtml = req . accepts ( " html" ) ;
4857 if ( isHtml ) {
4958 res . locals . nonce = crypto . randomBytes ( 16 ) . toString ( "base64" ) ;
5059 } else {
51- res . locals . nonce = '' ;
60+ res . locals . nonce = "" ;
5261 }
5362 next ( ) ;
5463} ) ;
@@ -70,15 +79,19 @@ const CACHE_DURATION = 30000; // 30 seconds
7079app . use ( async ( req , res , next ) => {
7180 try {
7281 // Only fetch for pages that might render the navbar
73- if ( req . method === 'GET' && ! req . path . startsWith ( '/api/' ) && ! req . path . startsWith ( '/public/dvr/' ) ) {
74- const now = Date . now ( ) ;
75- if ( ! cachedActivities || ( now - lastFetchTime > CACHE_DURATION ) ) {
76- cachedActivities = await getRecentActivities ( 5 ) ;
77- lastFetchTime = now ;
78- }
79- res . locals . recentActivities = cachedActivities ;
82+ if (
83+ req . method === "GET" &&
84+ ! req . path . startsWith ( "/api/" ) &&
85+ ! req . path . startsWith ( "/public/dvr/" )
86+ ) {
87+ const now = Date . now ( ) ;
88+ if ( ! cachedActivities || now - lastFetchTime > CACHE_DURATION ) {
89+ cachedActivities = await getRecentActivities ( 5 ) ;
90+ lastFetchTime = now ;
91+ }
92+ res . locals . recentActivities = cachedActivities ;
8093 }
81- } catch ( err ) {
94+ } catch ( err ) {
8295 res . locals . recentActivities = [ ] ;
8396 }
8497 next ( ) ;
@@ -90,7 +103,7 @@ const skipVideo = (req, res) => {
90103
91104app . use (
92105 morgan (
93- process . env . NODE_ENV === "production" ? "combined " : "dev" , // Use faster 'combined' or 'tiny' in prod
106+ process . env . NODE_ENV === "production" ? "tiny " : "dev" , // Use faster 'tiny' in prod
94107 {
95108 skip : skipVideo ,
96109 stream : {
@@ -116,7 +129,7 @@ app.use(
116129 "'self'" ,
117130 "https://cdn.jsdelivr.net" ,
118131 "https://cdnjs.cloudflare.com" ,
119- ( req , res ) => res . locals . nonce ? `'nonce-${ res . locals . nonce } '` : '' ,
132+ ( req , res ) => ( res . locals . nonce ? `'nonce-${ res . locals . nonce } '` : "" ) ,
120133 ] . filter ( Boolean ) ,
121134 "worker-src" : [ "'self'" , "blob:" ] ,
122135 "style-src" : [
@@ -157,8 +170,8 @@ app.use(
157170 } )
158171) ;
159172
160- app . use ( express . json ( { limit : "10mb " } ) ) ;
161- app . use ( express . urlencoded ( { extended : true , limit : "10mb " } ) ) ;
173+ app . use ( express . json ( { limit : "1mb " } ) ) ;
174+ app . use ( express . urlencoded ( { extended : true , limit : "1mb " } ) ) ;
162175app . use ( cookieParser ( ) ) ;
163176
164177// CSRF Protection Initialization (Cookie-based)
@@ -213,29 +226,26 @@ app.use(
213226 "/hls" ,
214227 express . static ( streamDir , {
215228 setHeaders : ( res , filePath ) => {
216- // Prevent caching of HLS files to avoid stale footage
217- res . setHeader ( "Cache-Control" , "no-store, no-cache, must-revalidate, proxy-revalidate" ) ;
218- res . setHeader ( "Pragma" , "no-cache" ) ;
219- res . setHeader ( "Expires" , "0" ) ;
220- res . setHeader ( "Surrogate-Control" , "no-store" ) ;
221-
222229 if ( filePath . endsWith ( ".m3u8" ) ) {
230+ res . setHeader ( "Cache-Control" , "no-cache" ) ;
223231 res . setHeader ( "Content-Type" , "application/vnd.apple.mpegurl" ) ;
224232 } else if ( filePath . endsWith ( ".ts" ) ) {
233+ res . setHeader ( "Cache-Control" , "public, max-age=60" ) ;
225234 res . setHeader ( "Content-Type" , "video/mp2t" ) ;
226235 }
227236 } ,
228237 } )
229238) ;
230239
231- // =================== Disable HLS Cache ===================
240+ // =================== Optimal HLS Caching strategy ===================
232241app . use (
233242 "/streams" ,
234243 ( req , res , next ) => {
235- res . setHeader ( "Cache-Control" , "no-store, no-cache, must-revalidate, proxy-revalidate" ) ;
236- res . setHeader ( "Pragma" , "no-cache" ) ;
237- res . setHeader ( "Expires" , "0" ) ;
238- res . setHeader ( "Surrogate-Control" , "no-store" ) ;
244+ if ( req . path . endsWith ( ".m3u8" ) ) {
245+ res . setHeader ( "Cache-Control" , "no-cache" ) ;
246+ } else if ( req . path . endsWith ( ".ts" ) ) {
247+ res . setHeader ( "Cache-Control" , "public, max-age=60" ) ;
248+ }
239249 next ( ) ;
240250 } ,
241251 express . static ( path . join ( __dirname , "public" , "streams" ) )
@@ -245,8 +255,10 @@ app.use(
245255app . use ( publicRoutes ) ;
246256
247257// 2. Specific Security Middleware for Admin/API
248- // Apply CSRF protection to routes that render forms or handle POSTs
249258app . use ( "/" , ( req , res , next ) => {
259+ if ( [ "GET" , "HEAD" , "OPTIONS" ] . includes ( req . method ) ) {
260+ return next ( ) ;
261+ }
250262 // Skip CSRF for purely public streaming APIs if they are GET only
251263 if ( req . path . startsWith ( "/api/public" ) && req . method === "GET" ) {
252264 return next ( ) ;
@@ -275,7 +287,7 @@ app.use((req, res, next) => {
275287
276288// 3. Admin/Protected Routes
277289// Note: Sensitive rate limiting is now applied inside userRouter specifically for login/password
278- app . use ( "/" , userRouter ) ;
290+ app . use ( "/" , userRouter ) ;
279291app . use ( "/camera" , checkAuth , cameraRoutes ) ;
280292app . use ( "/dvr" , checkAuth , dvrRoutes ) ;
281293app . use ( settingsRoutes ) ;
@@ -298,7 +310,6 @@ app.post("/api/start-stream", async (req, res) => {
298310 }
299311} ) ;
300312
301-
302313app . post ( "/api/stop-stream" , ( req , res ) => {
303314 const { rtspUrl } = req . body ;
304315 if ( ! rtspUrl ) return res . status ( 400 ) . json ( { error : "Missing RTSP URL" } ) ;
@@ -340,10 +351,6 @@ app.get("/api/public/camera/:id/hls", async (req, res) => {
340351 }
341352} ) ;
342353
343-
344-
345-
346-
347354// =================== Start Server ===================
348355const server = http . createServer ( app ) ;
349356
@@ -364,7 +371,7 @@ const gracefulShutdown = () => {
364371 logger . info ( "HTTP server closed." ) ;
365372 process . exit ( 0 ) ;
366373 } ) ;
367-
374+
368375 // Force exit if server doesn't close in 5 seconds
369376 setTimeout ( ( ) => {
370377 logger . error ( "Could not close connections in time, forcefully shutting down" ) ;
0 commit comments