11/**
22 * Chunked file uploader for Sploder-Launcher builds
3- * Uploads build artifacts in 90MB chunks with API key // Create URLSearchParams for proper form encoding
4- const formPairs = [];
5- for (const [key, value] of Object.entries(formFields)) {
6- formPairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
7- }
8-
9- const formDataString = formPairs.join('&');
10-
11- // Debug: Show first part of form data to verify structure
12- console.log(`π§ Form data preview: ${formDataString.substring(0, 200)}...`);
13- console.log(`π¦ Chunk ${chunkIndex + 1}/${totalChunks}: ${(chunkData.length / 1024 / 1024).toFixed(2)}MB`);cation
3+ * Uploads build artifacts in 90MB chunks with API key authentication
144 */
155
166const fs = require ( 'fs' ) ;
@@ -22,29 +12,28 @@ const { createHash } = require('crypto');
2212// Configuration
2313const CHUNK_SIZE = 90 * 1024 * 1024 ; // 90MB chunks
2414const API_KEY = process . env . UPLOAD_API_KEY ;
15+ const UPLOAD_URL = process . env . UPLOAD_URL ;
2516
17+ // Validate environment
2618if ( ! API_KEY ) {
2719 console . error ( 'β UPLOAD_API_KEY environment variable is required' ) ;
2820 process . exit ( 1 ) ;
2921}
3022
31- console . log ( `π API Key loaded: ${ API_KEY . substring ( 0 , 8 ) } ...${ API_KEY . substring ( API_KEY . length - 4 ) } ` ) ;
32-
33- if ( ! process . env . UPLOAD_URL ) {
23+ if ( ! UPLOAD_URL ) {
3424 console . error ( 'β UPLOAD_URL environment variable is required' ) ;
3525 process . exit ( 1 ) ;
3626}
3727
38- const UPLOAD_ENDPOINT = process . env . UPLOAD_URL + '/update/upload.php' ;
28+ const UPLOAD_ENDPOINT = UPLOAD_URL + '/update/upload.php' ;
3929
4030// Parse command line arguments
41- const args = process . argv . slice ( 2 ) ;
42- const uploadDirArg = args . find ( arg => arg . startsWith ( '--dir=' ) ) ;
43- const uploadDir = uploadDirArg ? uploadDirArg . split ( '=' ) [ 1 ] : './dist' ;
31+ const uploadDir = process . argv . find ( arg => arg . startsWith ( '--dir=' ) ) ?. split ( '=' ) [ 1 ] || './dist' ;
4432
4533console . log ( `π Starting chunked upload from directory: ${ uploadDir } ` ) ;
4634console . log ( `π‘ Upload endpoint: ${ UPLOAD_ENDPOINT } ` ) ;
4735console . log ( `π¦ Chunk size: ${ ( CHUNK_SIZE / 1024 / 1024 ) . toFixed ( 1 ) } MB` ) ;
36+ console . log ( `π API Key: ${ API_KEY . substring ( 0 , 4 ) } ...${ API_KEY . substring ( API_KEY . length - 4 ) } \n` ) ;
4837
4938/**
5039 * Calculate MD5 hash of a file
@@ -66,12 +55,11 @@ function calculateFileHash(filePath) {
6655function makeRequest ( url , options , data ) {
6756 return new Promise ( ( resolve , reject ) => {
6857 const urlObj = new URL ( url ) ;
69- const isHttps = urlObj . protocol === 'https:' ;
70- const client = isHttps ? https : http ;
58+ const client = urlObj . protocol === 'https:' ? https : http ;
7159
7260 const req = client . request ( {
7361 hostname : urlObj . hostname ,
74- port : urlObj . port || ( isHttps ? 443 : 80 ) ,
62+ port : urlObj . port || ( urlObj . protocol === 'https:' ? 443 : 80 ) ,
7563 path : urlObj . pathname + urlObj . search ,
7664 method : options . method || 'POST' ,
7765 headers : options . headers || { }
@@ -80,8 +68,7 @@ function makeRequest(url, options, data) {
8068 res . on ( 'data' , chunk => responseData += chunk ) ;
8169 res . on ( 'end' , ( ) => {
8270 try {
83- const parsed = JSON . parse ( responseData ) ;
84- resolve ( { status : res . statusCode , data : parsed } ) ;
71+ resolve ( { status : res . statusCode , data : JSON . parse ( responseData ) } ) ;
8572 } catch ( error ) {
8673 resolve ( { status : res . statusCode , data : responseData } ) ;
8774 }
@@ -101,39 +88,40 @@ function makeRequest(url, options, data) {
10188/**
10289 * Upload a single chunk using multipart/form-data with raw binary data
10390 */
104- async function uploadChunk ( filePath , fileName , chunkIndex , chunkData , totalChunks , fileHash ) {
105- // Create multipart form data with raw binary data (more efficient than base64)
106- const boundary = '----WebKitFormBoundary' + Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) ;
107-
108- const formFields = {
109- 'api_key' : API_KEY ,
110- 'file_name' : fileName ,
111- 'chunk_index' : chunkIndex . toString ( ) ,
112- 'total_chunks' : totalChunks . toString ( ) ,
113- 'file_hash' : fileHash
114- } ;
115-
116- // Build multipart form data manually
117- let formData = Buffer . alloc ( 0 ) ;
118-
119- // Add text fields
120- for ( const [ key , value ] of Object . entries ( formFields ) ) {
121- const fieldHeader = `--${ boundary } \r\nContent-Disposition: form-data; name="${ key } "\r\n\r\n${ value } \r\n` ;
122- formData = Buffer . concat ( [ formData , Buffer . from ( fieldHeader ) ] ) ;
123- }
124-
125- // Add binary chunk data
126- const binaryHeader = `--${ boundary } \r\nContent-Disposition: form-data; name="chunk_data"; filename="chunk_${ chunkIndex } "\r\nContent-Type: application/octet-stream\r\n\r\n` ;
127- const binaryFooter = `\r\n--${ boundary } --\r\n` ;
128-
129- formData = Buffer . concat ( [
130- formData ,
131- Buffer . from ( binaryHeader ) ,
132- chunkData ,
133- Buffer . from ( binaryFooter )
134- ] ) ;
135-
136- console . log ( `π¦ Chunk ${ chunkIndex + 1 } /${ totalChunks } : ${ ( chunkData . length / 1024 / 1024 ) . toFixed ( 2 ) } MB` ) ;
91+ async function uploadChunk ( fileName , chunkIndex , chunkData , totalChunks , fileHash ) {
92+ const boundary = '----WebKitFormBoundary' + Math . random ( ) . toString ( 36 ) . substring ( 2 ) ;
93+
94+ // Build multipart form data
95+ const parts = [
96+ `--${ boundary } ` ,
97+ `Content-Disposition: form-data; name="api_key"` ,
98+ '' ,
99+ API_KEY ,
100+ `--${ boundary } ` ,
101+ `Content-Disposition: form-data; name="file_name"` ,
102+ '' ,
103+ fileName ,
104+ `--${ boundary } ` ,
105+ `Content-Disposition: form-data; name="chunk_index"` ,
106+ '' ,
107+ chunkIndex . toString ( ) ,
108+ `--${ boundary } ` ,
109+ `Content-Disposition: form-data; name="total_chunks"` ,
110+ '' ,
111+ totalChunks . toString ( ) ,
112+ `--${ boundary } ` ,
113+ `Content-Disposition: form-data; name="file_hash"` ,
114+ '' ,
115+ fileHash ,
116+ `--${ boundary } ` ,
117+ `Content-Disposition: form-data; name="chunk_data"; filename="chunk_${ chunkIndex } "` ,
118+ `Content-Type: application/octet-stream` ,
119+ ''
120+ ] ;
121+
122+ const header = Buffer . from ( parts . join ( '\r\n' ) + '\r\n' ) ;
123+ const footer = Buffer . from ( `\r\n--${ boundary } --\r\n` ) ;
124+ const formData = Buffer . concat ( [ header , chunkData , footer ] ) ;
137125
138126 const response = await makeRequest ( UPLOAD_ENDPOINT , {
139127 method : 'POST' ,
@@ -148,7 +136,7 @@ async function uploadChunk(filePath, fileName, chunkIndex, chunkData, totalChunk
148136 }
149137
150138 if ( ! response . data . success ) {
151- throw new Error ( `Server error: ${ JSON . stringify ( response . data ) } ` ) ;
139+ throw new Error ( `Server error: ${ response . data . message } ` ) ;
152140 }
153141
154142 return response . data ;
@@ -162,7 +150,7 @@ async function uploadFile(filePath) {
162150 const fileSize = fs . statSync ( filePath ) . size ;
163151 const totalChunks = Math . ceil ( fileSize / CHUNK_SIZE ) ;
164152
165- console . log ( `\n π Uploading: ${ fileName } ` ) ;
153+ console . log ( `π Uploading: ${ fileName } ` ) ;
166154 console . log ( `π Size: ${ ( fileSize / 1024 / 1024 ) . toFixed ( 2 ) } MB` ) ;
167155 console . log ( `π§© Chunks: ${ totalChunks } ` ) ;
168156
@@ -177,13 +165,15 @@ async function uploadFile(filePath) {
177165
178166 for await ( const chunk of fileStream ) {
179167 const progress = ( ( chunkIndex + 1 ) / totalChunks * 100 ) . toFixed ( 1 ) ;
180- console . log ( `β¬οΈ Uploading chunk ${ chunkIndex + 1 } / ${ totalChunks } ( ${ progress } %)` ) ;
168+ const chunkSize = ( chunk . length / 1024 / 1024 ) . toFixed ( 2 ) ;
181169
182- await uploadChunk ( filePath , fileName , chunkIndex , chunk , totalChunks , fileHash ) ;
170+ console . log ( `β¬οΈ Uploading chunk ${ chunkIndex + 1 } /${ totalChunks } (${ progress } %) - ${ chunkSize } MB` ) ;
171+
172+ await uploadChunk ( fileName , chunkIndex , chunk , totalChunks , fileHash ) ;
183173 chunkIndex ++ ;
184174 }
185175
186- console . log ( `β
Successfully uploaded: ${ fileName } ` ) ;
176+ console . log ( `β
Successfully uploaded: ${ fileName } \n ` ) ;
187177}
188178
189179/**
@@ -203,30 +193,21 @@ function findFilesToUpload(directory) {
203193 const fullPath = path . join ( directory , item . name ) ;
204194
205195 if ( item . isFile ( ) ) {
206- // Windows installer files (.exe)
196+ // Windows installer files
207197 if ( item . name . endsWith ( '.exe' ) && item . name . includes ( 'Setup' ) ) {
208198 files . push ( { path : fullPath , type : 'windows-installer' } ) ;
209199 }
210- // Windows portable files (.zip)
200+ // Windows portable files
211201 else if ( item . name . endsWith ( '.zip' ) && item . name . includes ( 'Portable' ) ) {
212202 files . push ( { path : fullPath , type : 'windows-portable' } ) ;
213203 }
214- // macOS zip file (direct file)
204+ // macOS zip file
215205 else if ( item . name === 'Sploder-macOS.zip' ) {
216206 files . push ( { path : fullPath , type : 'macos-app' } ) ;
217207 }
218- } else if ( item . isDirectory ( ) ) {
219- // macOS app files (in mac/ directory) - only add if zip doesn't already exist
220- if ( item . name === 'mac' ) {
221- const macZipPath = path . join ( directory , 'Sploder-macOS.zip' ) ;
222- if ( fs . existsSync ( macZipPath ) ) {
223- // Zip file already exists, don't add the directory
224- console . log ( `π Note: Using existing ${ macZipPath } instead of mac directory` ) ;
225- } else {
226- console . log ( `π Note: macOS app directory found but no zip file. Consider creating ${ macZipPath } ` ) ;
227- }
228- }
229208 }
209+ // Note: Directory handling removed to avoid duplicates
210+ // The build script should create Sploder-macOS.zip directly
230211 }
231212
232213 return files ;
@@ -245,25 +226,26 @@ async function main() {
245226 return ;
246227 }
247228
248- console . log ( `\n π Found ${ files . length } files to upload:` ) ;
229+ console . log ( `π Found ${ files . length } files to upload:` ) ;
249230 files . forEach ( file => {
250231 console . log ( ` β’ ${ path . basename ( file . path ) } (${ file . type } )` ) ;
251232 } ) ;
233+ console . log ( '' ) ;
252234
253235 // Upload each file
254236 for ( const file of files ) {
255237 try {
256238 await uploadFile ( file . path ) ;
257239 } catch ( error ) {
258- console . error ( `β Failed to upload ${ file . path } :` , error . message ) ;
240+ console . error ( `β Failed to upload ${ file . path } : ${ error . message } ` ) ;
259241 process . exit ( 1 ) ;
260242 }
261243 }
262244
263- console . log ( '\n π All files uploaded successfully!' ) ;
245+ console . log ( 'π All files uploaded successfully!' ) ;
264246
265247 } catch ( error ) {
266- console . error ( ' β Upload process failed:' , error . message ) ;
248+ console . error ( ` β Upload process failed: ${ error . message } ` ) ;
267249 process . exit ( 1 ) ;
268250 }
269251}
0 commit comments