Update v1.1.0-beta.1
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2024-12-21 18:16:25 +01:00
parent f7658eca22
commit 51d11a6c36
20 changed files with 2688 additions and 935 deletions

131
services/BaseService.js Normal file
View File

@@ -0,0 +1,131 @@
const { logger } = require('../config/logs');
const fs = require('fs').promises;
const path = require('path');
class BaseService {
constructor(name, defaultConfig = {}) {
this.name = name;
this.defaultConfig = defaultConfig;
this.config = {};
this.isRunning = false;
this.status = 'stopped';
this.logger = logger;
this.setupPath = path.join(process.cwd(), 'data', 'setup.json');
}
async loadConfig() {
try {
const setupContent = await fs.readFile(this.setupPath, 'utf8');
const setup = JSON.parse(setupContent);
// Retrieve the service configuration from setup.json
const serviceConfig = setup[0]?.services?.[this.name] || {};
// Merge with the default configuration
this.config = {
...this.defaultConfig,
...serviceConfig
};
return this.config;
} catch (error) {
this.logger.error(`Error loading configuration for ${this.name}:`, error);
this.config = this.defaultConfig;
return this.config;
}
}
async start() {
if (this.isRunning) {
this.logger.warn(`Service ${this.name} is already running`);
return;
}
try {
// Load the configuration before starting
await this.loadConfig();
// Check if the service is enabled
if (this.config.enabled !== 'on') {
this.logger.info(`Service ${this.name} is not enabled in the configuration`);
return;
}
await this._startImplementation();
this.isRunning = true;
this.status = 'running';
this.logger.info(`Service ${this.name} started successfully`);
} catch (error) {
this.logger.error(`Error starting service ${this.name}:`, error);
this.status = 'error';
throw error;
}
}
async stop() {
if (!this.isRunning) {
this.logger.warn(`Service ${this.name} is not running`);
return;
}
try {
await this._stopImplementation();
this.isRunning = false;
this.status = 'stopped';
this.logger.info(`Service ${this.name} stopped successfully`);
} catch (error) {
this.logger.error(`Error stopping service ${this.name}:`, error);
this.status = 'error';
throw error;
}
}
getStatus() {
return {
name: this.name,
status: this.status,
isRunning: this.isRunning,
config: this.config
};
}
async updateConfig(newConfig) {
// Load the current configuration from setup.json
let setup;
try {
const setupContent = await fs.readFile(this.setupPath, 'utf8');
setup = JSON.parse(setupContent);
} catch (error) {
throw new Error(`Error reading setup.json: ${error.message}`);
}
// Update the service configuration
if (!setup[0].services) {
setup[0].services = {};
}
setup[0].services[this.name] = {
...this.config,
...newConfig
};
// Save to setup.json
try {
await fs.writeFile(this.setupPath, JSON.stringify(setup, null, 2));
this.config = setup[0].services[this.name];
this.logger.info(`Configuration for service ${this.name} updated:`, this.config);
} catch (error) {
throw new Error(`Error saving configuration: ${error.message}`);
}
}
// These methods must be implemented by subclasses
async _startImplementation() {
throw new Error('_startImplementation must be implemented by the subclass');
}
async _stopImplementation() {
throw new Error('_stopImplementation must be implemented by the subclass');
}
}
module.exports = BaseService;

View File

@@ -0,0 +1,141 @@
const BaseService = require('./BaseService');
const cron = require('node-cron');
const fs = require('fs').promises;
const path = require('path');
class FileCleanupService extends BaseService {
constructor(config = {}) {
super('fileCleanup', {
fileInfoPath: config.fileInfoPath || path.join(process.cwd(), 'data', 'file_info.json'),
cronSchedule: config.cronSchedule || '0 * * * *',
...config
});
this.job = null;
}
async _startImplementation() {
if (this.job) {
return;
}
// Valider le cron schedule
if (!cron.validate(this.config.cronSchedule)) {
throw new Error(`Schedule cron invalide: ${this.config.cronSchedule}`);
}
this.job = cron.schedule(this.config.cronSchedule, () => this.cleanup());
}
async _stopImplementation() {
if (this.job) {
this.job.stop();
this.job = null;
}
}
// Réutiliser vos méthodes existantes
async fileExists(filepath) {
try {
await fs.access(filepath);
return true;
} catch {
return false;
}
}
normalizePath(filepath) {
return path.normalize(filepath).replace(/\\/g, '/');
}
async readFileInfo() {
try {
const data = await fs.readFile(this.config.fileInfoPath, 'utf8');
return JSON.parse(data);
} catch (err) {
this.logger.error('Erreur lors de la lecture du fichier info:', err);
return [];
}
}
async saveFileInfo(fileInfo) {
try {
await fs.writeFile(
this.config.fileInfoPath,
JSON.stringify(fileInfo, null, 2),
'utf8'
);
return true;
} catch (err) {
this.logger.error('Erreur lors de la sauvegarde du fichier info:', err);
return false;
}
}
async processFile(file, now) {
const normalizedPath = this.normalizePath(file.path);
if (file.expiryDate && new Date(file.expiryDate) <= now) {
return { status: 'expired', file: { ...file, path: normalizedPath } };
}
if (!(await this.fileExists(normalizedPath))) {
return { status: 'missing', file: { ...file, path: normalizedPath } };
}
return { status: 'valid', file };
}
async cleanup() {
try {
const fileInfo = await this.readFileInfo();
const now = new Date();
const results = {
expired: [],
missing: [],
processed: 0,
remaining: 0
};
const validFiles = [];
await Promise.all(fileInfo.map(async (file) => {
const { status, file: processedFile } = await this.processFile(file, now);
if (status === 'expired') {
results.expired.push(processedFile);
results.processed++;
} else if (status === 'missing') {
results.missing.push(processedFile);
results.processed++;
} else {
validFiles.push(file);
}
}));
if (results.processed > 0) {
results.remaining = validFiles.length;
await this.saveFileInfo(validFiles);
this.logger.info('Nettoyage des fichiers terminé', {
expired: results.expired.length,
missing: results.missing.length,
totalProcessed: results.processed,
remainingFiles: results.remaining
});
}
return results;
} catch (err) {
this.logger.error('Erreur lors du nettoyage des fichiers:', {
error: err.message,
stack: err.stack
});
throw err;
}
}
async forceCleanup() {
return this.cleanup();
}
}
module.exports = new FileCleanupService();

