@@ -30,22 +30,25 @@ router.use(prefix, express.static(path.join(__dirname, "static")));
3030/** Logging */
3131router . use ( morgan ( "dev" ) ) ;
3232
33- const MAX_REQUEST_SIZE = process . env . LOWCODER_MAX_REQUEST_SIZE || "50mb" ;
33+ const MAX_REQUEST_SIZE_BYTES = parseRequestLimitNoLib (
34+ process . env . LOWCODER_MAX_REQUEST_SIZE ,
35+ "50mb"
36+ ) ;
3437
3538/** Parse the request */
36- router . use ( express . urlencoded ( { extended : false , limit : MAX_REQUEST_SIZE } ) ) ;
39+ router . use ( express . urlencoded ( { extended : false , limit : MAX_REQUEST_SIZE_BYTES } ) ) ;
3740
3841/** Custom middleware: use raw body for encrypted requests */
3942router . use ( ( req , res , next ) => {
4043 if ( req . headers [ "x-encrypted" ] ) {
41- bodyParser . text ( { type : "*/*" , limit : MAX_REQUEST_SIZE } ) ( req , res , next ) ;
44+ bodyParser . text ( { type : "*/*" , limit : MAX_REQUEST_SIZE_BYTES } ) ( req , res , next ) ;
4245 } else {
43- bodyParser . json ( { limit : MAX_REQUEST_SIZE } ) ( req , res , next ) ;
46+ bodyParser . json ( { limit : MAX_REQUEST_SIZE_BYTES } ) ( req , res , next ) ;
4447 }
4548} ) ;
4649
4750/** Takes care of JSON data */
48- router . use ( express . json ( { limit : MAX_REQUEST_SIZE } ) ) ;
51+ router . use ( express . json ( { limit : MAX_REQUEST_SIZE_BYTES } ) ) ;
4952
5053/** RULES OF OUR API */
5154
@@ -62,7 +65,7 @@ const corsOptions: CorsOptions = {
6265 'Accept' ,
6366 'Content-Type'
6467 ] ,
65- methods : [ 'GET' , 'POST' , 'PUT' , 'DELETE' , 'PATCH' ]
68+ methods : [ 'GET' , 'POST' , 'PUT' , 'DELETE' , 'PATCH' , 'OPTIONS' ]
6669} ;
6770
6871router . use ( cors ( corsOptions ) ) ;
@@ -97,3 +100,106 @@ router.use((req, res, next) => {
97100const httpServer = http . createServer ( router ) ;
98101const PORT = process . env . NODE_SERVICE_PORT ?? 6060 ;
99102httpServer . listen ( PORT , ( ) => logger . info ( `The server is running on port: ${ PORT } ` ) ) ;
103+
104+ /**
105+ * Parses request size strings into bytes.
106+ *
107+ * Accepts:
108+ * - "52428800" => bytes
109+ * - "50mb", "128kb", "1gb" => bytes (binary base 1024)
110+ * - "50m", "128k", "1g" => Spring short units (treated as mb/kb/gb)
111+ * - "1.5m", "0.25gb" => decimals supported
112+ * - "50 m" => whitespace tolerated
113+ *
114+ * Units are binary (KiB/MiB/GiB) using 1024 multipliers, which matches Node conventions well.
115+ * (If you want decimal SI units (1000), adjust multipliers accordingly.)
116+ */
117+ function parseRequestLimitNoLib ( input : unknown , fallback : string = "50mb" ) : number {
118+ const raw = input ?? fallback ;
119+
120+ // Direct number => bytes
121+ if ( typeof raw === "number" ) {
122+ if ( ! Number . isFinite ( raw ) || raw <= 0 ) throw new Error ( `Invalid request size number: ${ raw } ` ) ;
123+ return Math . floor ( raw ) ;
124+ }
125+
126+ const s0 = String ( raw ) . trim ( ) ;
127+ if ( ! s0 ) return parseRequestLimitNoLib ( fallback ) ;
128+
129+ // Remove whitespace and lowercase
130+ const s = s0 . replace ( / \s + / g, "" ) . toLowerCase ( ) ;
131+
132+ // Pure integer bytes
133+ if ( / ^ \d + $ / u. test ( s ) ) {
134+ const n = Number ( s ) ;
135+ if ( ! Number . isFinite ( n ) || n <= 0 ) throw new Error ( `Invalid request size: ${ s0 } ` ) ;
136+ return n ;
137+ }
138+
139+ // Match: number + unit (unit may be k|kb|m|mb|g|gb|t|tb|p|pb|b)
140+ const m = s . match ( / ^ ( \d + (?: \. \d + ) ? ) ( b | k b | k | m b | m | g b | g | t b | t | p b | p ) $ / u) ;
141+ if ( ! m ) {
142+ throw new Error (
143+ `Invalid LOWCODER_MAX_REQUEST_SIZE: "${ s0 } ". ` +
144+ `Use bytes ("52428800"), long units ("50mb"), or Spring units ("50m").`
145+ ) ;
146+ }
147+
148+ const value = Number ( m [ 1 ] ) ;
149+ const unit = m [ 2 ] ;
150+
151+ if ( ! Number . isFinite ( value ) || value <= 0 ) {
152+ throw new Error ( `Invalid LOWCODER_MAX_REQUEST_SIZE numeric value: "${ m [ 1 ] } "` ) ;
153+ }
154+
155+ // Binary multipliers (base 1024)
156+ const KB = 1024 ;
157+ const MB = 1024 * KB ;
158+ const GB = 1024 * MB ;
159+ const TB = 1024 * GB ;
160+ const PB = 1024 * TB ;
161+
162+ let bytes : number ;
163+
164+ switch ( unit ) {
165+ case "b" :
166+ bytes = value ;
167+ break ;
168+
169+ case "k" :
170+ case "kb" :
171+ bytes = value * KB ;
172+ break ;
173+
174+ case "m" :
175+ case "mb" :
176+ bytes = value * MB ;
177+ break ;
178+
179+ case "g" :
180+ case "gb" :
181+ bytes = value * GB ;
182+ break ;
183+
184+ case "t" :
185+ case "tb" :
186+ bytes = value * TB ;
187+ break ;
188+
189+ case "p" :
190+ case "pb" :
191+ bytes = value * PB ;
192+ break ;
193+
194+ default :
195+ // Should be unreachable because of regex, but keep safe:
196+ throw new Error ( `Unsupported unit: "${ unit } "` ) ;
197+ }
198+
199+ // Guard: must fit into a safe integer
200+ if ( ! Number . isFinite ( bytes ) || bytes <= 0 || ! Number . isSafeInteger ( Math . floor ( bytes ) ) ) {
201+ throw new Error ( `LOWCODER_MAX_REQUEST_SIZE too large or invalid: "${ s0 } "` ) ;
202+ }
203+
204+ return Math . floor ( bytes ) ;
205+ }
0 commit comments