mirror of
https://github.com/kmitresse/Compo-Service-Log-Project.git
synced 2026-05-13 17:11:49 +00:00
feat: fileService.ts
This commit is contained in:
+17
-1
@@ -1 +1,17 @@
|
||||
console.log('Hello world!')
|
||||
import express from "express";
|
||||
import dotenv from "dotenv";
|
||||
import { createServer } from "node:http";
|
||||
import { logger } from "./middlewares";
|
||||
import routes from "./routes";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
app.use(logger, routes);
|
||||
const server = createServer(app);
|
||||
|
||||
server.listen(process.env.PORT || 8080, () => {
|
||||
console.info(
|
||||
`Server is running on http://localhost:${process.env.PORT || 8080}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default as logger } from "./logger";
|
||||
@@ -0,0 +1,10 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
|
||||
export default function logger(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
console.info(`[${req.method}] ${req.url}`);
|
||||
next();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import fileService from "../../services/fileService";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/data/nudger", (req: Request, res: Response) => {
|
||||
fileService
|
||||
.downloadAndExtract("https://nudger.fr/opendata/gtin-open-data.zip")
|
||||
.then(() => {
|
||||
res.status(200).json({
|
||||
status: "SUCCESS",
|
||||
message: "Data nudger downloaded and extracted",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).json({ status: "ERROR", message: error.message });
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import fileService from "../../services/fileService";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/data/openfoodfacts", (req: Request, res: Response) => {
|
||||
fileService
|
||||
.downloadAndExtract(
|
||||
"https://static.openfoodfacts.org/data/en.openfoodfacts.org.products.csv.gz"
|
||||
)
|
||||
.then(() => {
|
||||
res.status(200).json({
|
||||
status: "SUCCESS",
|
||||
message: "Data openfoodfacts downloaded and extracted",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
res.status(500).json({ status: "ERROR", message: error.message });
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,5 @@
|
||||
import randomizeRouter from "./randomize";
|
||||
import nudgerRouter from "./data/nudger";
|
||||
import openDataRouter from "./data/openfoodfacts";
|
||||
|
||||
export default [randomizeRouter, nudgerRouter, openDataRouter];
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/randomize", (req: Request, res: Response) => {
|
||||
// TODO: Implement randomize route
|
||||
|
||||
// TODO: Parse the DMN file
|
||||
|
||||
// TODO:
|
||||
res.status(200).json({ status: "RANDOMIZED", data: [{}] });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,131 @@
|
||||
import axios from "axios";
|
||||
import * as unzipper from "unzipper";
|
||||
import * as fs from "fs-extra";
|
||||
import * as zlib from "zlib";
|
||||
import { extname, join, basename } from "path";
|
||||
import crypto from "crypto"; // Utilisé pour générer des identifiants uniques basés sur l'URL
|
||||
|
||||
type SupportedFormats = "zip" | "gz" | "gzip";
|
||||
|
||||
class FileService {
|
||||
private cacheDir: string;
|
||||
|
||||
constructor() {
|
||||
this.cacheDir = "./cache";
|
||||
fs.ensureDirSync(this.cacheDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Télécharger et extraire le fichier à partir de l'URL
|
||||
* @param url URL du fichier à télécharger
|
||||
*/
|
||||
async downloadAndExtract(url: string): Promise<void> {
|
||||
try {
|
||||
const fileType = this.getFileExtension(url);
|
||||
if (!fileType) throw new Error("Unsupported file format");
|
||||
if (this.isInCache(url)) return;
|
||||
|
||||
const response = await axios({
|
||||
method: "GET",
|
||||
url,
|
||||
responseType: "stream",
|
||||
});
|
||||
console.log(`Downloading : ${url}`);
|
||||
|
||||
// Décompresser et sauvegarder dans le cache
|
||||
const cacheKey = this.generateCacheKey(url);
|
||||
const cachedPath = join(this.cacheDir, cacheKey);
|
||||
|
||||
fs.ensureDirSync(cachedPath);
|
||||
|
||||
if (fileType === "zip") await this.extractZip(response.data, cachedPath);
|
||||
if (fileType === "gz" || fileType === "gzip")
|
||||
await this.extractGzip(
|
||||
response.data,
|
||||
join(cachedPath, basename(url).replace(/\.(gz|gzip)$/, ""))
|
||||
);
|
||||
|
||||
console.log(`Downloaded and extracted : ${basename(url)}`);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"An error occurred while downloading and extracting the file",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifier si le fichier est déjà en cache
|
||||
* @param url URL du fichier
|
||||
* @private
|
||||
*/
|
||||
private isInCache(url: string): boolean {
|
||||
const cacheKey = this.generateCacheKey(url);
|
||||
const cachedPath = join(this.cacheDir, cacheKey);
|
||||
return fs.pathExistsSync(cachedPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extraire les fichiers ZIP et stocker dans le cache
|
||||
* @param stream
|
||||
* @param cachePath
|
||||
* @private
|
||||
*/
|
||||
private async extractZip(
|
||||
stream: NodeJS.ReadableStream,
|
||||
cachePath: string
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
stream
|
||||
.pipe(unzipper.Extract({ path: cachePath }))
|
||||
.on("close", resolve)
|
||||
.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extraire les fichiers GZ et GZIP et stocker dans le cache
|
||||
* @param stream Flux du fichier téléchargé
|
||||
* @param cachePath Chemin où stocker le fichier décompressé
|
||||
* @private
|
||||
*/
|
||||
private async extractGzip(
|
||||
stream: NodeJS.ReadableStream,
|
||||
cachePath: string
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Ajouter une extension correcte (par exemple, si le fichier original est 'file.gz', le résultat sera 'file')
|
||||
const decompressedFilePath = cachePath.replace(/\.gz$/, "");
|
||||
|
||||
const writeStream = fs.createWriteStream(decompressedFilePath);
|
||||
|
||||
// Pipeliner le flux du téléchargement et la décompression
|
||||
stream
|
||||
.pipe(zlib.createGunzip()) // Décompresser le flux
|
||||
.pipe(writeStream) // Écrire le fichier décompressé
|
||||
.on("finish", resolve)
|
||||
.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtenir l'extension du fichier à partir de l'URL
|
||||
* @param url URL du fichier
|
||||
*/
|
||||
private getFileExtension(url: string): SupportedFormats | null {
|
||||
const extension = extname(url).toLowerCase();
|
||||
if (extension === ".zip") return "zip";
|
||||
if (extension === ".gz" || extension === ".gzip") return "gz";
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Générer un identifiant unique pour le fichier basé sur l'URL
|
||||
* @param url URL du fichier
|
||||
*/
|
||||
private generateCacheKey(url: string): string {
|
||||
return crypto.createHash("md5").update(url).digest("hex");
|
||||
}
|
||||
}
|
||||
|
||||
export default new FileService();
|
||||
Reference in New Issue
Block a user