cahier-de-bridge
Version:
Gestion d'un éditeur de mains de bridge
843 lines (764 loc) • 27.7 kB
JavaScript
// bridge.js version 1.0.2
/**************
/ NOTES
**************
Pour lancer le programme:
node bridge
Persistance réglages: https://socket.io/how-to/use-with-express-session
Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force
*/
//**************
// Declarations
//**************
;
const express = require("express");
const { createServer } = require("http");
const { join } = require("path");
const { Server } = require("socket.io");
const session = require("express-session");
const app = express();
const httpServer = createServer(app);
const nodemailer = require("nodemailer");
//const SendmailTransport = require("nodemailer/lib/sendmail-transport");
const md5 = require("md5");
const sessionMiddleware = session({
secret: "WqiZvuvVsIV1zmzJQeYUgINqXYe",
resave: true,
saveUninitialized: true,
});
app.use(sessionMiddleware);
const io = new Server(httpServer);
io.engine.use(sessionMiddleware);
const fs = require("fs");
const sqlite3 = require("sqlite3");
const child_process = require("child_process");
const favicon = require("serve-favicon"); // icone page web
const multer = require("multer"); // upload middleware
const { exit } = require("process");
//******************
// Initialisations
//******************
//initialisation du serveur web, des chemins locaux et du socket
app.set("env", process.env.ENV || "development");
// utiliser des répertoires statiques chainés (si page manquante, passe au rép suivant..)
app.use("/public", express.static("./")); // 'public' est en réalité ./
app.use("/css", express.static("./css/"));
app.use("/images", express.static("./images/"));
app.use("/node", express.static("./node_modules/"));
app.use("/js", express.static("./js"));
// exécuter favicon(path) à chaque appel d'une fonction de app
app.use(favicon("./images/favicon.ico"));
//initialisation du serveur web, des chemins locaux et du socket. Indispensable, même si ejs n'est pas utilisé
app.set("view engine", "ejs");
// un moteur de rendu est nécessaire. Mon choix: ejs..
app.engine(".html", require("ejs").__express);
app.use(
express.urlencoded({
extended: true,
})
);
app.use(express.json());
var app_config = {};
var transport;
// récupérer la configuration
const config_filename = __dirname + "/config.json";
if (!fs.existsSync(config_filename)) fs.copyFileSync(__dirname + "/_config.json", config_filename);
if (fs.existsSync(config_filename)) {
//file exists
//onsole.log('file ' + config_filename + ' exists');
try {
let s = fs.readFileSync(config_filename);
if (s != "{}") {
app_config = JSON.parse(s);
if (app_config.mail != undefined)
try {
transport = nodemailer.createTransport(app_config.mail);
console.log("Envoi Email via " + app_config.mail.host);
} catch (e) {
console.error("Erreur transport mail", e);
}
else console.error("Pas de configuration mail");
}
} catch (e) {
console.error("Parsing error 130", e);
exit(-1);
}
} else console.error("NTBS:" + config_filename + " introuvable. A créer");
//*******************************
// Initialisation de la session
//*******************************
var nbr_clients = 0;
//*********************************
// Initialiser quelques variables !
//*********************************
// récupérer date de package.json
//console.log(__dirname);
try {
let stats = fs.statSync("package.json");
let rawdata = JSON.parse(fs.readFileSync("package.json"));
console.log(rawdata.description + " version " + rawdata.version + " du " + stats.mtime.toLocaleDateString("fr-FR"));
} catch (error) {
throw error.message;
}
// récupérer le séparateur dossier (win ou linux)
let dir_sep = "";
for (let index = __dirname.length - 1; index >= 0 && dir_sep == ""; index--) {
const char = __dirname[index];
if (char == "/" || char == "\\") dir_sep = char;
}
if (dir_sep == "") {
console.error("Séparateur de dossier pas trouvé...");
dir_sep == "/";
}
let db_dir = __dirname + dir_sep + "db" + dir_sep;
try {
fs.statSync(db_dir);
} catch (e) {
if (e.code == "ENOENT") fs.mkdirSync(db_dir);
}
const dir_upload = __dirname + dir_sep + "upload";
if (!fs.existsSync(dir_upload)) {
console.log("Créer le dossier " + dir_upload);
fs.mkdirSync(dir_upload);
}
// base des users
var db_login;
const db_def_filename = "bridge.db";
var db_list = [];
openBase("", "login")
.then((db) => {
db_login = db;
// s'assurer qu'il existe bien une base par défaut
openBase(db_dir, "bridge").then((db1) => {
db1.close();
fs.readdir(db_dir, function (err, files) {
//handling error
if (!err) {
for (let f of files) {
if ((f.endsWith(".db") || f.endsWith(".bkp")) && IsBridge(f)) db_list.push(f);
}
console.log(db_list);
}
});
});
})
.catch((e) => console.error(e));
//*******************
// Socket.io
//*******************
// Chaque fois qu'une page web est affichée, 'connection' ouvre un nouveau socket.
//
const SESSION_RELOAD_INTERVAL = 10 * 60 * 1000;
function SetUser(row) {
let user = {
id: row.id,
nom: row.nom,
is_admin: Boolean(row.admin),
};
try {
user.choix = JSON.parse(row.choix);
} catch (e) {
console.error(e);
user.choix = {};
}
return user;
}
async function GetUser(nom) {
let foo = await db_get(db_login, "SELECT * FROM users WHERE nom=?", [nom]);
if (foo == undefined) foo = await db_get(db_login, "SELECT * FROM users ORDER BY id LIMIT 1");
return SetUser(foo);
}
io.on("connection", async (socket) => {
const files_to_remove = [];
const session = socket.request.session;
// timeout si inactif...
const timer = setInterval(() => {
socket.request.session.reload((err) => {
if (err) {
// forces the client to reconnect
socket.emit("alert", "Echec reload");
socket.conn.close();
}
});
}, SESSION_RELOAD_INTERVAL);
// récupérer user
if (session.user == undefined) session.user = await GetUser(app_config.user);
let db_name = session.user.choix.db || db_def_filename; // nom de la base ouverte par socket.io
let db = new sqlite3.Database(db_dir + db_name);
// passons aux choses sérieuses
nbr_clients++;
const who = session.user.nom + " est connecté à " + db_name;
console.log(who);
// et maintenant, on attend le ping-pong
socket.on("session", (cb) => {
session.need_login = app_config.need_login;
cb(session);
socket.emit("info", who);
socket.emit("db_list", db_list);
});
socket.on("disconnect", async function () {
clearInterval(timer);
try {
if (session.dirty == true) {
//console.log("Choix change:", session.user.choix);
const info = await db_run(db_login, "UPDATE users SET choix=? WHERE id=" + session.user.id, [JSON.stringify(session.user.choix)]);
session.dirty = false;
}
} catch (err) {
console.error(err.message);
}
nbr_clients--;
let st = session.user.nom + " déconnecté. ";
if (nbr_clients > 1) st += "Encore " + nbr_clients + " connectés";
else if (nbr_clients == 1) st += "Reste un seul client connecté";
else st += "Plus personne n'est connecté";
console.log(st);
if (db != undefined) {
// if (db.inTransaction) db_all(db,"ROLLBACK").run();
db.close();
db = undefined;
}
/* Effacer les fichiers uploadés */
for (let path of files_to_remove) if (fs.existsSync(path)) fs.unlinkSync(path);
});
socket.onAny((event, p1, p2, p3, p4, p5) => {
/*
if (p5 != undefined) console.log("IO WEB->", event, p1, p2, p3, p4, p5);
else if (p4 != undefined) console.log("IO WEB->", event, p1, p2, p3, p4);
else if (p3 != undefined) console.log("IO WEB->", event, p1, p2, p3.toString().substring(0, 20));
else if (p2 != undefined) console.log("IO WEB->", event, p1, p2);
else if (p1 != undefined) console.log("IO WEB->", event, p1);
else console.log("IO WEB->", event);
*/
});
/*****************/
/* AVEC CALLBACK */
/*****************/
async function AddTreeNode(node, id_parent) {
if (node.jeux == undefined) node.jeux = await db_all(db, "SELECT d.id,d.nom FROM data2tree t LEFT JOIN donnes d ON d.id==t.id_donne WHERE t.id_arbre" + id_parent);
node.childs = await db_all(db, "SELECT id,itm,pos FROM arbre WHERE id_parent" + id_parent + " ORDER BY pos");
for (let el of node.childs) {
await AddTreeNode(el, "=" + el.id);
}
return node;
}
socket.on("get_tree", async (cb) => {
if (db == undefined) cb({ err: "Session expirée. Identifiez-vous" });
else
try {
let node = {};
node.jeux = await db_all(db, "SELECT id,nom FROM donnes WHERE id NOT IN(SELECT id_donne FROM data2tree)");
await AddTreeNode(node, " IS NULL");
cb(node);
} catch (e) {
cb({ err: e.message });
}
});
socket.on("cb_all", async (query, values, cb) => {
if (db == undefined) cb({ err: "Session expirée. Identifiez-vous" });
else
db.all(query, values || [], function (err, rows) {
cb(err ? { err: err.message } : rows);
});
});
socket.on("cb_get", async (query, values, cb) => {
if (db == undefined) cb({ err: "Session expirée. Identifiez-vous" });
else
db.get(query, values || [], function (err, rows) {
//if (err) console.error(err.message, query, values);
cb(err ? { err: err.message } : rows);
});
});
socket.on("cb_run", async (query, values, cb) => {
if (db == undefined) cb({ err: "Session expirée. Identifiez-vous" });
else
db.run(query, values || [], function (err, info) {
cb(err ? { err: err.message } : info);
});
});
/* socket.on("chg_mp", function (id, old_val, new_val, cb) {
if (db == undefined) cb({ err: "Session fermée. Reconnectez vous" });
else
try {
let info = {};
let enr = db_login.prepare("SELECT id,hash,LENGTH(hash) AS pwl FROM users WHERE id=?").get(id);
if (enr == undefined) throw new Error("Erreur dans les identifiants");
else if ((old_val != "" || (enr.hash != undefined && enr.pwl != 0)) && bcrypt.compareSync(old_val, enr.hash) == false) throw new Error("Ancien mot de passe incorrect");
else if (new_val != "")
bcrypt.hash(new_val, 10, function (err, hash) {
if (err) throw new Error("Hash:" + err.message);
else info = db_login.prepare("UPDATE users SET hash=? WHERE id=" + enr.id).run(hash);
if (info.changes == 1) cb("Mot de passe modifié");
else throw new Error("Mot de passe inchangé..");
});
else {
info = db_login.prepare("UPDATE users SET hash=null WHERE id=" + enr.id).run();
if (info.changes == 1) cb("Mot de passe supprimé");
else throw new Error("Mot de passe inchangé..");
}
} catch (err) {
console.error(err.message);
cb({ err: err.message });
}
});*/
socket.on("upducfg", (nom, valeur) => {
let b = false;
if (typeof valeur == "object") b = JSON.stringify(session.user.choix[nom]) === JSON.stringify(valeur);
else b = session.user.choix[nom] == valeur;
if (!b) {
session.user.choix[nom] = valeur; // ajout dynamique.
session.dirty = true;
session.save();
}
});
socket.on("liste_donnes", async (cb) => {
if (db == undefined) cb({ err: "Session expirée. Identifiez-vous" });
else
try {
cb(await db_all(db, "SELECT id,nom FROM donnes ORDER BY nom"));
} catch (e) {
cb({ err: e.message });
}
});
socket.on("save_donne", async (enr, cb) => {
if (db == undefined) cb({ err: "Session fermée. Reconnectez-vous" });
else
try {
const contenu = JSON.stringify(enr.jeu);
if (enr.id == undefined) cb(await db_run(db, "INSERT INTO donnes (nom,data) VALUES (?,?)", [enr.nom, contenu]));
else cb(await db_run(db, "UPDATE donnes set nom=?,data=? WHERE id=?"), [enr.nom, contenu, enr.id]);
} catch (err) {
cb({ err: err.message });
}
});
socket.on("export", async (ar, cb) => {
if (db == undefined) cb({ err: "Session fermée. Reconnectez-vous" });
else
try {
let st = "";
let nom = "";
for (let id of ar) {
const row = await db_get(db, "SELECT * FROM donnes WHERE id=" + id);
if (nom != "") nom += ",";
nom += row.nom;
st += "INSERT INTO donnes (nom,data) VALUES ('" + row.nom + "', '" + row.data.replace(/'/g, "''").replace('", "txt1":', '",\n"txt1":').replace('", "txt2":', '",\n"txt2":').replace('", "donne":', '",\n"donne":').replace('], "enchere":', '],\n"enchere":') + "');\n";
}
nom += ".sql";
const fn = dir_upload + dir_sep + nom;
fs.writeFileSync(fn, st);
files_to_remove.push(fn);
cb({ fn: "/public/upload/" + nom });
} catch (e) {
cb({ err: err.message });
}
});
socket.on("import", async (fn, cb) => {
if (db == undefined) cb("Session fermée. Reconnectez-vous");
else {
files_to_remove.push(fn);
cb(await MakeSQLfile(db, fn));
}
});
// Database#backup(filename, [callback])
// Database#backup(filename, destName, sourceName, filenameIsDest, [callback])
socket.on("bkp", (cb) => {
if (db == undefined) cb({ err: "Session fermée. Reconnectez-vous" });
else
try {
const d = new Date();
session.bkp = "bridge " + d.toISOString().substring(0, 10) + ".bkp";
let backup = db.backup(db_dir + session.bkp, function (err) {
if (err) throw err;
backup.step(-1, function (err) {
if (err) throw err;
backup.finish(function (err) {
if (err) throw err;
cb("/public/db/" + session.bkp);
});
});
});
} catch (e) {
cb({ err: e.message });
}
});
socket.on("restore", async (file, cb) => {
if (db == undefined) cb({ err: "Session fermée. Reconnectez-vous" });
else
try {
// copier le fichier dans le répertoire de BDD
const idx = file.lastIndexOf(dir_sep);
const nom = file.substring(idx + 1);
openBase(db_dir, nom)
.then((db1) => {
db1.close();
const dest = db_dir + nom;
if (dest != file) {
console.log("Copier " + file + " dans " + dest);
fs.copyFileSync(file, dest);
files_to_remove.push(file);
}
if (db_list.indexOf(nom) == -1) db_list.push(nom);
cb("OK"); // le reste par appel à open_db
})
.catch((e) => cb(e.message));
} catch (e) {
console.error(e.message);
cb(e);
}
});
socket.on("open_db", async (nom, cb) => {
try {
if (db == undefined) throw new Error("Session fermée. Reconnectez-vous");
if (db_list.indexOf(nom) == -1) throw new Error("Base inconnue !");
cb("OK");
if (nom != db_name) {
session.user.choix.db = nom;
const info = await db_all(db_login, "UPDATE users SET choix=? WHERE id=" + session.user.id, [JSON.stringify(session.user.choix)]);
session.dirty = true;
session.save();
socket.emit("reload");
}
} catch (e) {
console.error(e);
cb(e);
}
});
/********************/
/* Contrôle d'accès */
/********************/
socket.on("connect_me", (nom, pw, cb) => {
db_login.get("SELECT * FROM users WHERE nom LIKE ? OR email LIKE ?", [nom, nom], (err, row) => {
if (err) cb(err.message);
else if (PasswordOK(pw, row.hash)) {
session.user = SetUser(row);
session.save();
cb("OK");
} else cb("Mot de passe incorrect");
});
});
socket.on("forgot", (nom, cb) => {
if (db_login == undefined) cb("NTBS: Liste des utilisateurs inaccessible");
else
db_login.get("SELECT id,nom,email FROM users WHERE nom LIKE ? OR email LIKE ?", [nom, nom], (err, row) => {
if (err) cb({ err: err.message, contact: app_config.email_to });
else if (row == undefined) cb({ err: "Utilisateur introuvable" });
else if (transport == undefined) cb({ err: "Envoi d'email pas configuré sur ce site", contact: app_config.email_to });
else {
// envoi d'un mail pour réinitialiser le mot de passe
if (row.email == undefined) cb({ err: "Email de '" + nom + "' manquant", contact: app_config.email_to });
else {
const hash = md5(row.id + row.nom + Date.now());
db_login.run("UPDATE users SET reset_hash=? WHERE id=?", [hash, row.id], (err) => {
if (err) cb({ err: err.message, contact: app_config.email_to });
else {
// le lien expire après 5 mn
setTimeout(() => {
db_login.run("UPDATE users SET reset_hash=null WHERE reset_hash=?", [hash], (err) => {
if (err) console.error(err.message);
});
}, 300000);
const raz_url = app_config.http_url + "/reset?p1=" + hash;
const message = {
from: app_config.mail.auth.user, // Sender address
to: row.email || app_config.email_to, // List of recipients
subject: "Demande de réinitialisation de mot de passe pour " + row.nom, // Subject line
html: 'Cliquez sur le lien pour réinitialiser votre mot de passe: <a href="' + raz_url + '">Nouveau mot de passe</a>',
text: "Pour réinitialiser votre mot de passe, cliquez ou copier dans votre navigateur l'adresse suivante: " + raz_url,
};
transport.sendMail(message, function (err, info) {
if (err) cb({ err: err.message, contact: app_config.email_to });
else cb("Un Email contenant le lien pour réinitialiser votre mot de passe a été envoyé à " + row.email);
});
}
});
}
}
});
});
socket.on("get_hash", (hash, cb) => {
if (db_login == undefined) cb({ err: "NTBS: Liste des utilisateurs inaccessible" });
else
db_login.get("SELECT nom,id FROM users WHERE reset_hash=?", [hash], (err, row) => {
if (err) cb({ err: err.message });
else if (row == undefined) cb({ err: "Lien de réinitialisation expiré" });
else cb(row);
});
});
socket.on("set_hash", (reset_hash, pw, cb) => {
if (db_login == undefined) cb({ err: "NTBS: Liste des utilisateurs inaccessible" });
else {
db_login.run("UPDATE users SET hash=?,reset_hash=NULL WHERE reset_hash=?", [pw == "" ? "NULL" : md5(pw), reset_hash], function (err) {
if (err) cb({ err: err.message });
else if (this.changes != 1) cb({ err: "Lien expiré" });
else cb("OK");
});
}
});
socket.on("get_user", (id, cb) => {
if (db_login == undefined) cb({ err: "NTBS: Liste des utilisateurs inaccessible" });
else
db_login.get("SELECT nom,email,admin FROM users WHERE id=" + id, (err, row) => {
if (err) cb({ err: err.message });
else if (row == undefined) cb({ err: "NTBS: Utilisateur effacé" });
else cb({ nom: row.nom, email: row.email, admin: Boolean(row.admin), has_mp: Boolean(row.hash != undefined) });
});
});
socket.on("is_user", (nom, cb) => {
if (db_login == undefined) cb("NTBS: Liste des utilisateurs inaccessible");
else
db_login.get("SELECT COUNT(*) as cnt FROM USERS WHERE nom LIKE ? OR email LIKE ?", [nom, nom], (err, row) => {
if (!err && row && row.cnt) cb("OK:" + row.cnt);
else cb("");
});
});
socket.on("is_dispo", (champ, value, cb) => {
if (db_login == undefined) cb({ err: "NTBS: Liste des utilisateurs inaccessible" });
else
db_login.get("SELECT COUNT(*) as cnt FROM USERS WHERE " + champ + " LIKE ? AND id <> " + session.user.id, [value], (err, row) => {
if (err) cb({ err: err.message });
else cb({ dispo: row.cnt == 0 });
});
});
socket.on("updUser", (champ, value, cb) => {
if (db_login == undefined) cb({ err: "NTBS: Session fermée. Reconnectez vous" });
else
db_login.run("UPDATE users SET " + champ + "=? WHERE id=" + session.user.id, [value], (err) => {
if (err) cb(err.message);
else cb("OK");
});
});
function PasswordOK(pw, hash) {
// Pour une petite appli sans gros enjeux de sécurité, le md5 suffit largement.
// Pour une appli plus secure, utiliser bcrypt (mais install compliqué) ou Crypto
return (!hash && pw == "") || hash == md5(pw);
}
async function ChangeMP(id, pw) {
db_login.run("UPDATE users SET hash=" + (pw == "" ? "NULL" : "'" + md5(pw) + "'") + " WHERE id=" + id, (err) => {
return err ? err.message : "OK";
});
}
async function CheckMP(id, pw) {
db_login.get("SELECT hash FROM users WHERE id=" + id, (err, row) => {
if (err) return err.message;
else if (row == undefined) return "Utilisateur introuvable";
else if (!PasswordOK(pw, row.hash)) return "Mot de passe incorrect";
else return "OK";
});
}
socket.on("updpwd", async (old_pw, new_pw, cb) => {
let r = await CheckMP(session.user.id, old_pw);
if (r != "OK") cb(r);
else ChangeMP(id, new_pw).then((r) => cb(r));
});
}); // fin socket.io
//*******************
// GET Routes
//*******************
app.get("/login", (req, res) => {
req.session.user = undefined;
res.render("login.html");
});
app.get("/", checkAuthenticated, (req, res) => {
res.render("index.html");
});
app.get("/index", checkAuthenticated, (req, res) => {
res.render("index.html");
});
app.get("/user", checkAuthenticated, (req, res) => {
res.render("user.html");
});
app.get("/reset", (req, res) => {
res.render("resetpw.html");
});
//*******************
// GET Routes
//*******************
// L'utilisation de multer pour charger un fichier dans un répertoire fourni lors du POST
// est assez complexe. https://stackoverflow.com/questions/75157185/how-to-upload-a-file-using-multer-in-a-specific-directory-defined-by-the-fronten
app.post("/upload_this", function (req, res) {
upload(req, res, function (err) {
res.send("OK:" + req.file.path);
});
});
function checkAuthenticated(req, res, next) {
//console.log("checkAuthenticated", app_config.need_login, req.sesssion);
if (app_config.need_login == false) next();
else if (req.session != undefined && req.session.user != undefined) {
//console.log(req.session.user.nom);
next();
} else {
res.redirect("/login");
//console.log("Echec auth");
}
}
//********************
// En arrière toute !
//********************
// Evenement beforeExit (executé a la fermeture normale du programme)
process.on("beforeExit", function () {
console.log("Au revoir tout le monde.");
});
//Evenement SIGINT (executé a la fermeture par CTRL-C du programme)
process.on("SIGINT", function () {
console.log("Adieu, monde cruel !");
process.exit();
});
//*******************
// En avant toute !
//*******************
httpServer.on("error", (e) => {
onErreurServer(e);
});
httpServer.listen(app_config.http_port, () => {
console.log("Ecoute port " + app_config.http_port + ". CTRL-C pour finir");
});
if (app_config.https_port != undefined && app_config.https_port > 0 && app_config.ssl_dir != undefined && fs.existsSync(app_config.ssl_dir))
try {
// Certificate
const privateKey = fs.readFileSync(app_config.ssl_dir + "privkey.pem", "utf8");
const certificate = fs.readFileSync(app_config.ssl_dir + "cert.pem", "utf8");
const ca = fs.readFileSync(app_config.ssl_dir + "chain.pem", "utf8");
const credentials = {
key: privateKey,
cert: certificate,
ca: ca,
};
const httpsServer = require("https").createServer(credentials, app);
httpsServer.on("error", (e) => {
onErreurServer(e);
});
httpsServer.listen(app_config.https_port, () => {
console.log("HTTPS Server running on port " + app_config.https_port);
io.attach(httpsServer);
});
} catch (e) {
console.error(e.message);
}
// lancer le navigateur par défaut sur la page par défaut. Le séparateur de dossier est différent sous Windows
if (dir_sep != "/")
child_process.exec("start http://localhost:" + app_config.http_port, (error, stdout, stderr) => {
if (error) {
console.error("oups", error.message);
}
});
//***********************************************
// FIN DU PROGRAMME: ATTEND CONNECTION CLIENT
//***********************************************
//*******************
// HELPERS
//*******************
function onErreurServer(e) {
if (e.errno == "EADDRINUSE") {
console.log("Le port " + e.port + " est déjà écouté. Fermez l'application qui l'utilise");
process.exit();
} else throw e;
}
//*******************
// Upload storage
//*******************
let storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, dir_upload); // set upload directory on local system.
},
filename: (req, file, callback) => {
callback(null, req.body.nom == undefined ? file.originalname : req.body.nom);
},
});
let upload = multer({
storage: storage,
}).single("userFile");
//*******************
// SQLITE PROMISIFY
//*******************
async function db_get(db, query, values = []) {
return new Promise(function (resolve, reject) {
db.get(query, values, function (err, row) {
if (err) {
return reject(err);
}
return resolve(row);
});
});
}
async function db_all(db, query, values = []) {
return new Promise(function (resolve, reject) {
db.all(query, values, function (err, rows) {
if (err) {
return reject(err);
}
return resolve(rows);
});
});
}
async function db_run(db, query, values = []) {
return new Promise(function (resolve, reject) {
db.run(query, values, function (err, row) {
if (err) {
return reject(err);
}
return resolve(row);
});
});
}
async function MakeSQLfile(db, path, verbose = true) {
let ok = fs.existsSync(path);
if (ok) {
const lignes = fs.readFileSync(path).toString().split("\n");
let stm = "";
let bloc = false;
for (let el of lignes) {
let lig = el.replace("\r", "");
if (!(lig.startsWith("#") || lig.startsWith("//"))) {
stm += lig + " ";
if (lig == "BEGIN") bloc = true;
else if (lig.startsWith("END")) bloc = false;
if (!bloc && lig.endsWith(";")) {
try {
await db_run(db, stm);
} catch (e) {
console.error(e);
ok = false;
}
stm = "";
}
}
}
if (ok) return await getVersion(db);
} else if (verbose) console.log(path + ": fichier manquant");
return undefined;
}
async function getVersion(db) {
try {
let foo = await db_get(db, "SELECT paramValue FROM parametres WHERE paramName='VERSION_BASE'");
return Number(foo.paramValue);
} catch (e) {
return undefined;
}
}
async function openBase(dir, nom) {
let db = new sqlite3.Database(dir + nom + ".db");
let version = (await getVersion(db)) || (await MakeSQLfile(db, nom + ".sql"));
//console.log("openBase", nom, version);
if (!version) return Promise.reject(new Error("Pas de VERSION_BASE")); // exit
else {
let row = await db_get(db, "SELECT paramValue FROM parametres WHERE paramName='NATURE_BASE'");
if (version) while (await MakeSQLfile(db, row.paramValue.toLowerCase() + version + "vers" + (version + 1) + ".sql", false)) version++;
return db;
}
}
async function IsBridge(nom) {
let ok = false;
const db = new sqlite3.Database(db_dir + nom);
let version = await getVersion(db);
if (version) {
const row = await db_get(db, "SELECT paramValue FROM parametres WHERE paramName='NATURE_BASE'");
const nature = row.paramValue.toLowerCase();
if (nature == "bridge") {
while (await MakeSQLfile(db, nature + version + "vers" + (version + 1) + ".sql", false)) version++;
ok = true;
}
}
db.close();
return ok;
}
//******************