parent
0c893baf56
commit
0055301391
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(()=>{"use strict";var e,v={},g={};function r(e){var i=g[e];if(void 0!==i)return i.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(i,t,f,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,f,o]=e[n],c=!0,u=0;u<t.length;u++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[u]))?t.splice(u--,1):(c=!1,o<a&&(a=o));if(c){e.splice(n--,1);var d=f();void 0!==d&&(i=d)}}return i}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,f,o]},r.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return r.d(i,{a:i}),i},r.d=(e,i)=>{for(var t in i)r.o(i,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((i,t)=>(r.f[t](e,i),i),[])),r.u=e=>e+"."+{632:"ed9e371233e76094",637:"2fc9559ec90af37c",859:"dccba581e7254ce5",893:"277223bd17cb8056"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="RTLApp:";r.l=(t,f,o,n)=>{if(e[t])e[t].push(f);else{var a,c;if(void 0!==o)for(var u=document.getElementsByTagName("script"),d=0;d<u.length;d++){var l=u[d];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==i+o){a=l;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",i+o),a.src=r.tu(t)),e[t]=[f];var s=(m,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(_=>_(b)),m)return m(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tu=i=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(i))})(),r.p="",(()=>{var e={666:0};r.f.j=(f,o)=>{var n=r.o(e,f)?e[f]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=f){var a=new Promise((l,s)=>n=e[f]=[l,s]);o.push(n[2]=a);var c=r.p+r.u(f),u=new Error;r.l(c,l=>{if(r.o(e,f)&&(0!==(n=e[f])&&(e[f]=void 0),n)){var s=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;u.message="Loading chunk "+f+" failed.\n("+s+": "+p+")",u.name="ChunkLoadError",u.type=s,u.request=p,n[1](u)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var i=(f,o)=>{var u,d,[n,a,c]=o,l=0;if(n.some(p=>0!==e[p])){for(u in a)r.o(a,u)&&(r.m[u]=a[u]);if(c)var s=c(r)}for(f&&f(o);l<n.length;l++)r.o(e,d=n[l])&&e[d]&&e[d][0](),e[n[l]]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(i.bind(null,0)),t.push=i.bind(null,t.push.bind(t))})()})();
|
||||
(()=>{"use strict";var e,v={},g={};function r(e){var i=g[e];if(void 0!==i)return i.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(i,t,f,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,f,o]=e[n],s=!0,u=0;u<t.length;u++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[u]))?t.splice(u--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var d=f();void 0!==d&&(i=d)}}return i}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,f,o]},r.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return r.d(i,{a:i}),i},r.d=(e,i)=>{for(var t in i)r.o(i,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((i,t)=>(r.f[t](e,i),i),[])),r.u=e=>e+"."+{632:"4af5fdd4fe008747",637:"ba85cb517f1848fc",859:"a1744360a2102eb6",893:"a44950223f73d9f3"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="RTLApp:";r.l=(t,f,o,n)=>{if(e[t])e[t].push(f);else{var a,s;if(void 0!==o)for(var u=document.getElementsByTagName("script"),d=0;d<u.length;d++){var l=u[d];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==i+o){a=l;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",i+o),a.src=r.tu(t)),e[t]=[f];var c=(m,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(_=>_(b)),m)return m(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tu=i=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(i))})(),r.p="",(()=>{var e={666:0};r.f.j=(f,o)=>{var n=r.o(e,f)?e[f]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=f){var a=new Promise((l,c)=>n=e[f]=[l,c]);o.push(n[2]=a);var s=r.p+r.u(f),u=new Error;r.l(s,l=>{if(r.o(e,f)&&(0!==(n=e[f])&&(e[f]=void 0),n)){var c=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;u.message="Loading chunk "+f+" failed.\n("+c+": "+p+")",u.name="ChunkLoadError",u.type=c,u.request=p,n[1](u)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var i=(f,o)=>{var u,d,[n,a,s]=o,l=0;if(n.some(p=>0!==e[p])){for(u in a)r.o(a,u)&&(r.m[u]=a[u]);if(s)var c=s(r)}for(f&&f(o);l<n.length;l++)r.o(e,d=n[l])&&e[d]&&e[d][0](),e[n[l]]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(i.bind(null,0)),t.push=i.bind(null,t.push.bind(t))})()})();
|
@ -0,0 +1,38 @@
|
||||
export var CollectionsEnum;
|
||||
(function (CollectionsEnum) {
|
||||
CollectionsEnum["OFFERS"] = "Offers";
|
||||
})(CollectionsEnum || (CollectionsEnum = {}));
|
||||
export var OfferFieldsEnum;
|
||||
(function (OfferFieldsEnum) {
|
||||
OfferFieldsEnum["BOLT12"] = "bolt12";
|
||||
OfferFieldsEnum["AMOUNTMSAT"] = "amountmSat";
|
||||
OfferFieldsEnum["TITLE"] = "title";
|
||||
OfferFieldsEnum["VENDOR"] = "vendor";
|
||||
OfferFieldsEnum["DESCRIPTION"] = "description";
|
||||
})(OfferFieldsEnum || (OfferFieldsEnum = {}));
|
||||
export const CollectionFieldsEnum = Object.assign({}, OfferFieldsEnum);
|
||||
export class Offer {
|
||||
constructor(bolt12, amountmSat, title, vendor, description, lastUpdatedAt) {
|
||||
this.bolt12 = bolt12;
|
||||
this.amountmSat = amountmSat;
|
||||
this.title = title;
|
||||
this.vendor = vendor;
|
||||
this.description = description;
|
||||
this.lastUpdatedAt = lastUpdatedAt;
|
||||
}
|
||||
}
|
||||
export const validateOffer = (documentToValidate) => {
|
||||
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' });
|
||||
}
|
||||
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' });
|
||||
}
|
||||
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' });
|
||||
}
|
||||
if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' });
|
||||
}
|
||||
return ({ isValid: true });
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
export const Offer = (sequelize, Sequelize) => {
|
||||
const offerInstance = sequelize.define('Offers', {
|
||||
id: {
|
||||
type: Sequelize.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
offerBolt12: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
amountmSat: {
|
||||
type: Sequelize.NUMBER,
|
||||
allowNull: false
|
||||
},
|
||||
title: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
vendor: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
description: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
}
|
||||
});
|
||||
return offerInstance;
|
||||
};
|
@ -1,28 +1,205 @@
|
||||
import { join, dirname } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { join, dirname, sep } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import sqlz from 'sequelize';
|
||||
const { Sequelize } = sqlz;
|
||||
import { Common } from '../utils/common.js';
|
||||
import { Logger } from '../utils/logger.js';
|
||||
import { Offer } from '../models/offers.model.js';
|
||||
import { CollectionsEnum, validateOffer } from '../models/database.model.js';
|
||||
export class DatabaseService {
|
||||
constructor() {
|
||||
this.common = Common;
|
||||
this.logger = Logger;
|
||||
this.rtlConfigPassword = '';
|
||||
this.directoryName = dirname(fileURLToPath(import.meta.url));
|
||||
this.rtlSequelize = null;
|
||||
this.rtlDB = null;
|
||||
this.rtlSequelize = new Sequelize({
|
||||
database: 'RTLStore',
|
||||
username: '',
|
||||
password: '',
|
||||
storage: join(this.directoryName, '..', '..', 'database', 'rtl-db.sqlite'),
|
||||
dialect: 'sqlite',
|
||||
logging: messageToLog => this.logger.log({ level: 'DEBUG', fileName: 'DBLog', msg: messageToLog })
|
||||
this.dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database');
|
||||
this.nodeDatabase = {};
|
||||
}
|
||||
loadDatabase(selectedNode) {
|
||||
try {
|
||||
if (!this.nodeDatabase[selectedNode.index]) {
|
||||
this.nodeDatabase[selectedNode.index] = { adapter: null, data: null };
|
||||
}
|
||||
this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode);
|
||||
this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData();
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err });
|
||||
}
|
||||
}
|
||||
create(selectedNode, collectionName, newDocument) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, newDocument);
|
||||
if (!validationRes.isValid) {
|
||||
reject(validationRes.error);
|
||||
}
|
||||
else {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].push(newDocument);
|
||||
this.saveDatabase(+selectedNode.index);
|
||||
resolve(newDocument);
|
||||
}
|
||||
}
|
||||
catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
update(selectedNode, collectionName, updatedDocument, documentFieldName, documentFieldValue) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
let foundDocIdx = -1;
|
||||
let foundDoc = null;
|
||||
if (this.nodeDatabase[selectedNode.index].data[collectionName]) {
|
||||
foundDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue);
|
||||
foundDoc = foundDocIdx > -1 ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) : null;
|
||||
}
|
||||
if (foundDocIdx > -1 && foundDoc) {
|
||||
for (const docKey in updatedDocument) {
|
||||
if (Object.prototype.hasOwnProperty.call(updatedDocument, docKey)) {
|
||||
foundDoc[docKey] = updatedDocument[docKey];
|
||||
}
|
||||
}
|
||||
updatedDocument = foundDoc;
|
||||
}
|
||||
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, updatedDocument);
|
||||
if (!validationRes.isValid) {
|
||||
reject(validationRes.error);
|
||||
}
|
||||
else {
|
||||
if (foundDocIdx > -1) {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument);
|
||||
}
|
||||
else {
|
||||
if (!this.nodeDatabase[selectedNode.index].data[collectionName]) {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName] = [];
|
||||
}
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].push(updatedDocument);
|
||||
}
|
||||
this.saveDatabase(+selectedNode.index);
|
||||
resolve(updatedDocument);
|
||||
}
|
||||
}
|
||||
catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
find(selectedNode, collectionName, documentFieldName, documentFieldValue) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
if (documentFieldName && documentFieldValue) {
|
||||
resolve(this.nodeDatabase[selectedNode.index].data[collectionName].find((document) => document[documentFieldName] === documentFieldValue));
|
||||
}
|
||||
else {
|
||||
resolve(this.nodeDatabase[selectedNode.index].data[collectionName]);
|
||||
}
|
||||
}
|
||||
catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
destroy(selectedNode, collectionName, documentFieldName, documentFieldValue) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
const removeDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue);
|
||||
if (removeDocIdx > -1) {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].splice(removeDocIdx, 1);
|
||||
}
|
||||
else {
|
||||
reject(new Error('Unable to delete, document not found.'));
|
||||
}
|
||||
this.saveDatabase(+selectedNode.index);
|
||||
resolve(documentFieldValue);
|
||||
}
|
||||
catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
this.rtlDB = {
|
||||
Sequelize: this.rtlSequelize,
|
||||
offer: Offer(this.rtlSequelize, Sequelize)
|
||||
};
|
||||
}
|
||||
validateDocument(collectionName, documentToValidate) {
|
||||
switch (collectionName) {
|
||||
case CollectionsEnum.OFFERS:
|
||||
return validateOffer(documentToValidate);
|
||||
default:
|
||||
return ({ isValid: false, error: 'Collection does not exist' });
|
||||
}
|
||||
}
|
||||
saveDatabase(nodeIndex) {
|
||||
try {
|
||||
if (!this.nodeDatabase[nodeIndex]) {
|
||||
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Config Not Found.' });
|
||||
}
|
||||
this.nodeDatabase[nodeIndex].adapter.saveData(this.nodeDatabase[nodeIndex].data);
|
||||
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Saved' });
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err });
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
unloadDatabase(nodeIndex) {
|
||||
this.saveDatabase(nodeIndex);
|
||||
this.nodeDatabase[nodeIndex] = null;
|
||||
}
|
||||
}
|
||||
export class DatabaseAdapter {
|
||||
constructor(dbDirectoryPath, fileName, selNode = null) {
|
||||
this.dbDirectoryPath = dbDirectoryPath;
|
||||
this.fileName = fileName;
|
||||
this.selNode = selNode;
|
||||
this.dbFile = '';
|
||||
this.dbFile = dbDirectoryPath + sep + fileName + '-node-' + selNode.index + '.json';
|
||||
}
|
||||
fetchData() {
|
||||
try {
|
||||
if (!fs.existsSync(this.dbDirectoryPath)) {
|
||||
fs.mkdirSync(this.dbDirectoryPath);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
return new Error('Unable to Create Directory Error ' + JSON.stringify(err));
|
||||
}
|
||||
try {
|
||||
if (!fs.existsSync(this.dbFile)) {
|
||||
fs.writeFileSync(this.dbFile, '{}');
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
return new Error('Unable to Create Database File Error ' + JSON.stringify(err));
|
||||
}
|
||||
try {
|
||||
const dataFromFile = fs.readFileSync(this.dbFile, 'utf-8');
|
||||
return !dataFromFile ? null : JSON.parse(dataFromFile);
|
||||
}
|
||||
catch (err) {
|
||||
return new Error('Database Read Error ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
getSelNode() {
|
||||
return this.selNode;
|
||||
}
|
||||
saveData(data) {
|
||||
try {
|
||||
if (data) {
|
||||
const tempFile = this.dbFile + '.tmp';
|
||||
fs.writeFileSync(tempFile, JSON.stringify(data, null, 2));
|
||||
fs.renameSync(tempFile, this.dbFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (err) {
|
||||
return new Error('Database Write Error ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
export const Database = new DatabaseService();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,46 @@
|
||||
export enum CollectionsEnum {
|
||||
OFFERS = 'Offers'
|
||||
}
|
||||
|
||||
export type Collections = {
|
||||
Offers: Offer[];
|
||||
}
|
||||
|
||||
export enum OfferFieldsEnum {
|
||||
BOLT12 = 'bolt12',
|
||||
AMOUNTMSAT = 'amountmSat',
|
||||
TITLE = 'title',
|
||||
VENDOR = 'vendor',
|
||||
DESCRIPTION = 'description'
|
||||
}
|
||||
|
||||
export const CollectionFieldsEnum = { ...OfferFieldsEnum };
|
||||
|
||||
export class Offer {
|
||||
|
||||
constructor(
|
||||
public bolt12: string,
|
||||
public amountmSat: number,
|
||||
public title: string,
|
||||
public vendor?: string,
|
||||
public description?: string,
|
||||
public lastUpdatedAt?: number
|
||||
) { }
|
||||
|
||||
}
|
||||
|
||||
export const validateOffer = (documentToValidate): any => {
|
||||
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.BOLT12)) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.BOLT12 + 'is mandatory.' });
|
||||
}
|
||||
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.AMOUNTMSAT)) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'is mandatory.' });
|
||||
}
|
||||
if (!documentToValidate.hasOwnProperty(CollectionFieldsEnum.TITLE)) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.TITLE + 'is mandatory.' });
|
||||
}
|
||||
if ((typeof documentToValidate[CollectionFieldsEnum.AMOUNTMSAT] !== 'number')) {
|
||||
return ({ isValid: false, error: CollectionFieldsEnum.AMOUNTMSAT + 'should be a number.' });
|
||||
}
|
||||
return ({ isValid: true });
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
export const Offer = (sequelize, Sequelize) => {
|
||||
const offerInstance = sequelize.define('Offers', {
|
||||
id: {
|
||||
type: Sequelize.UUIDV4,
|
||||
primaryKey: true,
|
||||
allowNull: false
|
||||
},
|
||||
offerBolt12: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
amountmSat: {
|
||||
type: Sequelize.NUMBER,
|
||||
allowNull: false
|
||||
},
|
||||
title: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
vendor: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
description: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
}
|
||||
});
|
||||
return offerInstance;
|
||||
};
|
@ -1,32 +1,208 @@
|
||||
import { join, dirname } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { join, dirname, sep } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import sqlz from 'sequelize';
|
||||
const { Sequelize } = sqlz;
|
||||
import { Common, CommonService } from '../utils/common.js';
|
||||
import { Logger, LoggerService } from '../utils/logger.js';
|
||||
import { Offer } from '../models/offers.model.js';
|
||||
import { Collections, CollectionsEnum, validateOffer } from '../models/database.model.js';
|
||||
import { CommonSelectedNode } from '../models/config.model.js';
|
||||
|
||||
export class DatabaseService {
|
||||
|
||||
public common: CommonService = Common;
|
||||
public logger: LoggerService = Logger;
|
||||
public rtlConfigPassword = '';
|
||||
public directoryName = dirname(fileURLToPath(import.meta.url));
|
||||
public rtlSequelize = null;
|
||||
public rtlDB = null;
|
||||
|
||||
constructor() {
|
||||
this.rtlSequelize = new Sequelize({
|
||||
database: 'RTLStore',
|
||||
username: '',
|
||||
password: '',
|
||||
storage: join(this.directoryName, '..', '..', 'database', 'rtl-db.sqlite'),
|
||||
dialect: 'sqlite',
|
||||
logging: messageToLog => this.logger.log({ level: 'DEBUG', fileName: 'DBLog', msg: messageToLog })
|
||||
public dbDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'database');
|
||||
public nodeDatabase: { id?: { adapter: DatabaseAdapter, data: Collections } } = {};
|
||||
|
||||
constructor() { }
|
||||
|
||||
loadDatabase(selectedNode: CommonSelectedNode) {
|
||||
try {
|
||||
if (!this.nodeDatabase[selectedNode.index]) {
|
||||
this.nodeDatabase[selectedNode.index] = { adapter: null, data: null };
|
||||
}
|
||||
this.nodeDatabase[selectedNode.index].adapter = new DatabaseAdapter(this.dbDirectory, 'rtldb', selectedNode);
|
||||
this.nodeDatabase[selectedNode.index].data = this.nodeDatabase[selectedNode.index].adapter.fetchData();
|
||||
} catch (err) {
|
||||
this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: 'Database', msg: 'Database Load Error', error: err });
|
||||
}
|
||||
}
|
||||
|
||||
create(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, newDocument: any) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, newDocument);
|
||||
if (!validationRes.isValid) {
|
||||
reject(validationRes.error);
|
||||
} else {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].push(newDocument);
|
||||
this.saveDatabase(+selectedNode.index);
|
||||
resolve(newDocument);
|
||||
}
|
||||
} catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
this.rtlDB = {
|
||||
Sequelize: this.rtlSequelize,
|
||||
offer: Offer(this.rtlSequelize, Sequelize)
|
||||
};
|
||||
}
|
||||
|
||||
update(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, updatedDocument: any, documentFieldName: string, documentFieldValue: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
let foundDocIdx = -1;
|
||||
let foundDoc = null;
|
||||
if (this.nodeDatabase[selectedNode.index].data[collectionName]) {
|
||||
foundDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document: any) => document[documentFieldName] === documentFieldValue);
|
||||
foundDoc = foundDocIdx > -1 ? JSON.parse(JSON.stringify(this.nodeDatabase[selectedNode.index].data[collectionName][foundDocIdx])) : null;
|
||||
}
|
||||
if (foundDocIdx > -1 && foundDoc) {
|
||||
for (const docKey in updatedDocument) {
|
||||
if (Object.prototype.hasOwnProperty.call(updatedDocument, docKey)) {
|
||||
foundDoc[docKey] = updatedDocument[docKey];
|
||||
}
|
||||
}
|
||||
updatedDocument = foundDoc;
|
||||
}
|
||||
const validationRes = this.validateDocument(CollectionsEnum.OFFERS, updatedDocument);
|
||||
if (!validationRes.isValid) {
|
||||
reject(validationRes.error);
|
||||
} else {
|
||||
if (foundDocIdx > -1) {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].splice(foundDocIdx, 1, updatedDocument);
|
||||
} else {
|
||||
if (!this.nodeDatabase[selectedNode.index].data[collectionName]) {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName] = [];
|
||||
}
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].push(updatedDocument);
|
||||
}
|
||||
this.saveDatabase(+selectedNode.index);
|
||||
resolve(updatedDocument);
|
||||
}
|
||||
} catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
find(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName?: string, documentFieldValue?: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
if (documentFieldName && documentFieldValue) {
|
||||
resolve(this.nodeDatabase[selectedNode.index].data[collectionName].find((document: any) => document[documentFieldName] === documentFieldValue));
|
||||
} else {
|
||||
resolve(this.nodeDatabase[selectedNode.index].data[collectionName]);
|
||||
}
|
||||
} catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy(selectedNode: CommonSelectedNode, collectionName: CollectionsEnum, documentFieldName: string, documentFieldValue: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!selectedNode || !selectedNode.index) {
|
||||
reject(new Error('Selected Node Config Not Found.'));
|
||||
}
|
||||
const removeDocIdx = this.nodeDatabase[selectedNode.index].data[collectionName].findIndex((document) => document[documentFieldName] === documentFieldValue);
|
||||
if (removeDocIdx > -1) {
|
||||
this.nodeDatabase[selectedNode.index].data[collectionName].splice(removeDocIdx, 1);
|
||||
} else {
|
||||
reject(new Error('Unable to delete, document not found.'));
|
||||
}
|
||||
this.saveDatabase(+selectedNode.index);
|
||||
resolve(documentFieldValue);
|
||||
} catch (errRes) {
|
||||
reject(errRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
validateDocument(collectionName: CollectionsEnum, documentToValidate: any) {
|
||||
switch (collectionName) {
|
||||
case CollectionsEnum.OFFERS:
|
||||
return validateOffer(documentToValidate);
|
||||
|
||||
default:
|
||||
return ({ isValid: false, error: 'Collection does not exist' });
|
||||
}
|
||||
}
|
||||
|
||||
saveDatabase(nodeIndex: number) {
|
||||
try {
|
||||
if (!this.nodeDatabase[nodeIndex]) {
|
||||
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error: Selected Node Config Not Found.' });
|
||||
}
|
||||
this.nodeDatabase[nodeIndex].adapter.saveData(this.nodeDatabase[nodeIndex].data);
|
||||
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'INFO', fileName: 'Database', msg: 'Database Saved' });
|
||||
} catch (err) {
|
||||
this.logger.log({ selectedNode: this.nodeDatabase[nodeIndex].adapter.selNode, level: 'ERROR', fileName: 'Database', msg: 'Database Save Error', error: err });
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
unloadDatabase(nodeIndex: number) {
|
||||
this.saveDatabase(nodeIndex);
|
||||
this.nodeDatabase[nodeIndex] = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class DatabaseAdapter {
|
||||
|
||||
private dbFile = '';
|
||||
|
||||
constructor(public dbDirectoryPath: string, public fileName: string, private selNode: CommonSelectedNode = null) {
|
||||
this.dbFile = dbDirectoryPath + sep + fileName + '-node-' + selNode.index + '.json';
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
try {
|
||||
if (!fs.existsSync(this.dbDirectoryPath)) {
|
||||
fs.mkdirSync(this.dbDirectoryPath);
|
||||
}
|
||||
} catch (err) {
|
||||
return new Error('Unable to Create Directory Error ' + JSON.stringify(err));
|
||||
}
|
||||
try {
|
||||
if (!fs.existsSync(this.dbFile)) {
|
||||
fs.writeFileSync(this.dbFile, '{}');
|
||||
}
|
||||
} catch (err) {
|
||||
return new Error('Unable to Create Database File Error ' + JSON.stringify(err));
|
||||
}
|
||||
try {
|
||||
const dataFromFile = fs.readFileSync(this.dbFile, 'utf-8');
|
||||
return !dataFromFile ? null : (<Collections>JSON.parse(dataFromFile));
|
||||
} catch (err) {
|
||||
return new Error('Database Read Error ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
getSelNode() {
|
||||
return this.selNode;
|
||||
}
|
||||
|
||||
saveData(data: any) {
|
||||
try {
|
||||
if (data) {
|
||||
const tempFile = this.dbFile + '.tmp';
|
||||
fs.writeFileSync(tempFile, JSON.stringify(data, null, 2));
|
||||
fs.renameSync(tempFile, this.dbFile);
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
return new Error('Database Write Error ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const Database = new DatabaseService();
|
||||
|
Loading…
Reference in New Issue