174 lines
4.6 KiB
JavaScript
174 lines
4.6 KiB
JavaScript
"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,
|
||
};
|