Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions adminforth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import jwt from 'jsonwebtoken';
import crypto from 'crypto';
import AdminForth from './index.js';
import { IAdminForthAuth } from './types/Back.js';
import { afLogger } from './modules/logger.js';

// Function to generate a password hash using PBKDF2
function calcPasswordHash(password, salt, iterations = 100000, keyLength = 64, digest = 'sha512') {
Expand Down Expand Up @@ -105,7 +106,7 @@ class AdminForthAuth implements IAdminForthAuth {
if (expirySeconds !== undefined) {
expiryMs = expirySeconds * 1000;
} else if (expiry !== undefined) {
console.warn('setCustomCookie: expiry(in ms) is deprecated, use expirySeconds instead (seconds), traceback:', new Error().stack);
afLogger.warn(`setCustomCookie: expiry(in ms) is deprecated, use expirySeconds instead (seconds), traceback: ${new Error().stack}`);
expiryMs = expiry;
}

Expand Down Expand Up @@ -145,23 +146,23 @@ class AdminForthAuth implements IAdminForthAuth {
decoded = jwt.verify(jwtToken, secret);
} catch (err) {
if (err.name === 'TokenExpiredError') {
console.error('Token expired:', err.message);
afLogger.error(`Token expired: ${err.message}`);
} else if (err.name === 'JsonWebTokenError') {
console.error('Token error:', err.message);
afLogger.error(`Token error: ${err.message}`);
} else {
console.error('Failed to verify JWT token', err);
afLogger.error(`Failed to verify JWT token: ${err}`);
}
return null;
}
const { pk, t } = decoded;
if (t !== mustHaveType) {
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
afLogger.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
return null;
}
if (decodeUser !== false) {
const dbUser = await this.adminforth.getUserByPk(pk);
if (!dbUser) {
console.error(`User with pk ${pk} not found in database`);
afLogger.error(`User with pk ${pk} not found in database`);
// will logout user which was deleted
return null;
}
Expand Down
6 changes: 4 additions & 2 deletions adminforth/basePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import fs from 'fs';

import crypto from 'crypto';

import { afLogger } from './modules/logger.js';


export default class AdminForthPlugin implements IAdminForthPlugin {

Expand All @@ -24,7 +26,7 @@ export default class AdminForthPlugin implements IAdminForthPlugin {
this.pluginDir = currentFileDir(metaUrl);
this.customFolderPath = path.join(this.pluginDir, this.customFolderName);
this.pluginOptions = pluginOptions;
process.env.HEAVY_DEBUG && console.log(`🪲 🪲 AdminForthPlugin.constructor`, this.constructor.name);
afLogger.trace(`🪲 🪲 AdminForthPlugin.constructor ${this.constructor.name}`);
this.className = this.constructor.name;
}

Expand All @@ -42,7 +44,7 @@ export default class AdminForthPlugin implements IAdminForthPlugin {

const seed = `af_pl_${this.constructor.name}_${resourceConfig.resourceId}_${uniqueness}`;
this.pluginInstanceId = md5hash(seed);
process.env.HEAVY_DEBUG && console.log(`🪲 AdminForthPlugin.modifyResourceConfig`, seed, 'id', this.pluginInstanceId);
afLogger.trace(`🪲 AdminForthPlugin.modifyResourceConfig, ${seed}, 'id', ${this.pluginInstanceId}`);
this.adminforth = adminforth;
}

Expand Down
3 changes: 0 additions & 3 deletions adminforth/commands/createCustomComponent/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,6 @@ async function handleCrudPageInjectionCreation(config, resources) {
const injectionPosition = await select({
message: 'Where exactly do you want to inject the component?',
choices: [
...(crudType === 'create' || crudType === 'edit'
? [{ name: '💾 Save button on create/edit page', value: 'saveButton' }, new Separator()]
: []),
{ name: '⬆️ Before Breadcrumbs', value: 'beforeBreadcrumbs' },
{ name: '➡️ Before Action Buttons', value: 'beforeActionButtons' },
{ name: '⬇️ After Breadcrumbs', value: 'afterBreadcrumbs' },
Expand Down

This file was deleted.

16 changes: 8 additions & 8 deletions adminforth/dataConnectors/baseConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { suggestIfTypo } from "../modules/utils.js";
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from "../types/Common.js";
import { randomUUID } from "crypto";
import dayjs from "dayjs";
import { afLogger } from '../modules/logger.js';


export default class AdminForthBaseConnector implements IAdminForthDataSourceConnectorBase {

client: any;

get db() {
console.warn('.db is deprecated, use .client instead');
afLogger.warn('.db is deprecated, use .client instead');
return this.client;
}

Expand Down Expand Up @@ -80,7 +81,6 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
// in case column isArray and enumerator/foreign resource - IN filter must be transformed into OR filter
if (filterValidation.ok && f.operator == AdminForthFilterOperators.IN) {
const column = resource.dataSourceColumns.find((col) => col.name == (f as IAdminForthSingleFilter).field);
// console.log(`\n~~~ column: ${JSON.stringify(column, null, 2)}\n~~~ resource.columns: ${JSON.stringify(resource.dataSourceColumns, null, 2)}\n~~~ filter: ${JSON.stringify(f, null, 2)}\n`);
if (column.isArray?.enabled && (column.enum || column.foreignResource)) {
filters[fIndex] = {
operator: AdminForthFilterOperators.OR,
Expand Down Expand Up @@ -138,7 +138,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
);
}
if (isPolymorphicTarget) {
process.env.HEAVY_DEBUG && console.log(`⚠️ Field '${filtersAsSingle.field}' not found in polymorphic target resource '${resource.resourceId}', allowing query to proceed.`);
afLogger.trace(`⚠️ Field '${filtersAsSingle.field}' not found in polymorphic target resource '${resource.resourceId}', allowing query to proceed.`);
return { ok: true, error: '' };
} else {
throw new Error(`Field '${filtersAsSingle.field}' not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
Expand Down Expand Up @@ -329,7 +329,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
}

async checkUnique(resource: AdminForthResource, column: AdminForthResourceColumn, value: any, record?: any): Promise<boolean> {
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 checkUnique|||', column, value);
afLogger.trace(`☝️🪲🪲🪲🪲 checkUnique||| ${column.name}, ${value}`);

const primaryKeyField = this.getPrimaryKey(resource);
const existingRecord = await this.getData({
Expand Down Expand Up @@ -385,11 +385,11 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
})
);
if (error) {
process.env.HEAVY_DEBUG && console.log('🪲🆕 check unique error', error);
afLogger.trace(`🪲🆕 check unique error, ${error}`);
return { error, ok: false };
}

process.env.HEAVY_DEBUG && console.log('🪲🆕 creating record',JSON.stringify(recordWithOriginalValues));
afLogger.trace(`🪲🆕 creating record, ${JSON.stringify(recordWithOriginalValues)}`);
let pkValue = await this.createRecordOriginalValues({ resource, record: recordWithOriginalValues });
if (recordWithOriginalValues[this.getPrimaryKey(resource)] !== undefined) {
// some data sources always return some value for pk, even if it is was not auto generated
Expand Down Expand Up @@ -441,12 +441,12 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
})
);
if (error) {
process.env.HEAVY_DEBUG && console.log('🪲🆕 check unique error', error);
afLogger.trace(`🪲🆕 check unique error, ${error}`);
return { error, ok: false };
}


process.env.HEAVY_DEBUG && console.log(`🪲✏️ updating record id:${recordId}, values: ${JSON.stringify(recordWithOriginalValues)}`);
afLogger.trace(`🪲✏️ updating record id:${recordId}, values: ${JSON.stringify(recordWithOriginalValues)}`);

await this.updateRecordOriginalValues({ resource, recordId, newValues: recordWithOriginalValues });

Expand Down
7 changes: 4 additions & 3 deletions adminforth/dataConnectors/clickhouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dayjs from 'dayjs';
import { createClient } from '@clickhouse/client'

import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/Common.js';
import { afLogger } from '../modules/logger.js';

class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector {

Expand Down Expand Up @@ -95,7 +96,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
});
rows = await q.json();
} catch (e) {
console.error(` 🛑Error connecting to datasource URL ${this.url}:`, e);
afLogger.error(` 🛑Error connecting to datasource URL ${this.url}: ${e}`);
return null;
}

Expand Down Expand Up @@ -169,7 +170,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
return {'error': `Failed to parse JSON: ${e.message}`}
}
} else {
console.error(`AdminForth: JSON field is not a string but ${field._underlineType}, this is not supported yet`);
afLogger.error(`AdminForth: JSON field is not a string but ${field._underlineType}, this is not supported yet`);
}
}
return value;
Expand Down Expand Up @@ -197,7 +198,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
if (field._underlineType.startsWith('String') || field._underlineType.startsWith('FixedString')) {
return JSON.stringify(value);
} else {
console.error(`AdminForth: JSON field is not a string/text but ${field._underlineType}, this is not supported yet`);
afLogger.error(`AdminForth: JSON field is not a string/text but ${field._underlineType}, this is not supported yet`);
}
}

Expand Down
10 changes: 5 additions & 5 deletions adminforth/dataConnectors/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MongoClient } from 'mongodb';
import { Decimal128, Double } from 'bson';
import { IAdminForthDataSourceConnector, IAdminForthSingleFilter, IAdminForthAndOrFilter, AdminForthResource } from '../types/Back.js';
import AdminForthBaseConnector from './baseConnector.js';

import { afLogger } from '../modules/logger.js';
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, } from '../types/Common.js';

const escapeRegex = (value) => {
Expand Down Expand Up @@ -36,11 +36,11 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS
try {
await this.client.connect();
this.client.on('error', (err) => {
console.log('Mongo error: ', err.message)
afLogger.error(`Mongo error: ${err.message}`);
});
console.log('Connected to Mongo');
afLogger.info('Connected to Mongo');
} catch (e) {
console.error(`Failed to connect to Mongo: ${e}`);
afLogger.error(`Failed to connect to Mongo: ${e}`);
}
})();
}
Expand Down Expand Up @@ -262,7 +262,7 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS

// explicitly ignore raw SQL filters for MongoDB
if ((filter as IAdminForthSingleFilter).insecureRawSQL !== undefined) {
console.warn('⚠️ Ignoring insecureRawSQL filter for MongoDB:', (filter as IAdminForthSingleFilter).insecureRawSQL);
afLogger.warn(`⚠️ Ignoring insecureRawSQL filter for MongoDB:, ${(filter as IAdminForthSingleFilter).insecureRawSQL}`);
return {};
}

Expand Down
33 changes: 12 additions & 21 deletions adminforth/dataConnectors/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AdminForthResource, IAdminForthSingleFilter, IAdminForthAndOrFilter, IA
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, } from '../types/Common.js';
import AdminForthBaseConnector from './baseConnector.js';
import mysql from 'mysql2/promise';
import { dbLogger, afLogger } from '../modules/logger.js';

class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector {

Expand All @@ -15,7 +16,7 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
queueLimit: 0
});
} catch (e) {
console.error(`Failed to connect to MySQL: ${e}`);
afLogger.error(`Failed to connect to MySQL: ${e}`);
}
}

Expand Down Expand Up @@ -175,8 +176,8 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
} else if (typeof value === 'object') {
return value;
} else {
console.error('JSON field value is not string or object, but has type:', typeof value);
console.error('Field:', field);
afLogger.error(`JSON field value is not string or object, but has type: ${typeof value}`);
afLogger.error(`Field:, ${field}`);
return {}
}
}
Expand Down Expand Up @@ -317,9 +318,9 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
if (orderBy) selectQuery += ` ${orderBy}`;
if (limit) selectQuery += ` LIMIT ${limit}`;
if (offset) selectQuery += ` OFFSET ${offset}`;
if (process.env.HEAVY_DEBUG_QUERY) {
console.log('🪲📜 MySQL Q:', selectQuery, 'values:', filterValues);
}

dbLogger.trace(`🪲📜 MySQL Q: ${selectQuery} values: ${JSON.stringify(filterValues)}`);

const [results] = await this.client.execute(selectQuery, filterValues);
return results.map((row) => {
const newRow = {};
Expand All @@ -341,9 +342,7 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
}
const { sql: where, values: filterValues } = this.whereClauseAndValues(filters);
const q = `SELECT COUNT(*) FROM ${tableName} ${where}`;
if (process.env.HEAVY_DEBUG_QUERY) {
console.log('🪲📜 MySQL Q:', q, 'values:', filterValues);
}
dbLogger.trace(`🪲📜 MySQL Q: ${q} values: ${JSON.stringify(filterValues)}`);
const [results] = await this.client.execute(q, filterValues);
return +results[0]["COUNT(*)"];
}
Expand All @@ -353,9 +352,7 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
const result = {};
await Promise.all(columns.map(async (col) => {
const q = `SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`;
if (process.env.HEAVY_DEBUG_QUERY) {
console.log('🪲📜 MySQL Q:', q);
}
dbLogger.trace(`🪲📜 MySQL Q: ${q}`);
const [results] = await this.client.execute(q);
const { min, max } = results[0];
result[col.name] = {
Expand All @@ -371,9 +368,7 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
const placeholders = columns.map(() => '?').join(', ');
const values = columns.map((colName) => typeof record[colName] === 'undefined' ? null : record[colName]);
const q = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
if (process.env.HEAVY_DEBUG_QUERY) {
console.log('🪲📜 MySQL Q:', q, 'values:', values);
}
dbLogger.trace(`🪲📜 MySQL Q: ${q} values: ${JSON.stringify(values)}`);
const ret = await this.client.execute(q, values);
return ret.insertId;
}
Expand All @@ -382,17 +377,13 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
const values = [...Object.values(newValues), recordId];
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `${col} = ?`).join(', ');
const q = `UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`;
if (process.env.HEAVY_DEBUG_QUERY) {
console.log('🪲📜 MySQL Q:', q, 'values:', values);
}
dbLogger.trace(`🪲📜 MySQL Q: ${q} values: ${JSON.stringify(values)}`);
await this.client.execute(q, values);
}

async deleteRecord({ resource, recordId }): Promise<boolean> {
const q = `DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`;
if (process.env.HEAVY_DEBUG_QUERY) {
console.log('🪲📜 MySQL Q:', q, 'values:', [recordId]);
}
dbLogger.trace(`🪲📜 MySQL Q: ${q} values: ${JSON.stringify([recordId])}`);
const res = await this.client.execute(q, [recordId]);
return res.rowCount > 0;
}
Expand Down
Loading