privet/lib/store.js

174 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
let DATA_FILE = path.join(__dirname, "..", "data.json");
let UPLOADS_DIR = path.join(__dirname, "..", "uploads");
function configure(dataFile, uploadsDir) {
DATA_FILE = dataFile;
UPLOADS_DIR = uploadsDir;
}
const DEFAULT_DEADLINE = "2026-10-15";
const START_COMPANIES = [
"АО «НК «КазМунайГаз»",
"АО «НК «КТЖ»",
"АО «Казахтелеком»",
"АО «KEGOC»",
"АО «НАК «Казатомпром»",
"АО «Казпочта»",
"АО «Самрук-Энерго»",
"ТОО «Самрук-Казына Инвест»",
"ТОО «Самрук-Казына Контракт»",
"ТОО «Samruk-Kazyna Ondeu»",
"АО «НГК «Таукен Самрук»",
"АО «Samruk-Kazyna Construction»",
"АО «НК «QazaqGaz»",
"ТОО «ПГУ Туркестан»",
"АО «ФНБ «Самрук-Казына»",
];
function ensureUploadsDir() {
if (!fs.existsSync(UPLOADS_DIR)) fs.mkdirSync(UPLOADS_DIR, { recursive: true });
}
function readRaw() {
if (!fs.existsSync(DATA_FILE)) return null;
try {
return JSON.parse(fs.readFileSync(DATA_FILE, "utf8"));
} catch (e) {
return null;
}
}
function writeRaw(db) {
fs.writeFileSync(DATA_FILE, JSON.stringify(db, null, 2));
}
function randPassword(len) {
const abc = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
let s = "";
const buf = crypto.randomBytes(len);
for (let i = 0; i < len; i++) s += abc[buf[i] % abc.length];
return s;
}
function makeUser(record) {
const pw = record._seedPassword || randPassword(12);
const salt = crypto.randomBytes(32);
const hash = crypto.scryptSync(pw, salt, 32);
const { _seedPassword, ...rest } = record;
return Object.assign(
{ failedCount: 0, lockedUntil: 0 },
rest,
{
salt: salt.toString("hex"),
passwordHash: hash.toString("hex"),
active: true,
...(record._keepTemp ? { tempPassword: pw } : {})
}
);
}
function seed() {
ensureUploadsDir();
const db = {
period: "3-й квартал 2026",
deadline: DEFAULT_DEADLINE,
consolidatorPhone: "",
users: [],
reports: [],
sessions: {},
};
const consPw = process.env.KFU_SVOD_PASSWORD || "12345678";
const cons = makeUser({
id: "u_consolidator",
role: "consolidator",
name: "",
companyName: "",
login: "svod",
_seedPassword: consPw,
});
db._consolidatorTempPassword = consPw;
db.users.push(cons);
const tmpPws = [];
START_COMPANIES.forEach(function (name, i) {
const pw = randPassword(12);
const salt = crypto.randomBytes(32);
const hash = crypto.scryptSync(pw, salt, 32);
const u = {
id: "u_" + i,
role: "subsidiary",
name: "",
companyName: name,
person: "",
phone: "",
login: "c" + (i + 1),
salt: salt.toString("hex"),
passwordHash: hash.toString("hex"),
active: true,
failedCount: 0,
lockedUntil: 0,
};
tmpPws.push(name + " | c" + (i + 1) + " | " + pw);
db.users.push(u);
});
db._firstRunSubsidiaryPasswords = tmpPws.join("\n");
writeRaw(db);
return db;
}
function load() {
let db = readRaw();
if (!db || !Array.isArray(db.users) || !Array.isArray(db.reports)) {
db = seed();
}
if (!Array.isArray(db.reports)) db.reports = [];
if (!db.period) db.period = "3-й квартал 2026";
if (!db.deadline) db.deadline = DEFAULT_DEADLINE;
db.users.forEach(function (u) {
if (typeof u.failedCount !== "number") u.failedCount = 0;
if (typeof u.lockedUntil !== "number") u.lockedUntil = 0;
if (typeof u.active !== "boolean") u.active = true;
});
if (!db.sessions || typeof db.sessions !== "object") db.sessions = {};
return db;
}
function save(db) {
writeRaw(db);
}
function genId(prefix) {
return prefix + "_" + Date.now().toString(36) + "_" + crypto.randomBytes(4).toString("hex");
}
function hashPassword(password, saltHex) {
const salt = Buffer.from(saltHex, "hex");
return crypto.scryptSync(password, salt, 32).toString("hex");
}
function verifyPassword(password, saltHex, expectedHex) {
const hash = Buffer.from(hashPassword(password, saltHex), "hex");
const expected = Buffer.from(expectedHex, "hex");
return hash.length === expected.length && crypto.timingSafeEqual(hash, expected);
}
module.exports = {
DATA_FILE: DATA_FILE,
UPLOADS_DIR: UPLOADS_DIR,
configure: configure,
ensureUploadsDir: ensureUploadsDir,
randPassword: randPassword,
makeUser: makeUser,
load: load,
save: save,
genId: genId,
START_COMPANIES: START_COMPANIES,
verifyPassword: verifyPassword,
};