1- import formidable from "formidable " ;
1+ import cors from "cors " ;
22import express from "express" ;
3+ import rateLimit from "express-rate-limit" ;
4+ import formidable from "formidable" ;
35import fs from "fs" ;
46import http from "http" ;
57import https from "https" ;
6- import cors from "cors" ;
7- import path from "path" ;
88import os from "os" ;
9+ import path from "path" ;
910import { fileURLToPath } from "url" ;
1011
1112const __filename = fileURLToPath ( import . meta. url ) ;
@@ -23,6 +24,15 @@ if (!fs.existsSync(distPath)) {
2324
2425const app = express ( ) ;
2526
27+ // Rate limiting
28+ const limiter = rateLimit ( {
29+ windowMs : 15 * 60 * 1000 , // 15 minutes
30+ max : 100 , // Limit each IP to 100 requests per windowMs
31+ message : "Too many requests from this IP, please try again later." ,
32+ } ) ;
33+
34+ app . use ( limiter ) ;
35+
2636app . use (
2737 cors ( {
2838 origin : ( origin , callback ) => {
@@ -36,6 +46,7 @@ app.use("/dist", express.static(distPath));
3646app . use ( "/assets" , express . static ( path . join ( __dirname , "../samples/demo/assets" ) ) ) ;
3747app . use ( "/css" , express . static ( path . join ( __dirname , "../samples/demo/css" ) ) ) ;
3848app . use ( "/font" , express . static ( path . join ( __dirname , "../samples/demo/font" ) ) ) ;
49+ app . use ( "/samples" , express . static ( path . join ( __dirname , "../samples" ) ) ) ;
3950
4051// Routes
4152app . get ( "/" , ( req , res ) => {
@@ -50,8 +61,16 @@ app.get("/hello-world", (req, res) => {
5061 res . sendFile ( path . join ( __dirname , "../samples/hello-world.html" ) ) ;
5162} ) ;
5263
53- app . get ( "/scenarios/use-file-input" , ( req , res ) => {
54- res . sendFile ( path . join ( __dirname , "../samples/scenarios/use-file-input.html" ) ) ;
64+ app . get ( "/multi-page-scanning" , ( req , res ) => {
65+ res . sendFile ( path . join ( __dirname , "../samples/scenarios/multi-page-scanning.html" ) ) ;
66+ } ) ;
67+
68+ app . get ( "/scanning-to-pdf" , ( req , res ) => {
69+ res . sendFile ( path . join ( __dirname , "../samples/scenarios/scanning-to-pdf.html" ) ) ;
70+ } ) ;
71+
72+ app . get ( "/image-file-scanning" , ( req , res ) => {
73+ res . sendFile ( path . join ( __dirname , "../samples/scenarios/image-file-scanning.html" ) ) ;
5574} ) ;
5675
5776// Allow upload feature
@@ -61,6 +80,8 @@ app.post("/upload", function (req, res) {
6180 const form = formidable ( {
6281 multiples : false ,
6382 keepExtensions : true ,
83+ maxFileSize : 25 * 1024 * 1024 , // 25MB limit
84+ maxFiles : 1 ,
6485 } ) ;
6586
6687 form . parse ( req , ( err , fields , files ) => {
@@ -74,12 +95,27 @@ app.post("/upload", function (req, res) {
7495 return res . status ( 400 ) . json ( { success : false , message : "No file uploaded" } ) ;
7596 }
7697
77- // Get current timestamp
78- let dt = new Date ( ) ;
98+ // Sanitize filename to prevent path traversal
99+ const newFileName = path . basename ( uploadedFile . originalFilename ) ;
100+
101+ // Validate file extension (whitelist for document scanner)
102+ const allowedExtensions = [ '.jpg' , '.jpeg' , '.png' , '.pdf' , '.bmp' , '.tiff' , '.tif' ] ;
103+ const fileExt = path . extname ( newFileName ) . toLowerCase ( ) ;
104+ if ( ! allowedExtensions . includes ( fileExt ) ) {
105+ return res . status ( 400 ) . json ( { success : false , message : "File type not allowed. Allowed types: jpg, jpeg, png, pdf, bmp, tiff" } ) ;
106+ }
107+
108+ // Sanitize filename to remove potentially dangerous characters
109+ const sanitizedName = newFileName . replace ( / [ ^ a - z A - Z 0 - 9 . _ - ] / g, '_' ) ;
110+
111+ const fileSavePath = __dirname ;
112+ const newFilePath = path . resolve ( fileSavePath , sanitizedName ) ;
79113
80- const fileSavePath = path . join ( __dirname , "\\" ) ;
81- const newFileName = uploadedFile . originalFilename ;
82- const newFilePath = path . join ( fileSavePath , newFileName ) ;
114+ // Verify path is within allowed directory (robust check using relative path)
115+ const relativePath = path . relative ( fileSavePath , newFilePath ) ;
116+ if ( relativePath . startsWith ( '..' ) || path . isAbsolute ( relativePath ) ) {
117+ return res . status ( 400 ) . json ( { success : false , message : "Invalid filename - path traversal detected" } ) ;
118+ }
83119
84120 // Move the uploaded file to the desired directory
85121 fs . rename ( uploadedFile . filepath , newFilePath , ( err ) => {
@@ -88,11 +124,11 @@ app.post("/upload", function (req, res) {
88124 return res . status ( 500 ) . send ( "Error saving the file." ) ;
89125 }
90126 console . log ( `\x1b[33m ${ newFileName } \x1b[0m uploaded successfully!` ) ;
91- } ) ;
92- res . status ( 200 ) . json ( {
93- success : true ,
94- message : ` ${ newFileName } uploaded successfully` ,
95- filename : newFileName ,
127+ res . status ( 200 ) . json ( {
128+ success : true ,
129+ message : ` ${ newFileName } uploaded successfully` ,
130+ filename : newFileName ,
131+ } ) ;
96132 } ) ;
97133 } ) ;
98134 } catch ( error ) {
@@ -151,7 +187,7 @@ httpsServer.on("error", (error) => {
151187 console . log ( `2. Close any other applications using port ${ httpsPort } ` ) ;
152188 console . log ( `3. Wait a few moments and try again - the port might be in a cleanup state\n` ) ;
153189 } else {
154- console . error ( "\x1b[31mHTTP Server error:\x1b[0m" , error ) ;
190+ console . error ( "\x1b[31mHTTPS Server error:\x1b[0m" , error ) ;
155191 }
156192 process . exit ( 1 ) ;
157193} ) ;
@@ -161,8 +197,12 @@ httpServer.listen(httpPort, () => {
161197 console . log ( "\n\x1b[1m Dynamsoft Document Scanner Samples\x1b[0m\n" ) ;
162198 console . log ( "\x1b[36m HTTP URLs:\x1b[0m" ) ;
163199 console . log ( "\x1b[90m-------------------\x1b[0m" ) ;
164- console . log ( "\x1b[33m Hello World:\x1b[0m http://localhost:" + httpPort + "/hello-world" ) ;
165- console . log ( "\x1b[33m Demo:\x1b[0m http://localhost:" + httpPort + "/demo" ) ;
200+ console . log ( "\x1b[1m\x1b[35m → Samples Index:\x1b[0m \x1b[1mhttp://localhost:" + httpPort + "/samples\x1b[0m" ) ;
201+ console . log ( "\x1b[33m Hello World:\x1b[0m http://localhost:" + httpPort + "/hello-world" ) ;
202+ console . log ( "\x1b[33m Scanning to PDF:\x1b[0m http://localhost:" + httpPort + "/scanning-to-pdf" ) ;
203+ console . log ( "\x1b[33m Demo:\x1b[0m http://localhost:" + httpPort + "/demo" ) ;
204+ console . log ( "\x1b[33m Multi-Page Scanning:\x1b[0m http://localhost:" + httpPort + "/multi-page-scanning" ) ;
205+ console . log ( "\x1b[33m Image File Scanning:\x1b[0m http://localhost:" + httpPort + "/image-file-scanning" ) ;
166206} ) ;
167207
168208httpsServer . listen ( httpsPort , "0.0.0.0" , ( ) => {
@@ -179,9 +219,15 @@ httpsServer.listen(httpsPort, "0.0.0.0", () => {
179219 console . log ( "\n" ) ;
180220 console . log ( "\x1b[36m HTTPS URLs:\x1b[0m" ) ;
181221 console . log ( "\x1b[90m-------------------\x1b[0m" ) ;
182- ipv4Addresses . forEach ( ( localIP ) => {
183- console . log ( "\x1b[32m Hello World:\x1b[0m https://" + localIP + ":" + httpsPort + "/hello-world" ) ;
184- console . log ( "\x1b[32m Demo:\x1b[0m https://" + localIP + ":" + httpsPort + "/demo" ) ;
222+ ipv4Addresses . forEach ( ( localIP , index ) => {
223+ if ( index > 0 ) console . log ( "" ) ; // Add spacing between different IPs
224+ console . log ( "\x1b[32m----IP[" + index + "]: " + localIP + "----\x1b" ) ;
225+ console . log ( "\x1b[1m\x1b[35m → Samples Index:\x1b[0m \x1b[1mhttps://" + localIP + ":" + httpsPort + "/samples\x1b[0m" ) ;
226+ console . log ( "\x1b[32m Hello World:\x1b[0m https://" + localIP + ":" + httpsPort + "/hello-world" ) ;
227+ console . log ( "\x1b[32m Scanning to PDF:\x1b[0m https://" + localIP + ":" + httpsPort + "/scanning-to-pdf" ) ;
228+ console . log ( "\x1b[32m Demo:\x1b[0m https://" + localIP + ":" + httpsPort + "/demo" ) ;
229+ console . log ( "\x1b[32m Multi-Page Scanning:\x1b[0m https://" + localIP + ":" + httpsPort + "/multi-page-scanning" ) ;
230+ console . log ( "\x1b[32m Image File Scanning:\x1b[0m https://" + localIP + ":" + httpsPort + "/image-file-scanning" ) ;
185231 } ) ;
186232 console . log ( "\n" ) ;
187233 console . log ( "\x1b[90mPress Ctrl+C to stop the server\x1b[0m\n" ) ;
0 commit comments