@bitwild/rockets-auth
Version:
Rockets Auth - Complete authentication and authorization solution for NestJS with JWT, OAuth, OTP, role-based access control, and more
611 lines • 22.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqliteRepositoryAdapter = void 0;
exports.Column = Column;
exports.Table = Table;
const sqlite3 = __importStar(require("sqlite3"));
const nestjs_common_1 = require("@concepta/nestjs-common");
const COLUMN_METADATA_KEY = Symbol('column_metadata');
const TABLE_METADATA_KEY = Symbol('table_metadata');
function Column(options = {}) {
return function (target, propertyKey) {
const existingMetadata = Reflect.getMetadata(COLUMN_METADATA_KEY, target) || {};
existingMetadata[propertyKey] = options;
Reflect.defineMetadata(COLUMN_METADATA_KEY, existingMetadata, target);
};
}
function Table(options = {}) {
return function (target) {
Reflect.defineMetadata(TABLE_METADATA_KEY, options, target);
};
}
class SqliteRepositoryAdapter {
constructor(entityClass, dbPath = './rockets.sqlite') {
this.columnCache = null;
this.entityClass = entityClass;
this.tableName = this.getTableNameFromEntity(entityClass);
this.db = new sqlite3.Database(dbPath);
this.initializeTable();
}
getTableNameFromEntity(entityClass) {
try {
if (typeof Reflect !== 'undefined' && Reflect.getMetadata) {
const tableMetadata = Reflect.getMetadata(TABLE_METADATA_KEY, entityClass) || {};
if (tableMetadata.name) {
return tableMetadata.name;
}
}
}
catch (e) {
}
const className = entityClass.name;
return (className
.replace(/([A-Z])/g, '_$1')
.toLowerCase()
.substring(1) + 's');
}
analyzeEntityProperties() {
var _a;
if (this.columnCache) {
return this.columnCache;
}
const properties = {};
const propertyNames = new Set();
try {
const globalWithTypeORM = global;
const typeormMetadataStorage = globalWithTypeORM.typeormMetadataStorage;
const metadata = (_a = typeormMetadataStorage === null || typeormMetadataStorage === void 0 ? void 0 : typeormMetadataStorage.tables) === null || _a === void 0 ? void 0 : _a.find((table) => table.target === this.entityClass);
if (metadata === null || metadata === void 0 ? void 0 : metadata.columns) {
metadata.columns.forEach((column) => {
propertyNames.add(column.propertyName);
});
}
}
catch (error) {
}
let currentPrototype = this.entityClass.prototype;
while (currentPrototype && currentPrototype !== Object.prototype) {
Object.getOwnPropertyNames(currentPrototype).forEach((name) => {
if (name !== 'constructor' && !name.startsWith('_')) {
const descriptor = Object.getOwnPropertyDescriptor(currentPrototype, name);
if (!descriptor || typeof descriptor.value !== 'function') {
propertyNames.add(name);
}
}
});
currentPrototype = Object.getPrototypeOf(currentPrototype);
}
try {
const sampleEntity = new this.entityClass();
Object.getOwnPropertyNames(sampleEntity).forEach((name) => {
if (!name.startsWith('_')) {
propertyNames.add(name);
}
});
}
catch (error) {
}
if (propertyNames.size === 0) {
['id', 'dateCreated', 'dateUpdated', 'dateDeleted', 'version'].forEach((prop) => {
propertyNames.add(prop);
});
}
propertyNames.add('id');
propertyNames.forEach((propertyName) => {
let metadata = null;
try {
const sampleEntity = new this.entityClass();
metadata = this.inferColumnMetadata(sampleEntity, propertyName);
}
catch (error) {
}
if (!metadata) {
const sqlType = this.inferTypeFromPropertyName(propertyName);
metadata = {
type: sqlType,
nullable: propertyName !== 'id',
};
}
properties[propertyName] = metadata;
});
this.applyUniversalConstraints(properties);
this.columnCache = properties;
return properties;
}
inferColumnMetadata(entity, propertyName) {
const value = entity[propertyName];
const propertyDescriptor = Object.getOwnPropertyDescriptor(entity, propertyName) ||
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), propertyName);
if (propertyDescriptor &&
(typeof propertyDescriptor.value === 'function' ||
propertyDescriptor.get ||
propertyDescriptor.set)) {
return null;
}
let sqlType;
let nullable = true;
let defaultValue;
if (typeof value === 'string') {
sqlType = 'TEXT';
}
else if (typeof value === 'number') {
sqlType = 'INTEGER';
}
else if (typeof value === 'boolean') {
sqlType = 'INTEGER';
defaultValue = value ? 1 : 0;
}
else if (value instanceof Date) {
sqlType = 'TEXT';
}
else if (value === null || value === undefined) {
sqlType = this.inferTypeFromPropertyName(propertyName);
nullable = true;
}
else {
sqlType = 'TEXT';
}
return {
type: sqlType,
nullable,
unique: false,
default: defaultValue,
};
}
inferTypeFromPropertyName(propertyName) {
const lowerName = propertyName.toLowerCase();
if (lowerName.includes('date') ||
lowerName.includes('time') ||
lowerName.endsWith('at')) {
return 'TEXT';
}
if (lowerName.includes('hash') ||
lowerName.includes('salt') ||
lowerName.includes('token')) {
return 'TEXT';
}
if (lowerName.includes('active') ||
lowerName.includes('enabled') ||
lowerName.includes('is') ||
lowerName.includes('has') ||
lowerName.includes('can') ||
lowerName.includes('should')) {
return 'INTEGER';
}
if (lowerName.includes('count') ||
lowerName.includes('number') ||
lowerName.includes('amount') ||
lowerName.includes('size') ||
lowerName.includes('length') ||
lowerName.includes('version')) {
return 'INTEGER';
}
return 'TEXT';
}
applyUniversalConstraints(columns) {
if (columns.id) {
columns.id.type = 'TEXT PRIMARY KEY';
columns.id.nullable = false;
}
if (columns.dateCreated) {
columns.dateCreated.type = 'TEXT NOT NULL';
columns.dateCreated.nullable = false;
}
if (columns.dateUpdated) {
columns.dateUpdated.type = 'TEXT NOT NULL';
columns.dateUpdated.nullable = false;
}
if (columns.dateDeleted) {
columns.dateDeleted.type = 'TEXT';
columns.dateDeleted.nullable = true;
}
if (columns.version) {
columns.version.type = 'INTEGER DEFAULT 1';
columns.version.nullable = false;
columns.version.default = 1;
}
}
getEntityColumns() {
const columnMetadata = this.analyzeEntityProperties();
const columns = {};
Object.entries(columnMetadata).forEach(([name, metadata]) => {
columns[name] = metadata;
});
return columns;
}
clearColumnCache() {
this.columnCache = null;
}
getColumnMetadata() {
return this.analyzeEntityProperties();
}
hasColumn(columnName) {
const metadata = this.analyzeEntityProperties();
return columnName in metadata;
}
initializeTable() {
const columns = this.getEntityColumns();
const columnDefinitions = Object.entries(columns)
.map(([name, metadata]) => `${name} ${metadata.type}`)
.join(',\n ');
const createTableSql = `
CREATE TABLE IF NOT EXISTS ${this.tableName} (
${columnDefinitions}
)
`;
try {
this.db.exec(createTableSql);
}
catch (err) {
console.error(`SqliteRepositoryAdapter: Error creating ${this.tableName} table:`, err);
throw err;
}
}
async ensureColumnsExist(requiredColumns) {
try {
const tableInfoSql = `PRAGMA table_info(${this.tableName})`;
const existingColumns = new Set();
const rows = await this.allQuery(tableInfoSql);
if (rows) {
rows.forEach((row) => {
existingColumns.add(row.name);
});
}
const columnMetadata = this.analyzeEntityProperties();
for (const columnName of requiredColumns) {
if (!existingColumns.has(columnName)) {
let metadata = columnMetadata[columnName];
if (!metadata) {
const sqlType = this.inferTypeFromPropertyName(columnName);
metadata = {
type: sqlType,
nullable: columnName !== 'id',
};
}
const columnDef = `${columnName} ${metadata.type}`;
const alterSql = `ALTER TABLE ${this.tableName} ADD COLUMN ${columnDef}`;
try {
await this.runQuery(alterSql);
}
catch (alterErr) {
console.error(`Error adding column ${columnName}:`, alterErr);
}
}
}
}
catch (err) {
console.error('Error ensuring columns exist:', err);
}
}
mapRowToEntity(row) {
const entity = {};
for (const [key, value] of Object.entries(row)) {
if (value !== null && value !== undefined) {
const isDateProperty = key.toLowerCase().includes('date') ||
key.toLowerCase().includes('time') ||
key.toLowerCase().includes('at') ||
key.toLowerCase().includes('created') ||
key.toLowerCase().includes('updated') ||
key.toLowerCase().includes('deleted');
if (isDateProperty &&
typeof value === 'string' &&
!isNaN(Date.parse(value))) {
entity[key] = new Date(value);
}
else if (key === 'active' && typeof value === 'number') {
entity[key] = Boolean(value);
}
else if (key === 'version' && typeof value === 'number') {
entity[key] = value;
}
else {
entity[key] = value;
}
}
else {
entity[key] = value;
}
}
return entity;
}
mapEntityToParams(entity) {
const columns = this.getColumnNames();
const columnMetadata = this.analyzeEntityProperties();
return columns.map((column) => {
const value = entity[column];
const metadata = columnMetadata[column];
if (value === null || value === undefined) {
return null;
}
if (metadata) {
if (metadata.type.includes('INTEGER')) {
if (typeof value === 'boolean') {
return value ? 1 : 0;
}
else {
return Number(value);
}
}
else if (value instanceof Date) {
return value.toISOString();
}
else {
return value;
}
}
else {
if (typeof value === 'boolean') {
return value ? 1 : 0;
}
else if (value instanceof Date) {
return value.toISOString();
}
else {
return value;
}
}
});
}
getColumnNames() {
return Object.keys(this.getEntityColumns());
}
entityName() {
return this.tableName;
}
generateId() {
return Date.now().toString() + Math.random().toString(36).substr(2, 9);
}
runQuery(sql, params = []) {
return new Promise((resolve, reject) => {
this.db.run(sql, params, function (err) {
if (err) {
reject(err);
}
else {
resolve({ lastID: this.lastID, changes: this.changes });
}
});
});
}
getQuery(sql, params = []) {
return new Promise((resolve, reject) => {
this.db.get(sql, params, (err, row) => {
if (err) {
reject(err);
}
else {
resolve(row);
}
});
});
}
allQuery(sql, params = []) {
return new Promise((resolve, reject) => {
this.db.all(sql, params, (err, rows) => {
if (err) {
reject(err);
}
else {
resolve(rows);
}
});
});
}
buildWhereClause(where, params) {
const conditions = [];
Object.entries(where).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
if (typeof value === 'object' &&
value !== null &&
!Array.isArray(value) &&
!(value instanceof Date)) {
const operators = value;
if (operators.$gt !== undefined) {
conditions.push(`${key} > ?`);
params.push(operators.$gt);
}
if (operators.$gte !== undefined) {
conditions.push(`${key} >= ?`);
params.push(operators.$gte);
}
if (operators.$lt !== undefined) {
conditions.push(`${key} < ?`);
params.push(operators.$lt);
}
if (operators.$lte !== undefined) {
conditions.push(`${key} <= ?`);
params.push(operators.$lte);
}
}
else {
conditions.push(`${key} = ?`);
params.push(value);
}
}
else {
conditions.push(`${key} IS NULL`);
}
});
return conditions.join(' AND ');
}
buildOrderClause(order) {
const orderClauses = [];
Object.entries(order).forEach(([key, direction]) => {
orderClauses.push(`${key} ${direction}`);
});
return orderClauses.join(', ');
}
async find(options) {
let sql = `SELECT * FROM ${this.tableName}`;
const params = [];
if (options === null || options === void 0 ? void 0 : options.where) {
const whereClause = this.buildWhereClause(options.where, params);
if (whereClause) {
sql += ` WHERE ${whereClause}`;
}
}
if (options === null || options === void 0 ? void 0 : options.order) {
const orderClause = this.buildOrderClause(options.order);
if (orderClause) {
sql += ` ORDER BY ${orderClause}`;
}
}
if (options === null || options === void 0 ? void 0 : options.take) {
sql += ` LIMIT ${options.take}`;
}
if (options === null || options === void 0 ? void 0 : options.skip) {
sql += ` OFFSET ${options.skip}`;
}
const rows = await this.allQuery(sql, params);
const entities = rows.map((row) => this.mapRowToEntity(row));
return entities;
}
async findOne(options) {
const results = await this.find(Object.assign(Object.assign({}, options), { take: 1 }));
const result = results.length > 0 ? results[0] : null;
return result;
}
create(entityLike) {
const entity = Object.assign({ id: this.generateId() }, entityLike);
return entity;
}
merge(mergeIntoEntity, ...entityLikes) {
return Object.assign(mergeIntoEntity, ...entityLikes);
}
async save(entity, options) {
try {
if (Array.isArray(entity)) {
const savedEntities = [];
for (const item of entity) {
const savedEntity = (await this.save(item, options));
savedEntities.push(savedEntity);
}
return savedEntities;
}
const entityRecord = entity;
if (!entityRecord.id) {
entityRecord.id = this.generateId();
}
const now = new Date();
if (!entityRecord.dateCreated) {
entityRecord.dateCreated = now;
}
entityRecord.dateUpdated = now;
if (entityRecord.version === null || entityRecord.version === undefined) {
entityRecord.version = 1;
}
const cachedColumns = this.getColumnNames();
const entityColumns = Object.keys(entityRecord).filter((key) => entityRecord[key] !== undefined && !key.startsWith('_'));
const allColumns = new Set([...entityColumns, ...cachedColumns]);
const columns = Array.from(allColumns);
await this.ensureColumnsExist(columns);
const placeholders = columns.map(() => '?').join(', ');
const sql = `INSERT OR REPLACE INTO ${this.tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
const params = columns.map((column) => {
const value = entityRecord[column];
if (value === null || value === undefined) {
return null;
}
if (typeof value === 'boolean') {
return value ? 1 : 0;
}
else if (value instanceof Date) {
return value.toISOString();
}
else {
return value;
}
});
await this.runQuery(sql, params);
return entity;
}
catch (e) {
console.error('Error in save method:', e);
throw new nestjs_common_1.ModelQueryException(this.entityName(), {
originalError: e,
});
}
}
async remove(entity) {
try {
if (Array.isArray(entity)) {
const removedEntities = [];
for (const item of entity) {
const removedEntity = (await this.remove(item));
removedEntities.push(removedEntity);
}
return removedEntities;
}
const entityRecord = entity;
if (entityRecord.id) {
const sql = `DELETE FROM ${this.tableName} WHERE id = ?`;
await this.runQuery(sql, [entityRecord.id]);
}
return entity;
}
catch (e) {
throw new nestjs_common_1.ModelQueryException(this.entityName(), {
originalError: e,
});
}
}
gt(value) {
return { $gt: value };
}
gte(value) {
return { $gte: value };
}
lt(value) {
return { $lt: value };
}
lte(value) {
return { $lte: value };
}
async close() {
return new Promise((resolve, reject) => {
this.db.close((err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
}
}
exports.SqliteRepositoryAdapter = SqliteRepositoryAdapter;
//# sourceMappingURL=sqlite-repository.adapter.js.map