129 lines
4.5 KiB
TypeScript
129 lines
4.5 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "path";
|
|
import AdmZip from "adm-zip";
|
|
import { Ajv } from "ajv";
|
|
import addFormats from "ajv-formats";
|
|
import log from "electron-log/main";
|
|
|
|
import packageJson from "./../package.json";
|
|
|
|
const isDev = process.env.NODE_ENV === "development";
|
|
const root = path.join(__dirname, "..");
|
|
const sourcePublicPath = path.join(root, "public");
|
|
const runtimePublicPath = isDev ? sourcePublicPath : path.join(root, ".output/public");
|
|
|
|
/**
|
|
* In charge to unzip data from showroom Bo,
|
|
* validate data config and check version
|
|
*/
|
|
export default abstract class UnzipData {
|
|
static zipFileToUse: string|null;
|
|
|
|
private static resolvePublicPath(relativePath: string): string {
|
|
const runtimePath = path.join(runtimePublicPath, relativePath) as fs.PathLike;
|
|
if (fs.existsSync(runtimePath)) {
|
|
return runtimePath as string;
|
|
}
|
|
|
|
return path.join(sourcePublicPath, relativePath);
|
|
}
|
|
|
|
/**
|
|
* Init zipFileToUse if not already inited
|
|
*/
|
|
private static init() {
|
|
if (!UnzipData.zipFileToUse) {
|
|
// * Try to locate last zip file and load it.
|
|
const zipFile = fs.readdirSync("static/data")
|
|
.map((entryPath) => {
|
|
const fullPath = path.join("static/data", entryPath);
|
|
return { path: fullPath, stat: fs.lstatSync(fullPath) };
|
|
})
|
|
.filter((entry) => entry.stat.isFile() && entry.path.endsWith(".zip"))
|
|
.sort((objA, objB) => objB.stat.mtime.getTime() - objA.stat.mtime.getTime())
|
|
.at(0) ?? null;
|
|
|
|
if (zipFile) {
|
|
UnzipData.zipFileToUse = zipFile.path;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decompress zip file content
|
|
* And fetch data.json content
|
|
*/
|
|
public static loadZipFileContent(): void {
|
|
UnzipData.init();
|
|
if (!UnzipData.zipFileToUse) {
|
|
throw new Error("No json data to load");
|
|
}
|
|
|
|
const zip = new AdmZip(fs.readFileSync(UnzipData.zipFileToUse));
|
|
UnzipData.removeOldExtractedFiles();
|
|
zip.extractAllTo("public/data");
|
|
}
|
|
|
|
private static removeOldExtractedFiles() {
|
|
// * Clear export dir
|
|
fs.rmSync("public/data/pictures", { recursive: true, force: true });
|
|
fs.rmSync("public/data/data.json", { force: true });
|
|
}
|
|
|
|
/**
|
|
* Load json data file that was extracted
|
|
*/
|
|
public static loadJsonDataFile(): TypeApp | null {
|
|
const jsonDataPath = UnzipData.resolvePublicPath("data/data.json");
|
|
const jsonSchemaPath = UnzipData.resolvePublicPath("schemas");
|
|
|
|
const jsonData = fs.readFileSync(
|
|
jsonDataPath,
|
|
{ encoding: "utf8", flag: "r" }
|
|
);
|
|
|
|
if (!UnzipData.validateAgainstJsonSchema(jsonSchemaPath, jsonData)) {
|
|
log.warn(`Data for file ${UnzipData.zipFileToUse} is not valid JSON`);
|
|
return null;
|
|
}
|
|
|
|
const appJsonConfig: TypeApp = JSON.parse(jsonData);
|
|
const appVersion: string = packageJson.version;
|
|
|
|
if (appJsonConfig.version !== appVersion) {
|
|
throw new Error(`App version ${appVersion} is not compatible with config version ${appJsonConfig.version}`);
|
|
}
|
|
|
|
return appJsonConfig;
|
|
}
|
|
|
|
private static validateAgainstJsonSchema(jsonSchemaPath:string, jsonData: string): boolean {
|
|
try {
|
|
const schemaList = fs.readdirSync(jsonSchemaPath)
|
|
.filter(schemaPath => schemaPath.endsWith(".schema.json"))
|
|
.map(schemaPath => JSON.parse(fs.readFileSync(`${jsonSchemaPath}/${schemaPath}`, { encoding: "utf8", flag: "r" })) as Record<string,string|any>);
|
|
const appSchemaIndex = schemaList.findIndex((schema) => String(schema.$id).endsWith("app.schema.json"));
|
|
if (appSchemaIndex === -1) {
|
|
throw new Error("app.schema.json not found");
|
|
}
|
|
|
|
const appSchema = schemaList.splice(appSchemaIndex, 1).at(0);
|
|
if (!appSchema) {
|
|
throw new Error("app.schema.json not found");
|
|
}
|
|
|
|
const ajv = new Ajv();
|
|
addFormats(ajv);
|
|
ajv.addMetaSchema(appSchema);
|
|
ajv.addSchema(schemaList);
|
|
|
|
return ajv.validate(appSchema, JSON.parse(jsonData));
|
|
} catch (err:unknown) {
|
|
if (err instanceof Error) {
|
|
log.error(err);
|
|
return false;
|
|
}
|
|
throw new Error("validateAgainstJsonSchema unkown error");
|
|
}
|
|
}
|
|
}
|