167 lines
5.2 KiB
JavaScript
167 lines
5.2 KiB
JavaScript
/**
|
|
* prepareHubResources.js
|
|
*
|
|
* Downloads the AionHub index.json and all extension zip packages
|
|
* into resources/hub/ so they are bundled with the app as local fallback.
|
|
*
|
|
* Called during the build pipeline before electron-builder runs.
|
|
*
|
|
* Environment variables:
|
|
* AIONUI_HUB_TAG - Git tag to fetch from (default: 'dist-latest')
|
|
* AIONUI_HUB_SKIP - Set to '1' to skip hub resource preparation
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const https = require('https');
|
|
|
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
const HUB_DIR = path.join(PROJECT_ROOT, 'resources', 'hub');
|
|
|
|
const DEFAULT_TAG = 'dist-latest';
|
|
const BASE_URLS = [
|
|
`https://raw.githubusercontent.com/iOfficeAI/AionHub/${process.env.AIONUI_HUB_TAG || DEFAULT_TAG}/`,
|
|
`https://cdn.jsdelivr.net/gh/iOfficeAI/AionHub@${process.env.AIONUI_HUB_TAG || DEFAULT_TAG}/`,
|
|
];
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function ensureDir(dir) {
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Download a URL to a local file path. Tries each base URL in order.
|
|
* Returns the base URL that succeeded.
|
|
*/
|
|
async function downloadFile(relativePath, destPath) {
|
|
for (const base of BASE_URLS) {
|
|
const url = new URL(relativePath, base).toString();
|
|
try {
|
|
await downloadUrl(url, destPath);
|
|
return url;
|
|
} catch (error) {
|
|
console.warn(` [hub] Failed from ${url}: ${error.message}`);
|
|
}
|
|
}
|
|
throw new Error(`Failed to download ${relativePath} from all mirrors`);
|
|
}
|
|
|
|
function downloadUrl(url, destPath) {
|
|
return new Promise((resolve, reject) => {
|
|
const follow = (url, redirectCount = 0) => {
|
|
if (redirectCount > 5) {
|
|
reject(new Error('Too many redirects'));
|
|
return;
|
|
}
|
|
|
|
const get = url.startsWith('https') ? https.get : require('http').get;
|
|
get(url, (res) => {
|
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
follow(res.headers.location, redirectCount + 1);
|
|
return;
|
|
}
|
|
|
|
if (res.statusCode !== 200) {
|
|
res.resume();
|
|
reject(new Error(`HTTP ${res.statusCode}`));
|
|
return;
|
|
}
|
|
|
|
const file = fs.createWriteStream(destPath);
|
|
res.pipe(file);
|
|
file.on('finish', () => {
|
|
file.close();
|
|
resolve();
|
|
});
|
|
file.on('error', (err) => {
|
|
fs.unlinkSync(destPath);
|
|
reject(err);
|
|
});
|
|
}).on('error', reject);
|
|
};
|
|
|
|
follow(url);
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main
|
|
// ---------------------------------------------------------------------------
|
|
|
|
async function prepareHubResources() {
|
|
if (process.env.AIONUI_HUB_SKIP === '1') {
|
|
console.log('[hub] Skipping hub resource preparation (AIONUI_HUB_SKIP=1)');
|
|
return { skipped: true };
|
|
}
|
|
|
|
const tag = process.env.AIONUI_HUB_TAG || DEFAULT_TAG;
|
|
console.log(`[hub] Preparing hub resources from tag: ${tag}`);
|
|
|
|
// Clean and create target directory
|
|
if (fs.existsSync(HUB_DIR)) {
|
|
fs.rmSync(HUB_DIR, { recursive: true, force: true });
|
|
}
|
|
ensureDir(HUB_DIR);
|
|
|
|
// Step 1: Download index.json
|
|
const indexPath = path.join(HUB_DIR, 'index.json');
|
|
console.log('[hub] Downloading index.json...');
|
|
const indexUrl = await downloadFile('index.json', indexPath);
|
|
console.log(`[hub] index.json downloaded from ${indexUrl}`);
|
|
|
|
// Step 2: Parse index and download all extension zips
|
|
const index = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
const extensions = index.extensions || {};
|
|
const names = Object.keys(extensions);
|
|
|
|
console.log(`[hub] Found ${names.length} extensions to bundle`);
|
|
|
|
const results = [];
|
|
for (const name of names) {
|
|
const ext = extensions[name];
|
|
const tarball = ext.dist?.tarball;
|
|
if (!tarball) {
|
|
console.warn(`[hub] Skipping ${name}: no dist.tarball`);
|
|
continue;
|
|
}
|
|
|
|
const zipPath = path.join(HUB_DIR, path.basename(tarball));
|
|
try {
|
|
const url = await downloadFile(tarball, zipPath);
|
|
const size = fs.statSync(zipPath).size;
|
|
console.log(`[hub] ${name} -> ${path.basename(tarball)} (${(size / 1024).toFixed(1)} KB)`);
|
|
results.push({ name, file: path.basename(tarball), size, url });
|
|
} catch (error) {
|
|
console.error(`[hub] Failed to download ${name}: ${error.message}`);
|
|
// Non-fatal: continue with other extensions
|
|
}
|
|
}
|
|
|
|
// Step 3: Write manifest for debugging/verification
|
|
const manifest = {
|
|
tag,
|
|
generatedAt: new Date().toISOString(),
|
|
indexUrl,
|
|
extensions: results,
|
|
};
|
|
fs.writeFileSync(path.join(HUB_DIR, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
|
|
console.log(`[hub] Done: ${results.length}/${names.length} extensions bundled in resources/hub/`);
|
|
return { skipped: false, count: results.length, total: names.length };
|
|
}
|
|
|
|
// Support both direct execution and require() from build-with-builder.js
|
|
if (require.main === module) {
|
|
prepareHubResources().catch((err) => {
|
|
console.error('[hub] Fatal error:', err);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { prepareHubResources };
|