Skip to content

Commit 82e88a7

Browse files
Auto-uploading?
1 parent 084f4b0 commit 82e88a7

File tree

3 files changed

+229
-1
lines changed

3 files changed

+229
-1
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ jobs:
6969
if: runner.os == 'Windows'
7070
run: npx yarn dist -- --url ${{ secrets.SITE_URL }} --portable
7171

72+
- name: Upload build artifacts to server
73+
run: node scripts/uploader.js --dir=dist
74+
env:
75+
UPLOAD_URL: ${{ secrets.UPLOAD_URL }}
76+
UPLOAD_API_KEY: ${{ secrets.UPLOAD_API_KEY }}
77+
7278
- name: Upload .exe files
7379
if: runner.os == 'Windows'
7480
uses: actions/upload-artifact@v4

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"dist-64bit": "yarn compile && electron-builder",
1212
"dist-32bit": "yarn compile && electron-builder --ia32",
1313
"dist": "node scripts/build.js",
14-
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
14+
"dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null",
15+
"upload": "node scripts/uploader.js"
1516
},
1617
"dependencies": {
1718
"discord-rpc": "^4.0.1",

scripts/uploader.js

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/**
2+
* Chunked file uploader for Sploder-Launcher builds
3+
* Uploads build artifacts in 90MB chunks with API key authentication
4+
*/
5+
6+
const fs = require('fs');
7+
const path = require('path');
8+
const https = require('https');
9+
const http = require('http');
10+
const { createHash } = require('crypto');
11+
12+
// Configuration
13+
const CHUNK_SIZE = 90 * 1024 * 1024; // 90MB chunks
14+
const UPLOAD_ENDPOINT = process.env.UPLOAD_URL+"/update/upload.php";
15+
const API_KEY = process.env.UPLOAD_API_KEY;
16+
17+
if (!API_KEY) {
18+
console.error('❌ UPLOAD_API_KEY environment variable is required');
19+
process.exit(1);
20+
}
21+
22+
// Parse command line arguments
23+
const args = process.argv.slice(2);
24+
const uploadDirArg = args.find(arg => arg.startsWith('--dir='));
25+
const uploadDir = uploadDirArg ? uploadDirArg.split('=')[1] : './dist';
26+
27+
console.log(`🚀 Starting chunked upload from directory: ${uploadDir}`);
28+
console.log(`📡 Upload endpoint: ${UPLOAD_ENDPOINT}`);
29+
console.log(`📦 Chunk size: ${(CHUNK_SIZE / 1024 / 1024).toFixed(1)}MB`);
30+
31+
/**
32+
* Calculate MD5 hash of a file
33+
*/
34+
function calculateFileHash(filePath) {
35+
return new Promise((resolve, reject) => {
36+
const hash = createHash('md5');
37+
const stream = fs.createReadStream(filePath);
38+
39+
stream.on('data', data => hash.update(data));
40+
stream.on('end', () => resolve(hash.digest('hex')));
41+
stream.on('error', reject);
42+
});
43+
}
44+
45+
/**
46+
* Make HTTP request
47+
*/
48+
function makeRequest(url, options, data) {
49+
return new Promise((resolve, reject) => {
50+
const urlObj = new URL(url);
51+
const isHttps = urlObj.protocol === 'https:';
52+
const client = isHttps ? https : http;
53+
54+
const req = client.request({
55+
hostname: urlObj.hostname,
56+
port: urlObj.port || (isHttps ? 443 : 80),
57+
path: urlObj.pathname + urlObj.search,
58+
method: options.method || 'POST',
59+
headers: options.headers || {}
60+
}, (res) => {
61+
let responseData = '';
62+
res.on('data', chunk => responseData += chunk);
63+
res.on('end', () => {
64+
try {
65+
const parsed = JSON.parse(responseData);
66+
resolve({ status: res.statusCode, data: parsed });
67+
} catch (error) {
68+
resolve({ status: res.statusCode, data: responseData });
69+
}
70+
});
71+
});
72+
73+
req.on('error', reject);
74+
75+
if (data) {
76+
req.write(data);
77+
}
78+
79+
req.end();
80+
});
81+
}
82+
83+
/**
84+
* Upload a single chunk
85+
*/
86+
async function uploadChunk(filePath, fileName, chunkIndex, chunkData, totalChunks, fileHash) {
87+
const formData = new URLSearchParams();
88+
formData.append('api_key', API_KEY);
89+
formData.append('file_name', fileName);
90+
formData.append('chunk_index', chunkIndex.toString());
91+
formData.append('total_chunks', totalChunks.toString());
92+
formData.append('file_hash', fileHash);
93+
formData.append('chunk_data', chunkData.toString('base64'));
94+
95+
const response = await makeRequest(UPLOAD_ENDPOINT, {
96+
method: 'POST',
97+
headers: {
98+
'Content-Type': 'application/x-www-form-urlencoded',
99+
'Content-Length': Buffer.byteLength(formData.toString())
100+
}
101+
}, formData.toString());
102+
103+
if (response.status !== 200 || !response.data.success) {
104+
throw new Error(`Upload failed for chunk ${chunkIndex}: ${JSON.stringify(response.data)}`);
105+
}
106+
107+
return response.data;
108+
}
109+
110+
/**
111+
* Upload a file in chunks
112+
*/
113+
async function uploadFile(filePath) {
114+
const fileName = path.basename(filePath);
115+
const fileSize = fs.statSync(filePath).size;
116+
const totalChunks = Math.ceil(fileSize / CHUNK_SIZE);
117+
118+
console.log(`\n📄 Uploading: ${fileName}`);
119+
console.log(`📊 Size: ${(fileSize / 1024 / 1024).toFixed(2)}MB`);
120+
console.log(`🧩 Chunks: ${totalChunks}`);
121+
122+
// Calculate file hash
123+
console.log('🔍 Calculating file hash...');
124+
const fileHash = await calculateFileHash(filePath);
125+
console.log(`✅ File hash: ${fileHash}`);
126+
127+
// Upload chunks
128+
const fileStream = fs.createReadStream(filePath, { highWaterMark: CHUNK_SIZE });
129+
let chunkIndex = 0;
130+
131+
for await (const chunk of fileStream) {
132+
const progress = ((chunkIndex + 1) / totalChunks * 100).toFixed(1);
133+
console.log(`⬆️ Uploading chunk ${chunkIndex + 1}/${totalChunks} (${progress}%)`);
134+
135+
await uploadChunk(filePath, fileName, chunkIndex, chunk, totalChunks, fileHash);
136+
chunkIndex++;
137+
}
138+
139+
console.log(`✅ Successfully uploaded: ${fileName}`);
140+
}
141+
142+
/**
143+
* Find files to upload based on platform
144+
*/
145+
function findFilesToUpload(directory) {
146+
const files = [];
147+
148+
if (!fs.existsSync(directory)) {
149+
console.error(`❌ Directory not found: ${directory}`);
150+
return files;
151+
}
152+
153+
const items = fs.readdirSync(directory, { withFileTypes: true });
154+
155+
for (const item of items) {
156+
const fullPath = path.join(directory, item.name);
157+
158+
if (item.isFile()) {
159+
// Windows installer files (.exe)
160+
if (item.name.endsWith('.exe') && item.name.includes('Setup')) {
161+
files.push({ path: fullPath, type: 'windows-installer' });
162+
}
163+
// Windows portable files (.zip)
164+
else if (item.name.endsWith('.zip') && item.name.includes('Portable')) {
165+
files.push({ path: fullPath, type: 'windows-portable' });
166+
}
167+
} else if (item.isDirectory()) {
168+
// macOS app files (in mac/ directory)
169+
if (item.name === 'mac') {
170+
// Zip the entire mac directory for upload
171+
const macZipPath = path.join(directory, 'Sploder-macOS.zip');
172+
if (fs.existsSync(macZipPath)) {
173+
files.push({ path: macZipPath, type: 'macos-app' });
174+
} else {
175+
console.log(`📝 Note: macOS app directory found but no zip file. Consider creating ${macZipPath}`);
176+
}
177+
}
178+
}
179+
}
180+
181+
return files;
182+
}
183+
184+
/**
185+
* Main upload process
186+
*/
187+
async function main() {
188+
try {
189+
console.log('🔍 Scanning for files to upload...');
190+
const files = findFilesToUpload(uploadDir);
191+
192+
if (files.length === 0) {
193+
console.log('📭 No files found to upload');
194+
return;
195+
}
196+
197+
console.log(`\n📋 Found ${files.length} files to upload:`);
198+
files.forEach(file => {
199+
console.log(` • ${path.basename(file.path)} (${file.type})`);
200+
});
201+
202+
// Upload each file
203+
for (const file of files) {
204+
try {
205+
await uploadFile(file.path);
206+
} catch (error) {
207+
console.error(`❌ Failed to upload ${file.path}:`, error.message);
208+
process.exit(1);
209+
}
210+
}
211+
212+
console.log('\n🎉 All files uploaded successfully!');
213+
214+
} catch (error) {
215+
console.error('❌ Upload process failed:', error.message);
216+
process.exit(1);
217+
}
218+
}
219+
220+
// Run the uploader
221+
main();

0 commit comments

Comments
 (0)