app-wizard-cli
Version:
CLI pour gérer votre projet Nest/Angular
109 lines (108 loc) • 4.38 kB
JavaScript
/**
* @module services/fetch-template-service
* Liste et télécharge un template, en passant le header `x-frontend-version`.
*/
import fs from 'fs-extra';
import * as path from 'path';
import os from 'os';
import axios from 'axios';
import AdmZip from 'adm-zip';
import { AuthService } from './auth-service.js';
import { ConfigService } from './config-service.js';
export class FetchTemplateService {
/**
* Liste les templates disponibles (via l’API protégée).
*/
static async listTemplates() {
const { endpoints, version } = await new ConfigService().getConfig();
const token = await new AuthService().getAccessToken();
const resp = await axios.get(`${endpoints.backendUrl}/app-templates`, {
headers: {
Authorization: `Bearer ${token}`,
'x-frontend-version': version.frontend,
},
});
return resp.data;
}
/**
* Télécharge et déploie un template ZIP.
* @param templateId L’ID du template à récupérer.
* @param targetDir Répertoire de destination.
*/
static async fetchTemplate(templateId, targetDir) {
const { endpoints, version } = await new ConfigService().getConfig();
const token = await new AuthService().getAccessToken();
const downloadUrl = `${endpoints.backendUrl}/app-templates/${templateId}/download`;
// 1) Télécharger en stream
const resp = await axios.get(downloadUrl, {
headers: {
Authorization: `Bearer ${token}`,
'x-frontend-version': version.frontend,
},
responseType: 'stream',
validateStatus: () => true,
});
// 2) Debug headers et status
console.log('🛠️ DEBUG Download HTTP status:', resp.status);
console.log('🛠️ DEBUG Content-Type:', resp.headers['content-type']);
console.log('🛠️ DEBUG Content-Length:', resp.headers['content-length']);
// 3) Gérer les erreurs JSON
if (resp.status !== 200) {
const chunks = [];
for await (const chunk of resp.data) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
let errMsg = `Statut ${resp.status}`;
try {
const txt = Buffer.concat(chunks).toString('utf8');
const err = JSON.parse(txt);
errMsg = err.message || txt;
}
catch {
// on garde errMsg brut
}
throw new Error(`Échec du téléchargement du template : ${errMsg}`);
}
// 4) Écriture du ZIP brut dans un fichier temporaire
const tmpZipDir = await fs.mkdtemp(path.join(os.tmpdir(), 'appwizard-'));
const zipFilePath = path.join(tmpZipDir, `${templateId}.zip`);
const writer = fs.createWriteStream(zipFilePath);
await new Promise((resolve, reject) => {
resp.data.pipe(writer);
resp.data.on('error', reject);
writer.on('finish', resolve);
writer.on('error', reject);
});
// 5) Debug signature du fichier ZIP (4 premiers octets)
try {
const fd = await fs.open(zipFilePath, 'r');
const buf = Buffer.alloc(4);
await fs.read(fd, buf, 0, 4, 0);
await fs.close(fd);
console.log('🛠️ DEBUG ZIP signature (hex):', buf.toString('hex'));
// signature valide = '504b0304'
}
catch (e) {
console.log('🛠️ DEBUG impossible de lire la signature ZIP:', e);
}
// 6) Vérification et extraction
let zip;
try {
zip = new AdmZip(zipFilePath);
}
catch (e) {
throw new Error(`Le fichier reçu n'est pas un ZIP valide (${e instanceof Error ? e.message : e})`);
}
const extractDir = await fs.mkdtemp(path.join(os.tmpdir(), 'appwizard-'));
try {
zip.extractAllTo(extractDir, true);
console.log(`📂 Copie vers ${targetDir}…`);
await fs.copy(extractDir, targetDir, { overwrite: true });
console.log('✅ Template déployé.');
}
finally {
await fs.remove(tmpZipDir);
await fs.remove(extractDir);
}
}
}