123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- const { JembaDbThread } = require('jembadb');
- const utils = require('../core/utils');
- const log = new (require('../core/AppLogger'))().log;//singleton
- const asyncExit = new (require('./AsyncExit'))();
- const cleanPeriod = 1*60*1000;//1 минута
- const cleanUnusedTokenTimeout = 5*60*1000;//5 минут
- class WebAccess {
- constructor(config) {
- this.config = config;
- this.freeAccess = (config.accessPassword === '');
- this.accessTimeout = config.accessTimeout*60*1000;
- this.accessMap = new Map();
- asyncExit.add(this.closeDb.bind(this));
- setTimeout(() => { this.periodicClean(); }, cleanPeriod);
- }
- async init() {
- const config = this.config;
- const dbPath = `${config.dataDir}/web-access`;
- const db = new JembaDbThread();//в отдельном потоке
- await db.lock({
- dbPath,
- create: true,
- softLock: true,
- tableDefaults: {
- cacheSize: config.dbCacheSize,
- },
- });
- try {
- //открываем таблицы
- await db.openAll();
- } catch(e) {
- if (
- e.message.indexOf('corrupted') >= 0
- || e.message.indexOf('Unexpected token') >= 0
- || e.message.indexOf('invalid stored block lengths') >= 0
- ) {
- log(LM_ERR, `DB ${dbPath} corrupted`);
- log(`Open "${dbPath}" with auto repair`);
- await db.openAll({autoRepair: true});
- } else {
- throw e;
- }
- }
- await db.create({table: 'access', quietIfExists: true});
- //проверим, нужно ли обнулить таблицу access
- const pass = utils.getBufHash(this.config.accessPassword, 'sha256', 'hex');
- await db.create({table: 'config', quietIfExists: true});
- let rows = await db.select({table: 'config', where: `@@id('pass')`});
- if (!rows.length || rows[0].value !== pass) {
- //пароль сменился в конфиге, обнуляем токены
- await db.truncate({table: 'access'});
- await db.insert({table: 'config', replace: true, rows: [{id: 'pass', value: pass}]});
- }
- //загрузим токены сессий
- rows = await db.select({table: 'access'});
- for (const row of rows)
- this.accessMap.set(row.id, row.value);
- this.db = db;
- }
- async closeDb() {
- if (this.db) {
- await this.db.unlock();
- this.db = null;
- }
- }
- async periodicClean() {
- while (1) {//eslint-disable-line no-constant-condition
- try {
- const now = Date.now();
- //почистим accessMap
- if (!this.freeAccess) {
- for (const [accessToken, accessRec] of this.accessMap) {
- if ( !(accessRec.used > 0 || now - accessRec.time < cleanUnusedTokenTimeout)
- || !(this.accessTimeout === 0 || now - accessRec.time < this.accessTimeout)
- ) {
- await this.deleteAccess(accessToken);
- } else if (!accessRec.saved) {
- await this.saveAccess(accessToken);
- }
- }
- }
- } catch(e) {
- log(LM_ERR, `WebAccess.periodicClean error: ${e.message}`);
- }
-
- await utils.sleep(cleanPeriod);
- }
- }
- async hasAccess(accessToken) {
- if (this.freeAccess)
- return true;
- const accessRec = this.accessMap.get(accessToken);
- if (accessRec) {
- const now = Date.now();
- if (this.accessTimeout === 0 || now - accessRec.time < this.accessTimeout) {
- accessRec.used++;
- accessRec.time = now;
- accessRec.saved = false;
- if (accessRec.used === 1)
- await this.saveAccess(accessToken);
- return true;
- }
- }
- return false;
- }
- async deleteAccess(accessToken) {
- await this.db.delete({table: 'access', where: `@@id(${this.db.esc(accessToken)})`});
- this.accessMap.delete(accessToken);
- }
- async saveAccess(accessToken) {
- const value = this.accessMap.get(accessToken);
- if (!value || value.saved)
- return;
- value.saved = true;
- await this.db.insert({
- table: 'access',
- replace: true,
- rows: [{id: accessToken, value}]
- });
- }
- newToken() {
- const salt = utils.randomHexString(32);
- const accessToken = utils.getBufHash(this.config.accessPassword + salt, 'sha256', 'hex');
- this.accessMap.set(accessToken, {time: Date.now(), used: 0});
- return salt;
- }
- }
- module.exports = WebAccess;
|