181
services/reportService.js Normal file
View File

@@ -0,0 +1,181 @@
const BaseService = require('./BaseService');
const cron = require('node-cron');
const os = require('os');
const fs = require('fs');
const path = require('path');
const ip = require('ip');
const si = require('systeminformation');
const fetch = require('node-fetch');
const packageJson = require('../package.json');
class ReportManagerService extends BaseService {
constructor() {
// Configuration par défaut
const defaultConfig = {
enabled: 'off',
endpoint: 'https://cdn-apollon-p198-61m1.dinawo.fr/api/report/receive',
cronSchedule: '0 0 * * *' // Par défaut, une fois par jour à minuit
};
super('reportManager', defaultConfig);
this.job = null;
}
async _startImplementation() {
if (this.job) {
return;
}
// Valider le cron schedule
const schedule = this.config.cronSchedule || this.defaultConfig.cronSchedule;
if (!cron.validate(schedule)) {
throw new Error(`Schedule cron invalide: ${schedule}`);
}
this.job = cron.schedule(schedule, () => this.generateAndSendReport());
this.logger.info(`Service de rapport programmé avec le planning: ${schedule}`);
}
async _stopImplementation() {
if (this.job) {
this.job.stop();
this.job = null;
}
}
formatUptime(uptime) {
const days = Math.floor(uptime / (24 * 60 * 60));
uptime %= (24 * 60 * 60);
const hours = Math.floor(uptime / (60 * 60));
uptime %= (60 * 60);
const minutes = Math.floor(uptime / 60);
return `${days}d ${hours}h ${minutes}m`;
}
async getInternalErrors() {
const date = new Date();
date.setDate(date.getDate() - 1);
const previousDate = date.toISOString().split('T')[0];
const logFile = path.join(__dirname, '..', 'logs', `log-${previousDate}.log`);
try {
const logs = fs.readFileSync(logFile, 'utf-8');
return logs.split('\n').filter(line => /\[38;5;9mInternal-Error/.test(line));
} catch (err) {
this.logger.error('Erreur lors de la lecture des logs:', err);
return [];
}
}
async generateReport() {
try {
const internalErrors = await this.getInternalErrors();
if (internalErrors.length === 0) {
this.logger.info('Pas d\'erreurs internes dans les logs d\'hier. Pas de rapport généré.');
return null;
}
const loadavg = os.loadavg().map(load => (load / os.cpus().length) * 100);
const osInfo = {
type: os.type(),
platform: os.platform(),
arch: os.arch(),
release: os.release(),
uptime: this.formatUptime(os.uptime()),
loadavg: loadavg
};
const networkInterfaces = os.networkInterfaces();
const diskUsage = await si.fsSize();
const cpuTemperature = await si.cpuTemperature();
const userInfo = os.userInfo();
const systemInfo = {
memoryInfo: ((os.totalmem() - os.freemem()) / os.totalmem() * 100).toFixed(2),
cpuInfo: (os.cpus().length / os.cpus().length * 100).toFixed(2),
diskInfo: diskUsage.map(disk => ({
fs: disk.fs,
type: disk.type,
size: disk.size,
used: disk.used,
available: disk.available,
use: disk.use
})),
ipAddress: ip.address(),
cdnVersion: packageJson.version,
osInfo: osInfo,
userInfo: {
username: userInfo.username,
homedir: userInfo.homedir,
shell: userInfo.shell
},
errors: internalErrors,
networkInterfaces: networkInterfaces,
serverUptime: os.uptime(),
systemLoad: loadavg,
cpuTemperature: cpuTemperature
};
const reportDate = new Date();
reportDate.setDate(reportDate.getDate() - 1);
const reportFileName = `report_${reportDate.toISOString().split('T')[0]}_${ip.address()}.json`;
// Sauvegarder le rapport localement
const reportDir = path.join(__dirname, '..', 'report');
if (!fs.existsSync(reportDir)) {
fs.mkdirSync(reportDir, { recursive: true });
}
fs.writeFileSync(
path.join(reportDir, reportFileName),
JSON.stringify(systemInfo, null, 2)
);
return systemInfo;
} catch (error) {
this.logger.error('Erreur lors de la génération du rapport:', error);
throw error;
}
}
async generateAndSendReport() {
try {
const report = await this.generateReport();
if (!report) {
return;
}
this.logger.info("Envoi du rapport...");
const response = await fetch(this.config.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(report)
});
if (!response.ok) {
throw new Error(`Erreur HTTP! statut: ${response.status}`);
}
const responseData = await response.json();
this.logger.info('Rapport envoyé avec succès. Réponse:', responseData);
return responseData;
} catch (error) {
this.logger.error('Échec de l\'envoi du rapport:', error);
throw error;
}
}
// Méthode pour forcer l'envoi d'un rapport immédiatement
async forceSendReport() {
return this.generateAndSendReport();
}
}
module.exports = new ReportManagerService();