diff --git a/adminforth/auth.ts b/adminforth/auth.ts index 6faff95e0..6c8416f80 100644 --- a/adminforth/auth.ts +++ b/adminforth/auth.ts @@ -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') { @@ -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; } @@ -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; } diff --git a/adminforth/basePlugin.ts b/adminforth/basePlugin.ts index cbb3b37fa..82229b325 100644 --- a/adminforth/basePlugin.ts +++ b/adminforth/basePlugin.ts @@ -6,6 +6,8 @@ import fs from 'fs'; import crypto from 'crypto'; +import { afLogger } from './modules/logger.js'; + export default class AdminForthPlugin implements IAdminForthPlugin { @@ -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; } @@ -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; } diff --git a/adminforth/commands/createCustomComponent/main.js b/adminforth/commands/createCustomComponent/main.js index 2b62eb65c..243a8c666 100644 --- a/adminforth/commands/createCustomComponent/main.js +++ b/adminforth/commands/createCustomComponent/main.js @@ -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' }, diff --git a/adminforth/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs b/adminforth/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs deleted file mode 100644 index 06ed5e2ac..000000000 --- a/adminforth/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +++ /dev/null @@ -1,28 +0,0 @@ - - - - - diff --git a/adminforth/dataConnectors/baseConnector.ts b/adminforth/dataConnectors/baseConnector.ts index eed448b8e..d53233753 100644 --- a/adminforth/dataConnectors/baseConnector.ts +++ b/adminforth/dataConnectors/baseConnector.ts @@ -10,6 +10,7 @@ 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 { @@ -17,7 +18,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon client: any; get db() { - console.warn('.db is deprecated, use .client instead'); + afLogger.warn('.db is deprecated, use .client instead'); return this.client; } @@ -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, @@ -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}'?` : ''}`); @@ -329,7 +329,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon } async checkUnique(resource: AdminForthResource, column: AdminForthResourceColumn, value: any, record?: any): Promise { - 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({ @@ -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 @@ -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 }); diff --git a/adminforth/dataConnectors/clickhouse.ts b/adminforth/dataConnectors/clickhouse.ts index 0e848c378..798682647 100644 --- a/adminforth/dataConnectors/clickhouse.ts +++ b/adminforth/dataConnectors/clickhouse.ts @@ -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 { @@ -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; } @@ -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; @@ -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`); } } diff --git a/adminforth/dataConnectors/mongo.ts b/adminforth/dataConnectors/mongo.ts index d6b58f786..94487e26a 100644 --- a/adminforth/dataConnectors/mongo.ts +++ b/adminforth/dataConnectors/mongo.ts @@ -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) => { @@ -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}`); } })(); } @@ -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 {}; } diff --git a/adminforth/dataConnectors/mysql.ts b/adminforth/dataConnectors/mysql.ts index 2d2179c10..ed97a8563 100644 --- a/adminforth/dataConnectors/mysql.ts +++ b/adminforth/dataConnectors/mysql.ts @@ -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 { @@ -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}`); } } @@ -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 {} } } @@ -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 = {}; @@ -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(*)"]; } @@ -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] = { @@ -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; } @@ -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 { 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; } diff --git a/adminforth/dataConnectors/postgres.ts b/adminforth/dataConnectors/postgres.ts index bae17dea8..639a29bc3 100644 --- a/adminforth/dataConnectors/postgres.ts +++ b/adminforth/dataConnectors/postgres.ts @@ -3,6 +3,8 @@ import { AdminForthResource, IAdminForthSingleFilter, IAdminForthAndOrFilter, IA import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, } from '../types/Common.js'; import AdminForthBaseConnector from './baseConnector.js'; import pkg from 'pg'; +import { afLogger, dbLogger } from '../modules/logger.js'; + const { Client } = pkg; @@ -15,13 +17,13 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa try { await this.client.connect(); this.client.on('error', async (err) => { - console.log('Postgres error: ', err.message, err.stack) + afLogger.error(`Postgres error: ${err.message} ${err.stack}`); this.client.end(); await new Promise((resolve) => { setTimeout(resolve, 1000) }); this.setupClient(url); }); } catch (e) { - console.error(`Failed to connect to Postgres ${e}`); + afLogger.error(`Failed to connect to Postgres ${e}`); } } @@ -198,8 +200,8 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa } 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 {} } } @@ -351,9 +353,7 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa const d = [...filterValues, limit, offset]; const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `"${s.field}" ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : ''; const selectQuery = `SELECT ${columns} FROM "${tableName}" ${where} ${orderBy} ${limitOffset}`; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ PG Q:', selectQuery, 'params:', d); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ PG Q: ${selectQuery}, params: ${JSON.stringify(d)}`); const stmt = await this.client.query(selectQuery, d); const rows = stmt.rows; return rows.map((row) => { @@ -376,9 +376,7 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa } const { sql: where, values: filterValues } = this.whereClauseAndValues(resource, filters); const q = `SELECT COUNT(*) FROM "${tableName}" ${where}`; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ PG Q:', q, 'values:', filterValues); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ PG Q: ${q}, values: ${JSON.stringify(filterValues)}`); const stmt = await this.client.query(q, filterValues); return +stmt.rows[0].count; } @@ -388,9 +386,7 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa 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('๐Ÿชฒ๐Ÿ“œ PG Q:', q); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ PG Q: ${q}`); const stmt = await this.client.query(q); const { min, max } = stmt.rows[0]; result[col.name] = { @@ -410,11 +406,7 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa } const primaryKey = this.getPrimaryKey(resource); const q = `INSERT INTO "${tableName}" (${columns.join(', ')}) VALUES (${placeholders}) RETURNING "${primaryKey}"`; - // console.log('\n๐Ÿ”ต [PG INSERT]:', q); - // console.log('๐Ÿ“ฆ [VALUES]:', JSON.stringify(values, null, 2)); - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ PG Q:', q, 'values:', values); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ PG Q: ${q}, values: ${JSON.stringify(values)}`); const ret = await this.client.query(q, values); return ret.rows[0][primaryKey]; } @@ -423,17 +415,13 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa const values = [...Object.values(newValues), recordId]; const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', '); const q = `UPDATE "${resource.table}" SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ PG Q:', q, 'values:', values); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ PG Q: ${q}, values: ${JSON.stringify(values)}`); await this.client.query(q, values); } async deleteRecord({ resource, recordId }): Promise { const q = `DELETE FROM "${resource.table}" WHERE "${this.getPrimaryKey(resource)}" = $1`; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ PG Q:', q, 'values:', [recordId]); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ PG Q: ${q}, values: ${JSON.stringify([recordId])}`); const res = await this.client.query(q, [recordId]); return res.rowCount > 0; } diff --git a/adminforth/dataConnectors/sqlite.ts b/adminforth/dataConnectors/sqlite.ts index 1b8b8f30e..5538e23fc 100644 --- a/adminforth/dataConnectors/sqlite.ts +++ b/adminforth/dataConnectors/sqlite.ts @@ -3,6 +3,7 @@ import { IAdminForthDataSourceConnector, IAdminForthSingleFilter, IAdminForthAnd import AdminForthBaseConnector from './baseConnector.js'; import dayjs from 'dayjs'; import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/Common.js'; +import { dbLogger, afLogger } from '../modules/logger.js'; class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector { @@ -122,7 +123,7 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData return {'error': `Failed to parse JSON: ${e.message}`} } } 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`); } } @@ -156,7 +157,7 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData if (field._underlineType == 'text' || field._underlineType == 'varchar') { 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`); } } @@ -287,9 +288,7 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData const stmt = this.client.prepare(q); const d = [...filterValues, limit, offset]; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ SQLITE Q', q, 'params:', d); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ SQLITE Q: ${q}, params: ${JSON.stringify(d)}`); const rows = await stmt.all(d); return rows.map((row) => { @@ -313,9 +312,7 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData const where = this.whereClause(filters); const filterValues = this.getFilterParams(filters); const q = `SELECT COUNT(*) FROM ${tableName} ${where}`; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ SQLITE Q', q, 'params:', filterValues); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ SQLITE Q: ${q}, params: ${JSON.stringify(filterValues)}`); const totalStmt = this.client.prepare(q); return +totalStmt.get([...filterValues])['COUNT(*)']; } @@ -338,14 +335,9 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData const columns = Object.keys(record); const placeholders = columns.map(() => '?').join(', '); const values = columns.map((colName) => record[colName]); - // const q = this.client.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`); const sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`; - //console.log('\n๐ŸŸข [SQLITE INSERT]:', sql); - //console.log('๐Ÿ“ฆ [VALUES]:', JSON.stringify(values, null, 2)); const q = this.client.prepare(sql); - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ SQL Q:', q, 'values:', values); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ SQLITE Q: ${sql}, values: ${JSON.stringify(values)}`); const ret = await q.run(values); return ret.lastInsertRowid; } @@ -354,9 +346,7 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData const columnsWithPlaceholders = Object.keys(newValues).map((col) => `${col} = ?`); const values = [...Object.values(newValues), recordId]; const q = `UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`; - if (process.env.HEAVY_DEBUG_QUERY) { - console.log('๐Ÿชฒ๐Ÿ“œ SQLITE Q', q, 'params:', values); - } + dbLogger.trace(`๐Ÿชฒ๐Ÿ“œ SQLITE Q: ${q}, params: ${JSON.stringify(values)}`); const query = this.client.prepare(q); await query.run(values); } diff --git a/adminforth/documentation/blog/2024-10-01-ai-blog/index.md b/adminforth/documentation/blog/2024-10-01-ai-blog/index.md index 8a294008b..291b4fcb7 100644 --- a/adminforth/documentation/blog/2024-10-01-ai-blog/index.md +++ b/adminforth/documentation/blog/2024-10-01-ai-blog/index.md @@ -212,7 +212,7 @@ Open `index.ts` file in root directory and update it with the following content: ```ts title="./index.ts" import express from 'express'; -import AdminForth, { Filters, Sorts } from 'adminforth'; +import AdminForth, { Filters, Sorts, logger } from 'adminforth'; import userResource from './resources/adminuser.js'; import postResource from './resources/posts.js'; import contentImageResource from './resources/content-image.js'; @@ -287,7 +287,7 @@ if (import.meta.url === `file://${process.argv[1]}`) { const port = 3500; admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development' }).then(() => { - console.log('Bundling AdminForth SPA done.'); + logger.info('Bundling AdminForth SPA done.'); }); // api to server recent posts @@ -343,7 +343,7 @@ if (import.meta.url === `file://${process.argv[1]}`) { }); admin.express.listen(port, () => { - console.log(`\nโšก AdminForth is available at http://localhost:${port}/admin\n`) + logger.info(`\nโšก AdminForth is available at http://localhost:${port}/admin\n`) }); } ``` diff --git a/adminforth/documentation/docs/tutorial/01-helloWorld.md b/adminforth/documentation/docs/tutorial/01-helloWorld.md index 24db51226..f5a79f3d5 100644 --- a/adminforth/documentation/docs/tutorial/01-helloWorld.md +++ b/adminforth/documentation/docs/tutorial/01-helloWorld.md @@ -131,7 +131,7 @@ Create `index.ts` file in root directory with following content: ```ts title="./index.ts" import express from 'express'; -import AdminForth, { AdminForthDataTypes, Filters } from 'adminforth'; +import AdminForth, { AdminForthDataTypes, Filters, logger } from 'adminforth'; import type { AdminForthResourceInput, AdminForthResource, AdminUser } from 'adminforth'; export const admin = new AdminForth({ @@ -218,7 +218,7 @@ export const admin = new AdminForth({ }, edit: { beforeSave: async ({ oldRecord, updates, adminUser, resource }: { oldRecord: any, updates: any, adminUser: AdminUser, resource: AdminForthResource }) => { - console.log('Updating user', updates); + logger.info('Updating user', updates); if (oldRecord.id === adminUser.dbUser.id && updates.role) { return { ok: false, error: 'You cannot change your own role' }; } @@ -321,7 +321,7 @@ if (import.meta.url === `file://${process.argv[1]}`) { // needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development' }).then(() => { - console.log('Bundling AdminForth SPA done.'); + logger.info('Bundling AdminForth SPA done.'); }); // serve after you added all api @@ -338,7 +338,7 @@ if (import.meta.url === `file://${process.argv[1]}`) { }); admin.express.listen(port, () => { - console.log(`\nโšก AdminForth is available at http://localhost:${port}\n`) + logger.info(`\nโšก AdminForth is available at http://localhost:${port}\n`) }); } ``` diff --git a/adminforth/documentation/docs/tutorial/03-Customization/09-Actions.md b/adminforth/documentation/docs/tutorial/03-Customization/09-Actions.md index 1d0a595cd..8e50b4897 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/09-Actions.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/09-Actions.md @@ -22,7 +22,7 @@ Here's how to add a custom action: // Handler function when action is triggered action: async ({ recordId, adminUser }) => { - console.log("auto submit", recordId, adminUser); + logger.info("auto submit", recordId, adminUser); return { ok: true, successMessage: "Auto submitted" diff --git a/adminforth/documentation/docs/tutorial/03-Customization/16-websocket.md b/adminforth/documentation/docs/tutorial/03-Customization/16-websocket.md index 497fb4c42..38b18d7ed 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/16-websocket.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/16-websocket.md @@ -7,10 +7,11 @@ In two words, to subscribe to a topic from any frontend component you need to do ```javascript import websocket from '@/websocket'; +import { logger } from 'adminforth' websocket.subscribe('/topic-name', (data) => { // this callback called when we receive publish in topic from the websocket - console.log(data); + logger.info(data); }); ``` @@ -192,7 +193,7 @@ const admin = new AdminForth({ //diff-add const [subject, param] = /^\/(.+?)\/(.+)/.exec(topic)!.slice(1); //diff-add - console.log(`Websocket user ${adminUser.username} tries to subscribe to topic ${subject} with param ${param}`); + logger.info(`Websocket user ${adminUser.username} tries to subscribe to topic ${subject} with param ${param}`); //diff-add if (subject === 'property-cost') { //diff-add diff --git a/adminforth/documentation/docs/tutorial/05-Plugins/15-email-invite.md b/adminforth/documentation/docs/tutorial/05-Plugins/15-email-invite.md index 261d1fce3..3fde70097 100644 --- a/adminforth/documentation/docs/tutorial/05-Plugins/15-email-invite.md +++ b/adminforth/documentation/docs/tutorial/05-Plugins/15-email-invite.md @@ -31,6 +31,7 @@ To Setup SES, you need to have an AWS account and SES service enabled. You can f ```typescript title="./resources/adminuser.ts" import EmailInvitePlugin from '@adminforth/email-invite'; import EmailAdapterAwsSes from '@adminforth/email-adapter-aws-ses'; +import { logger } from 'adminforth' export default { dataSource: 'maindb', @@ -77,7 +78,7 @@ export default { }, edit: { beforeSave: async ({ oldRecord, updates, adminUser, resource }: { oldRecord: any, updates: any, adminUser: AdminUser, resource: AdminForthResource }) => { - console.log('Updating user', updates); + logger.info('Updating user', updates); if (oldRecord.id === adminUser.dbUser.id && updates.role) { return { ok: false, error: 'You cannot change your own role' }; } diff --git a/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md b/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md index 6f2ea4b62..7ab89bf15 100644 --- a/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md +++ b/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md @@ -31,6 +31,7 @@ To Setup SES, you need to have an AWS account and SES service enabled. You can f ```typescript title="./resources/adminuser.ts" import EmailInvitePlugin from '@adminforth/email-invite'; import EmailAdapterAwsSes from '@adminforth/email-adapter-aws-ses'; +import { logger } from 'adminforth' export default { dataSource: 'maindb', @@ -77,7 +78,7 @@ export default { }, edit: { beforeSave: async ({ oldRecord, updates, adminUser, resource }: { oldRecord: any, updates: any, adminUser: AdminUser, resource: AdminForthResource }) => { - console.log('Updating user', updates); + logger.info('Updating user', updates); if (oldRecord.id === adminUser.dbUser.id && updates.role) { return { ok: false, error: 'You cannot change your own role' }; } diff --git a/adminforth/index.ts b/adminforth/index.ts index 25b48752c..225e04c18 100644 --- a/adminforth/index.ts +++ b/adminforth/index.ts @@ -34,6 +34,8 @@ import AdminForthRestAPI, { interpretResource } from './modules/restApi.js'; import ClickhouseConnector from './dataConnectors/clickhouse.js'; import OperationalResource from './modules/operationalResource.js'; import SocketBroker from './modules/socketBroker.js'; +import { afLogger } from './modules/logger.js'; +export { logger } from './modules/logger.js'; // exports export * from './types/Back.js'; @@ -124,62 +126,62 @@ class AdminForth implements IAdminForth { } constructor(config: AdminForthInputConfig) { - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง AdminForth constructor started'); + afLogger.trace('๐Ÿ”ง AdminForth constructor started'); if (global.adminforth) { throw new Error('AdminForth instance already created in this process. '+ 'If you want to use multiple instances, consider using different process for each instance'); } - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Creating CodeInjector...'); + afLogger.trace('๐Ÿ”ง Creating CodeInjector...'); this.codeInjector = new CodeInjector(this); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง CodeInjector created'); + afLogger.trace('๐Ÿ”ง CodeInjector created'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Creating ConfigValidator...'); + afLogger.trace('๐Ÿ”ง Creating ConfigValidator...'); this.configValidator = new ConfigValidator(this, config); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง ConfigValidator created'); + afLogger.trace('๐Ÿ”ง ConfigValidator created'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Creating AdminForthRestAPI...'); + afLogger.trace('๐Ÿ”ง Creating AdminForthRestAPI...'); this.restApi = new AdminForthRestAPI(this); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง AdminForthRestAPI created'); + afLogger.trace('๐Ÿ”ง AdminForthRestAPI created'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Creating SocketBroker...'); + afLogger.trace('๐Ÿ”ง Creating SocketBroker...'); this.websocket = new SocketBroker(this); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง SocketBroker created'); + afLogger.trace('๐Ÿ”ง SocketBroker created'); this.activatedPlugins = []; - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Validating config...'); + afLogger.trace('๐Ÿ”ง Validating config...'); this.configValidator.validateConfig(); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Config validated'); + afLogger.trace('๐Ÿ”ง Config validated'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Activating plugins...'); + afLogger.trace('๐Ÿ”ง Activating plugins...'); this.activatePlugins(); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Plugins activated'); + afLogger.trace('๐Ÿ”ง Plugins activated'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Validating after plugin activation...'); + afLogger.trace('๐Ÿ”ง Validating after plugin activation...'); this.configValidator.validateAfterPluginsActivation(); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Config validated'); + afLogger.trace('๐Ÿ”ง Config validated'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Creating ExpressServer...'); + afLogger.trace('๐Ÿ”ง Creating ExpressServer...'); this.express = new ExpressServer(this); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง ExpressServer created'); + afLogger.trace('๐Ÿ”ง ExpressServer created'); // this.fastify = new FastifyServer(this); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Creating AdminForthAuth...'); + afLogger.trace('๐Ÿ”ง Creating AdminForthAuth...'); this.auth = new AdminForthAuth(this); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง AdminForthAuth created'); + afLogger.trace('๐Ÿ”ง AdminForthAuth created'); this.connectors = {}; this.statuses = { dbDiscover: 'running', }; - console.log(`${this.formatAdminForth()} v${ADMINFORTH_VERSION} initializing...`); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง About to set global.adminforth...'); + afLogger.info(`${this.formatAdminForth()} v${ADMINFORTH_VERSION} initializing...`); + afLogger.trace('๐Ÿ”ง About to set global.adminforth...'); global.adminforth = this; - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง global.adminforth set successfully'); - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง AdminForth constructor completed'); + afLogger.trace('๐Ÿ”ง global.adminforth set successfully'); + afLogger.trace('๐Ÿ”ง AdminForth constructor completed'); } formatAdminForth() { @@ -202,16 +204,16 @@ class AdminForth implements IAdminForth { } activatePlugins() { - process.env.HEAVY_DEBUG && console.log('๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ Activating plugins'); + afLogger.trace('๐Ÿ”Œ๐Ÿ”Œ๐Ÿ”Œ Activating plugins'); const allPluginInstances = []; for (let resource of this.config.resources) { - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Checking plugins for resource: ${resource.resourceId}`); + afLogger.trace(`๐Ÿ”Œ Checking plugins for resource: ${resource.resourceId}`); for (let pluginInstance of resource.plugins || []) { - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Found plugin: ${pluginInstance.constructor.name} for resource ${resource.resourceId}`); + afLogger.trace(`๐Ÿ”Œ Found plugin: ${pluginInstance.constructor.name} for resource ${resource.resourceId}`); allPluginInstances.push({pi: pluginInstance, resource}); } } - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Total plugins to activate: ${allPluginInstances.length}`); + afLogger.trace(`๐Ÿ”Œ Total plugins to activate: ${allPluginInstances.length}`); let activationLoopCounter = 0; while (true) { @@ -219,8 +221,8 @@ class AdminForth implements IAdminForth { if (activationLoopCounter > 10) { throw new Error('Plugin activation loop exceeded 10 iterations, possible infinite loop (some plugin tries to activate himself in a loop)'); } - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Plugin activation loop iteration: ${activationLoopCounter}`); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Activated plugins count: ${this.activatedPlugins.length}/${allPluginInstances.length}`); + afLogger.trace(`๐Ÿ”Œ Plugin activation loop iteration: ${activationLoopCounter}`); + afLogger.trace(`๐Ÿ”Œ Activated plugins count: ${this.activatedPlugins.length}/${allPluginInstances.length}`); const allPluginsAreActivated = allPluginInstances.length === this.activatedPlugins.length; if (allPluginsAreActivated) { break; @@ -230,33 +232,33 @@ class AdminForth implements IAdminForth { !this.activatedPlugins.find((p) => p.pluginInstanceId === pluginInstance.pluginInstanceId) ); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Unactivated plugins remaining: ${unactivatedPlugins.length}`); + afLogger.trace(`๐Ÿ”Œ Unactivated plugins remaining: ${unactivatedPlugins.length}`); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Unactivated plugins count: ${unactivatedPlugins.length}`); + afLogger.trace(`๐Ÿ”Œ Unactivated plugins count: ${unactivatedPlugins.length}`); unactivatedPlugins.sort(({pi: a}, {pi: b}) => a.activationOrder - b.activationOrder); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Activating plugins in order:`, unactivatedPlugins.map(({pi}) => pi.constructor.name)); + afLogger.trace(`๐Ÿ”Œ Activating plugins in order: ${unactivatedPlugins.map(({pi}) => pi.constructor.name)}`); unactivatedPlugins.forEach( ({pi: pluginInstance, resource}, index) => { - process.env.HEAVY_DEBUG && console.log("Activating plugin:",pluginInstance.constructor.name) - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Activating plugin ${index + 1}/${allPluginInstances.length}: ${pluginInstance.constructor.name} for resource ${resource.resourceId}`); + afLogger.trace(`Activating plugin: ${pluginInstance.constructor.name}`); + afLogger.trace(`๐Ÿ”Œ Activating plugin ${index + 1}/${allPluginInstances.length}: ${pluginInstance.constructor.name} for resource ${resource.resourceId}`); pluginInstance.modifyResourceConfig(this, resource, allPluginInstances); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Plugin ${pluginInstance.constructor.name} modifyResourceConfig completed`); + afLogger.trace(`๐Ÿ”Œ Plugin ${pluginInstance.constructor.name} modifyResourceConfig completed`); const plugin = this.activatedPlugins.find((p) => p.pluginInstanceId === pluginInstance.pluginInstanceId); if (plugin) { - process.env.HEAVY_DEBUG && console.log(`Current plugin pluginInstance.pluginInstanceId ${pluginInstance.pluginInstanceId}`); + afLogger.trace(`Current plugin pluginInstance.pluginInstanceId ${pluginInstance.pluginInstanceId}`); throw new Error(`Attempt to activate Plugin ${pluginInstance.constructor.name} second time for same resource, but plugin does not support it. To support multiple plugin instance pre one resource, plugin should return unique string values for each installation from instanceUniqueRepresentation`); } this.activatedPlugins.push(pluginInstance); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œ Plugin ${pluginInstance.constructor.name} activated successfully`); + afLogger.trace(`๐Ÿ”Œ Plugin ${pluginInstance.constructor.name} activated successfully`); } ); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Œactivated plugins:`, this.activatedPlugins.map((pi) => pi.constructor.name)); + afLogger.trace(`๐Ÿ”Œactivated plugins: ${this.activatedPlugins.map((pi) => pi.constructor.name)}`); } - process.env.HEAVY_DEBUG && console.log('๐Ÿ”Œ All plugins activation completed'); + afLogger.trace('๐Ÿ”Œ All plugins activation completed'); } getPluginsByClassName(className: string): T[] { @@ -382,7 +384,7 @@ class AdminForth implements IAdminForth { try { await this.connectors[dataSourceId].setupClient(this.config.dataSources.find((ds) => ds.id === dataSourceId).url); } catch (e) { - console.error(`Error while connecting to datasource '${dataSourceId}':`, e); + afLogger.error(`Error while connecting to datasource '${dataSourceId}': ${e}`); } })); @@ -397,13 +399,13 @@ class AdminForth implements IAdminForth { try { fieldTypes = await this.connectors[res.dataSource].discoverFields(res); } catch (e) { - console.error(`Error discovering fields for resource '${res.table}' (In resource '${res.resourceId}')`, e); + afLogger.error(`Error discovering fields for resource '${res.table}' (In resource '${res.resourceId}') ${e}`); } if (fieldTypes !== null && !Object.keys(fieldTypes).length) { throw new Error(`Table '${res.table}' (In resource '${res.resourceId}') has no fields or does not exist`); } if (fieldTypes === null) { - console.error(`โ›” DataSource ${res.dataSource} was not able to perform field discovery. It will not work properly`); + afLogger.error(`โ›” DataSource ${res.dataSource} was not able to perform field discovery. It will not work properly`); if (process.env.NODE_ENV === 'production') { process.exit(1); } @@ -440,13 +442,11 @@ class AdminForth implements IAdminForth { this.operationalResources[resource.resourceId] = new OperationalResource(this.connectors[resource.dataSource], resource); }); - // console.log('โš™๏ธโš™๏ธโš™๏ธ Database discovery done', JSON.stringify(this.config.resources, null, 2)); } async getAllTables(): Promise<{ [dataSourceId: string]: string[] }> { const results: { [dataSourceId: string]: string[] } = {}; - // console.log('Connectors to process:', Object.keys(this.connectors)); if (!this.config.databaseConnectors) { this.config.databaseConnectors = {...this.connectorClasses}; } @@ -458,11 +458,10 @@ class AdminForth implements IAdminForth { const tables = await connector.getAllTables(); results[dataSourceId] = tables; } catch (err) { - console.error(`Error getting tables for dataSource ${dataSourceId}:`, err); + afLogger.error(`Error getting tables for dataSource ${dataSourceId}: ${err}`); results[dataSourceId] = []; } } else { - // console.log(`Connector ${dataSourceId} does not have getAllTables method`); results[dataSourceId] = []; } }) @@ -490,7 +489,7 @@ class AdminForth implements IAdminForth { isUUID: isProbablyUUIDColumn(column), })); } catch (err) { - console.error(`Error getting columns for table ${tableName} in dataSource ${dataSourceId}:`, err); + afLogger.error(`Error getting columns for table ${tableName} in dataSource ${dataSourceId}: ${err}`); results[dataSourceId] = []; } } else { @@ -551,7 +550,7 @@ class AdminForth implements IAdminForth { // execute hook if needed for (const hook of listify(resource.hooks?.create?.beforeSave)) { - console.log('๐Ÿชฒ Hook beforeSave', hook); + afLogger.debug(`๐Ÿชฒ Hook beforeSave ${hook}`); const resp = await hook({ resource, record, @@ -584,7 +583,7 @@ class AdminForth implements IAdminForth { } } const connector = this.connectors[resource.dataSource]; - process.env.HEAVY_DEBUG && console.log('๐Ÿชฒ๐Ÿ†• creating record createResourceRecord', record); + afLogger.trace(`๐Ÿชฒ๐Ÿ†• creating record createResourceRecord, record: ${JSON.stringify(record)}`); const { error, createdRecord } = await connector.createRecord({ resource, record, adminUser }); if ( error ) { return { error }; @@ -594,7 +593,7 @@ class AdminForth implements IAdminForth { // execute hook if needed for (const hook of listify(resource.hooks?.create?.afterSave)) { - process.env.HEAVY_DEBUG && console.log('๐Ÿชฒ Hook afterSave', hook); + afLogger.trace(`๐Ÿชฒ Hook afterSave ${hook}`); const resp = await hook({ recordId: primaryKey, resource, @@ -632,7 +631,7 @@ class AdminForth implements IAdminForth { } if (record) { - console.warn(`updateResourceRecord function received 'record' param which is deprecated and will be removed in future version, please use 'updates' instead.`); + afLogger.warn(`updateResourceRecord function received 'record' param which is deprecated and will be removed in future version, please use 'updates' instead.`); } // remove editReadonly columns from record diff --git a/adminforth/modules/codeInjector.ts b/adminforth/modules/codeInjector.ts index 987c12ab8..b40d10322 100644 --- a/adminforth/modules/codeInjector.ts +++ b/adminforth/modules/codeInjector.ts @@ -9,6 +9,7 @@ import AdminForth, { AdminForthConfigMenuItem } from '../index.js'; import { ADMIN_FORTH_ABSOLUTE_PATH, getComponentNameFromPath, transformObject, deepMerge, md5hash, slugifyString } from './utils.js'; import { ICodeInjector } from '../types/Back.js'; import { StylesGenerator } from './styleGenerator.js'; +import { afLogger } from '../modules/logger.js'; let TMP_DIR; @@ -64,9 +65,9 @@ function hashify(obj) { } function notifyWatcherIssue(limit) { - console.log('Ran out of file handles after watching %s files.', limit); - console.log('Falling back to polling which uses more CPU.'); - console.log('Run ulimit -n 10000 to increase the limit for open files.'); + afLogger.info('Ran out of file handles after watching %s files.', limit); + afLogger.info('Falling back to polling which uses more CPU.'); + afLogger.info('Run ulimit -n 10000 to increase the limit for open files.'); } class CodeInjector implements ICodeInjector { @@ -91,7 +92,7 @@ class CodeInjector implements ICodeInjector { } cleanup() { - console.log('Cleaning up...'); + afLogger.info('Cleaning up...'); this.allWatchers.forEach((watcher) => { watcher.removeAll(); }); @@ -107,14 +108,6 @@ class CodeInjector implements ICodeInjector { } - // async runShell({ command }) { - // console.log(`โš™๏ธ Running shell ${command}...`); - // console.time(`${command} done in`); - // const { stdout: out, stderr: err } = await execAsync(command); - // console.timeEnd(`${command} done in`); - // console.log(`Command ${command} output:`, out, err); - // } - async runNpmShell({command, cwd, envOverrides = {}}: { command: string, cwd: string, @@ -131,9 +124,9 @@ class CodeInjector implements ICodeInjector { ...envOverrides, }; - console.log(`โš™๏ธ exec: npm ${command}`); - process.env.HEAVY_DEBUG && console.log(`๐Ÿชฒ npm ${command} cwd:`, cwd); - process.env.HEAVY_DEBUG && console.time(`npm ${command} done in`); + afLogger.trace(`โš™๏ธ exec: npm ${command}`); + afLogger.trace(`๐Ÿชฒ npm ${command} cwd: ${cwd}`); + afLogger.trace(`npm ${command} done in`); // On Windows, execute npm.cmd directly; on Unix, use node + npm let execCommand: string; @@ -159,11 +152,10 @@ class CodeInjector implements ICodeInjector { } const { stdout: out, stderr: err } = await execAsync(execCommand, execOptions); - process.env.HEAVY_DEBUG && console.timeEnd(`npm ${command} done in`); + afLogger.trace(`npm ${command} done in`); - // process.env.HEAVY_DEBUG && console.log(`๐Ÿชฒ npm ${command} output:`, out); if (err) { - process.env.HEAVY_DEBUG && console.error(`๐Ÿชฒnpm ${command} errors/warnings:`, err); + afLogger.trace(`๐Ÿชฒnpm ${command} errors/warnings: ${err}`); } } @@ -243,9 +235,7 @@ class CodeInjector implements ICodeInjector { dereference: true, // needed to dereference types // preserveTimestamps: true, // needed to not invalidate any caches }); - if (process.env.HEAVY_DEBUG) { - console.log('๐Ÿชฒโš™๏ธ fsExtra.copy copy single file', src, dest); - } + afLogger.trace(`๐Ÿชฒโš™๏ธ fsExtra.copy copy single file, ${src}, ${dest}`); })); } async migrateLegacyCustomLayout(oldMeta) { @@ -350,9 +340,7 @@ class CodeInjector implements ICodeInjector { registerSettingPages(this.adminforth.config.auth.userMenuSettingsPages); const spaDir = this.getSpaDir(); - if (process.env.HEAVY_DEBUG) { - console.log(`๐Ÿชฒโš™๏ธ fsExtra.copy from ${spaDir} -> ${this.spaTmpPath()}`); - } + afLogger.trace(`๐Ÿชฒโš™๏ธ fsExtra.copy from ${spaDir} -> ${this.spaTmpPath()}`); // try to rm /src/types directory try { @@ -368,8 +356,8 @@ class CodeInjector implements ICodeInjector { // /adminforth/* used for local development and /dist/* used for production const filterPasses = !src.includes(`${path.sep}adminforth${path.sep}spa${path.sep}node_modules`) && !src.includes(`${path.sep}adminforth${path.sep}spa${path.sep}dist`) && !src.includes(`${path.sep}dist${path.sep}spa${path.sep}node_modules`) && !src.includes(`${path.sep}dist${path.sep}spa${path.sep}dist`); - if (process.env.HEAVY_DEBUG && !filterPasses) { - console.log('๐Ÿชฒโš™๏ธ fsExtra.copy filtered out', src); + if (!filterPasses) { + afLogger.trace(`๐Ÿชฒโš™๏ธ fsExtra.copy filtered out, ${src}`); } return filterPasses @@ -398,9 +386,7 @@ class CodeInjector implements ICodeInjector { for (const [src, dest] of Object.entries(this.srcFoldersToSync)) { const to = path.join(this.spaTmpPath(), 'src', 'custom', dest); - if (process.env.HEAVY_DEBUG) { - console.log(`๐Ÿชฒโš™๏ธ srcFoldersToSync: fsExtra.copy from ${src}, ${to}`); - } + afLogger.trace(`๐Ÿชฒโš™๏ธ srcFoldersToSync: fsExtra.copy from ${src}, ${to}`); await fsExtra.copy(src, to, { recursive: true, @@ -517,7 +503,6 @@ class CodeInjector implements ICodeInjector { this.allComponentNames[filePath] = componentName; }); - // console.log('๐Ÿ”ง Injecting code into Vue sources...', this.allComponentNames); let customComponentsImports = ''; for (const [targetPath, component] of Object.entries(this.allComponentNames)) { @@ -649,7 +634,7 @@ class CodeInjector implements ICodeInjector { // for every installed plugin generate packages for (const plugin of this.adminforth.activatedPlugins) { - process.env.HEAVY_DEBUG && console.log('๐Ÿ”ง Checking packages for plugin', plugin.constructor.name, plugin.customFolderPath); + afLogger.trace(`๐Ÿ”ง Checking packages for plugin, ${plugin.constructor.name}, ${plugin.customFolderPath}`); const [lockHash, packages] = await this.packagesFromNpm(plugin.customFolderPath); if (packages.length) { pluginPackages.push({ @@ -670,14 +655,14 @@ class CodeInjector implements ICodeInjector { try { const existingHash = await fs.promises.readFile(hashPath, 'utf-8'); if (existingHash === fullHash) { - process.env.HEAVY_DEBUG && console.log(`๐ŸชฒHashes match, skipping npm ci/install, from file: ${existingHash}, actual: ${fullHash}`); + afLogger.trace(`๐ŸชฒHashes match, skipping npm ci/install, from file: ${existingHash}, actual: ${fullHash}`); return; } else { - process.env.HEAVY_DEBUG && console.log(`๐Ÿชฒ Hashes do not match: from file: ${existingHash} actual: ${fullHash}, proceeding with npm ci/install`); + afLogger.trace(`๐Ÿชฒ Hashes do not match: from file: ${existingHash} actual: ${fullHash}, proceeding with npm ci/install`); } } catch (e) { // ignore - process.env.HEAVY_DEBUG && console.log('๐ŸชฒHash file does not exist, proceeding with npm ci/install', e); + afLogger.trace(`๐ŸชฒHash file does not exist, proceeding with npm ci/install, ${e}`); } await this.runNpmShell({command: 'ci', cwd: this.spaTmpPath(), envOverrides: { @@ -730,7 +715,7 @@ class CodeInjector implements ICodeInjector { }; await collectDirectories(spaPath); - process.env.HEAVY_DEBUG && console.log('๐Ÿชฒ๐Ÿ”Ž Watch for:', directories.join(',')); + afLogger.trace(`๐Ÿชฒ๐Ÿ”Ž Watch for: ${directories.join(',')}`); const watcher = filewatcher({ debounce: 30 }); directories.forEach((dir) => { @@ -739,7 +724,7 @@ class CodeInjector implements ICodeInjector { files.forEach((file) => { const fullPath = path.join(dir, file); if (fs.lstatSync(fullPath).isFile()) { - process.env.HEAVY_DEBUG && console.log(`๐Ÿชฒ๐Ÿ”Ž Watch for file ${fullPath}`); + afLogger.trace(`๐Ÿชฒ๐Ÿ”Ž Watch for file ${fullPath}`); watcher.add(fullPath); } }) @@ -748,7 +733,7 @@ class CodeInjector implements ICodeInjector { watcher.on( 'change', async (file) => { - process.env.HEAVY_DEBUG && console.log(`๐Ÿ› File ${file} changed (SPA), preparing sources...`); + afLogger.trace(`๐Ÿ› File ${file} changed (SPA), preparing sources...`); await this.updatePartials({ filesUpdated: [file.replace(spaPath + path.sep, '')] }); } ) @@ -764,7 +749,7 @@ class CodeInjector implements ICodeInjector { try { await fs.promises.access(customComponentsDir, fs.constants.F_OK); } catch (e) { - process.env.HEAVY_DEBUG && console.log(`๐ŸชฒCustom components dir ${customComponentsDir} does not exist, skipping watching`); + afLogger.trace(`๐ŸชฒCustom components dir ${customComponentsDir} does not exist, skipping watching`); return; } @@ -796,27 +781,25 @@ class CodeInjector implements ICodeInjector { const watcher = filewatcher({ debounce: 30 }); files.forEach((file) => { - process.env.HEAVY_DEBUG && console.log(`๐Ÿชฒ๐Ÿ”Ž Watch for file ${file}`); + afLogger.trace(`๐Ÿชฒ๐Ÿ”Ž Watch for file ${file}`); watcher.add(file); }); - process.env.HEAVY_DEBUG && console.log('๐Ÿชฒ๐Ÿ”Ž Watch for:', directories.join(',')); + afLogger.trace(`๐Ÿชฒ๐Ÿ”Ž Watch for: ${directories.join(',')}`); watcher.on( 'change', async (fileOrDir) => { // copy one file const relativeFilename = fileOrDir.replace(customComponentsDir + path.sep, ''); - if (process.env.HEAVY_DEBUG) { - console.log(`๐Ÿ”Ž fileOrDir ${fileOrDir} changed`); - console.log(`๐Ÿ”Ž relativeFilename ${relativeFilename}`); - console.log(`๐Ÿ”Ž customComponentsDir ${customComponentsDir}`); - console.log(`๐Ÿ”Ž destination ${destination}`); - } + afLogger.trace(`๐Ÿ”Ž fileOrDir ${fileOrDir} changed`); + afLogger.trace(`๐Ÿ”Ž relativeFilename ${relativeFilename}`); + afLogger.trace(`๐Ÿ”Ž customComponentsDir ${customComponentsDir}`); + afLogger.trace(`๐Ÿ”Ž destination ${destination}`); const isFile = fs.lstatSync(fileOrDir).isFile(); if (isFile) { const destPath = path.join(this.spaTmpPath(), 'src', 'custom', destination, relativeFilename); - process.env.HEAVY_DEBUG && console.log(`๐Ÿ”Ž Copying file ${fileOrDir} to ${destPath}`); + afLogger.trace(`๐Ÿ”Ž Copying file ${fileOrDir} to ${destPath}`); await fsExtra.copy(fileOrDir, destPath); return; } else { @@ -836,7 +819,7 @@ class CodeInjector implements ICodeInjector { return content; } catch (e) { // file does not exist - process.env.HEAVY_DEBUG && console.log(`๐ŸชฒFile ${filePath} does not exist, returning null`); + afLogger.trace(`๐ŸชฒFile ${filePath} does not exist, returning null`); return null; } } @@ -867,7 +850,7 @@ class CodeInjector implements ICodeInjector { } async bundleNow({ hotReload = false }: { hotReload: boolean }) { - console.log(`${this.adminforth.formatAdminForth()} Bundling ${hotReload ? 'and listening for changes (๐Ÿ”ฅ Hotreload)' : ' (no hot reload)'}`); + afLogger.info(`${this.adminforth.formatAdminForth()} Bundling ${hotReload ? 'and listening for changes (๐Ÿ”ฅ Hotreload)' : ' (no hot reload)'}`); this.adminforth.runningHotReload = hotReload; await this.prepareSources(); @@ -890,8 +873,8 @@ class CodeInjector implements ICodeInjector { const allFiles = []; const sourcesHash = await this.computeSourcesHash(this.spaTmpPath(), allFiles); - process.env.VERY_HEAVY_DEBUG && console.log('๐Ÿชฒ๐Ÿชฒ allFiles:', JSON.stringify( - allFiles.sort((a,b) => a.localeCompare(b)), null, 1)) + afLogger.trace(`๐Ÿชฒ๐Ÿชฒ allFiles:, ${JSON.stringify( + allFiles.sort((a,b) => a.localeCompare(b)), null, 1)}`); const buildHash = await this.tryReadFile(path.join(serveDir, '.adminforth_build_hash')); const messagesHash = await this.tryReadFile(path.join(serveDir, '.adminforth_messages_hash')); @@ -899,11 +882,9 @@ class CodeInjector implements ICodeInjector { const skipBuild = buildHash === sourcesHash; const skipExtract = messagesHash === sourcesHash; - if (process.env.HEAVY_DEBUG) { - console.log(`๐Ÿชฒ SPA build hash: ${buildHash}`); - console.log(`๐Ÿชฒ SPA messages hash: ${messagesHash}`); - console.log(`๐Ÿชฒ SPA sources hash: ${sourcesHash}`); - } + afLogger.trace(`๐Ÿชฒ SPA build hash: ${buildHash}`); + afLogger.trace(`๐Ÿชฒ SPA messages hash: ${messagesHash}`); + afLogger.trace(`๐Ÿชฒ SPA sources hash: ${sourcesHash}`); if (!skipBuild) { // remove serveDir if exists @@ -927,7 +908,7 @@ class CodeInjector implements ICodeInjector { // save hash await fs.promises.writeFile(path.join(serveDir, '.adminforth_messages_hash'), sourcesHash); } else { - console.log(`AdminForth i18n message extraction skipped โ€” build already performed for the current sources.`); + afLogger.info(`AdminForth i18n message extraction skipped โ€” build already performed for the current sources.`); } if (!hotReload) { @@ -942,14 +923,14 @@ class CodeInjector implements ICodeInjector { // save hash await fs.promises.writeFile(path.join(serveDir, '.adminforth_build_hash'), sourcesHash); } else { - console.log(`Skipping AdminForth SPA bundling - already completed for the current sources.`); + afLogger.info(`Skipping AdminForth SPA bundling - already completed for the current sources.`); } } else { const command = 'run dev'; - console.log(`โš™๏ธ spawn: npm ${command}...`); + afLogger.info(`โš™๏ธ spawn: npm ${command}...`); if (process.env.VITE_ADMINFORTH_PUBLIC_PATH) { - console.log('โš ๏ธ Your VITE_ADMINFORTH_PUBLIC_PATH:', process.env.VITE_ADMINFORTH_PUBLIC_PATH, 'has no effect'); + afLogger.info(`โš ๏ธ Your VITE_ADMINFORTH_PUBLIC_PATH: ${process.env.VITE_ADMINFORTH_PUBLIC_PATH} has no effect`); } const env = { VITE_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, @@ -973,19 +954,19 @@ class CodeInjector implements ICodeInjector { // parse port from message " โžœ Local: http://localhost:xyz/" const s = stripAnsiCodes(data.toString()); - process.env.HEAVY_DEBUG && console.log('๐Ÿชฒ devServer stdout โžœ (port detect):', s); + afLogger.trace(`๐Ÿชฒ devServer stdout โžœ (port detect): ${s}`); const portMatch = s.match(/.+?http:\/\/.+?:(\d+).+?/m); if (portMatch) { this.devServerPort = parseInt(portMatch[1]); } } else { - process.env.HEAVY_DEBUG && console.log(`[AdminForth SPA]:`); - process.env.HEAVY_DEBUG && process.stdout.write(data); + afLogger.trace(`[AdminForth SPA]:`); + afLogger.trace(data.toString()); } }); devServer.stderr.on('data', (data) => { - console.error(`[AdminForth SPA ERROR]:`); - process.stdout.write(data); + afLogger.error(`[AdminForth SPA ERROR]:`); + afLogger.error(data.toString()); }); } diff --git a/adminforth/modules/configValidator.ts b/adminforth/modules/configValidator.ts index 8aa830ebc..1d60c3ee8 100644 --- a/adminforth/modules/configValidator.ts +++ b/adminforth/modules/configValidator.ts @@ -29,6 +29,7 @@ import { } from "../types/Common.js"; import AdminForth from "adminforth"; import { AdminForthConfigMenuItem } from "adminforth"; +import { afLogger } from "./logger.js"; export default class ConfigValidator implements IConfigValidator { @@ -1211,7 +1212,7 @@ export default class ConfigValidator implements IConfigValidator { } if (warnings.length > 0) { - console.warn(`AdminForth config warnings: ${warnings.join(', ')}`); + afLogger.warn(`AdminForth config warnings: ${warnings.join(', ')}`); } //add ids for onSelectedAllActions for each resource diff --git a/adminforth/modules/logger.ts b/adminforth/modules/logger.ts new file mode 100644 index 000000000..cf6a8d405 --- /dev/null +++ b/adminforth/modules/logger.ts @@ -0,0 +1,28 @@ +import pino from 'pino'; + +const baseLogger = pino({ + transport: { + target: 'pino-pretty', + options: { + colorize: true, + ignore: 'pid,hostname', + hideObject: true, + messageFormat: '{layer}: {msg}', + } + }, +}); + +export const logger = baseLogger.child( + { layer: 'user_logs' }, + { level: process.env.HEAVY_DEBUG ? 'trace' : ( process.env.DEBUG_LEVEL || 'info' ) } +); + +export const afLogger = baseLogger.child( + { layer: 'af' }, + { level: process.env.HEAVY_DEBUG ? 'trace' : ( process.env.AF_DEBUG_LEVEL || 'info' ) } +); + +export const dbLogger = baseLogger.child( + { layer: 'db' }, + { level: process.env.HEAVY_DEBUG_QUERY ? 'trace' : (process.env.DB_DEBUG_LEVEL|| 'info') } +); \ No newline at end of file diff --git a/adminforth/modules/restApi.ts b/adminforth/modules/restApi.ts index 1f784d3fe..1765711fd 100644 --- a/adminforth/modules/restApi.ts +++ b/adminforth/modules/restApi.ts @@ -15,6 +15,8 @@ import { Filters, } from "../types/Back.js"; +import { afLogger } from "./logger.js"; + import { ADMINFORTH_VERSION, listify, md5hash, getLoginPromptHTML } from './utils.js'; import AdminForthAuth from "../auth.js"; @@ -77,9 +79,7 @@ export async function interpretResource( source: ActionCheckSource, adminforth: IAdminForth ): Promise<{allowedActions: AllowedActionsResolved}> { - if (process.env.HEAVY_DEBUG) { - console.log('๐ŸชฒInterpreting resource', resource.resourceId, source, 'adminUser', adminUser); - } + afLogger.trace(`๐ŸชฒInterpreting resource, ${resource.resourceId}, ${source}, 'adminUser', ${adminUser}`); const allowedActions = {} as AllowedActionsResolved; // we need to compute only allowed actions for this source: @@ -176,7 +176,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { showIn: Object.values(AdminForthResourcePages).reduce((acc, page) => { return { ...acc, [page]: false } }, {} as ShowInResolved), type: AdminForthDataTypes.STRING, }); - console.log('Adding passwordHashField to userResource', userResource) + afLogger.info(`Adding passwordHashField to userResource, ${userResource}`); } const userRecord = ( @@ -208,7 +208,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { const expireInDuration = rememberMe ? (this.adminforth.config.auth.rememberMeDuration || '30d') : '1d'; - console.log('expireInDuration', expireInDuration); await this.processLoginCallbacks(adminUser, toReturn, response, { body, headers, query, cookies, requestUrl, @@ -1046,13 +1045,13 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { const availableSearchFields = searchableFields.filter((fieldName) => { const fieldExists = targetResource.columns.some(col => col.name === fieldName); if (!fieldExists) { - process.env.HEAVY_DEBUG && console.log(`โš ๏ธ Field '${fieldName}' not found in polymorphic target resource '${targetResource.resourceId}', skipping in search filter.`); + afLogger.trace(`โš ๏ธ Field '${fieldName}' not found in polymorphic target resource '${targetResource.resourceId}', skipping in search filter.`); } return fieldExists; }); if (availableSearchFields.length === 0) { - process.env.HEAVY_DEBUG && console.log(`โš ๏ธ No searchable fields available in polymorphic target resource '${targetResource.resourceId}', skipping resource.`); + afLogger.trace(`โš ๏ธ No searchable fields available in polymorphic target resource '${targetResource.resourceId}', skipping resource.`); resolve({ items: [] }); return; } diff --git a/adminforth/modules/socketBroker.ts b/adminforth/modules/socketBroker.ts index 6fe9e9880..8e68f97b4 100644 --- a/adminforth/modules/socketBroker.ts +++ b/adminforth/modules/socketBroker.ts @@ -1,5 +1,6 @@ import { IAdminForth, IWebSocketBroker, IWebSocketClient } from "../types/Back.js"; import { AdminUser } from "../types/Common.js"; +import { afLogger } from '../modules/logger.js'; export default class SocketBroker implements IWebSocketBroker { clients: IWebSocketClient[] = []; @@ -72,7 +73,7 @@ export default class SocketBroker implements IWebSocketBroker { try { authResult = await this.adminforth.config.auth.websocketTopicAuth(data.topic, client.adminUser); } catch (e) { - console.error('Error in websocketTopicAuth, assuming connection not allowed', e); + afLogger.error(`Error in websocketTopicAuth, assuming connection not allowed ${e}`); } if (!authResult) { client.send(JSON.stringify({ type: 'error', message: 'Unauthorized' })); @@ -93,7 +94,7 @@ export default class SocketBroker implements IWebSocketBroker { try { await this.adminforth.config.auth.websocketSubscribed(data.topic, client.adminUser); } catch (e) { - console.error(`Error in websocketSubscribed for topic ${data.topic}`, e); + afLogger.error(`Error in websocketSubscribed for topic ${data.topic}, ${e}`); } })(); // run in background } @@ -125,18 +126,17 @@ export default class SocketBroker implements IWebSocketBroker { async publish(topic: string, data: any, filterUsers?: (adminUser: AdminUser) => Promise): Promise { if (!this.topics[topic]) { - process.env.HEAVY_DEBUG && console.log('No clients subscribed to topic', topic); + afLogger.trace(`No clients subscribed to topic ${topic}`); return; } for (const client of this.topics[topic]) { if (filterUsers) { if (! (await filterUsers(client.adminUser)) ) { - process.env.HEAVY_DEBUG && console.log('Client not authorized to receive message', topic, client.adminUser); + afLogger.trace(`Client not authorized to receive message ${topic} ${client.adminUser}`); continue; } } - process.env.HEAVY_DEBUG && console.log('Sending data to soket', topic, data); - + afLogger.trace(`Sending data to socket ${topic} ${JSON.stringify(data)}`); client.send(JSON.stringify({ type: 'message', topic, data })); } } diff --git a/adminforth/package-lock.json b/adminforth/package-lock.json index f8e1fc6e8..8ae3340a3 100644 --- a/adminforth/package-lock.json +++ b/adminforth/package-lock.json @@ -36,6 +36,8 @@ "mysql2": "^3.14.2", "node-fetch": "^3.3.2", "pg": "^8.11.5", + "pino": "^10.1.0", + "pino-pretty": "^13.1.3", "rate-limiter-flexible": "^8.1.0", "recast": "^0.23.11", "ws": "^8.18.0" @@ -623,6 +625,12 @@ "@octokit/openapi-types": "^27.0.0" } }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -1211,6 +1219,15 @@ "node": ">=4" } }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/aws-ssl-profiles": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", @@ -1996,6 +2013,15 @@ "node": ">= 12" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/dayjs": { "version": "1.11.11", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", @@ -2609,6 +2635,18 @@ ], "license": "MIT" }, + "node_modules/fast-copy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz", + "integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==", + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -3052,6 +3090,12 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -3501,6 +3545,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4905,6 +4958,7 @@ }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -4921,6 +4975,7 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.1.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -4932,11 +4987,13 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -4953,6 +5010,7 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -4967,6 +5025,7 @@ }, "node_modules/npm/node_modules/@isaacs/fs-minipass": { "version": "4.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -4978,11 +5037,13 @@ }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/agent": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -4998,6 +5059,7 @@ }, "node_modules/npm/node_modules/@npmcli/arborist": { "version": "8.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5046,6 +5108,7 @@ }, "node_modules/npm/node_modules/@npmcli/config": { "version": "9.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5064,6 +5127,7 @@ }, "node_modules/npm/node_modules/@npmcli/fs": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5075,6 +5139,7 @@ }, "node_modules/npm/node_modules/@npmcli/git": { "version": "6.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5094,6 +5159,7 @@ }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5109,6 +5175,7 @@ }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { "version": "4.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5123,6 +5190,7 @@ }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { "version": "8.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5138,6 +5206,7 @@ }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { "version": "20.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5168,6 +5237,7 @@ }, "node_modules/npm/node_modules/@npmcli/name-from-folder": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5176,6 +5246,7 @@ }, "node_modules/npm/node_modules/@npmcli/node-gyp": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5184,6 +5255,7 @@ }, "node_modules/npm/node_modules/@npmcli/package-json": { "version": "6.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5201,6 +5273,7 @@ }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { "version": "8.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5212,6 +5285,7 @@ }, "node_modules/npm/node_modules/@npmcli/query": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5223,6 +5297,7 @@ }, "node_modules/npm/node_modules/@npmcli/redact": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5231,6 +5306,7 @@ }, "node_modules/npm/node_modules/@npmcli/run-script": { "version": "9.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5247,6 +5323,7 @@ }, "node_modules/npm/node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -5256,6 +5333,7 @@ }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { "version": "0.3.2", + "dev": true, "inBundle": true, "license": "Apache-2.0", "engines": { @@ -5264,6 +5342,7 @@ }, "node_modules/npm/node_modules/@sigstore/tuf": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -5276,6 +5355,7 @@ }, "node_modules/npm/node_modules/@tufjs/canonical-json": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5284,6 +5364,7 @@ }, "node_modules/npm/node_modules/abbrev": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5292,6 +5373,7 @@ }, "node_modules/npm/node_modules/agent-base": { "version": "7.1.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5303,6 +5385,7 @@ }, "node_modules/npm/node_modules/aggregate-error": { "version": "3.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5315,6 +5398,7 @@ }, "node_modules/npm/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5323,6 +5407,7 @@ }, "node_modules/npm/node_modules/ansi-styles": { "version": "6.2.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5334,21 +5419,25 @@ }, "node_modules/npm/node_modules/aproba": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/archy": { "version": "1.0.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/bin-links": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5364,6 +5453,7 @@ }, "node_modules/npm/node_modules/binary-extensions": { "version": "2.3.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5375,6 +5465,7 @@ }, "node_modules/npm/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5383,6 +5474,7 @@ }, "node_modules/npm/node_modules/cacache": { "version": "19.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5405,6 +5497,7 @@ }, "node_modules/npm/node_modules/cacache/node_modules/chownr": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "engines": { @@ -5413,6 +5506,7 @@ }, "node_modules/npm/node_modules/cacache/node_modules/minizlib": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5425,6 +5519,7 @@ }, "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "bin": { @@ -5439,6 +5534,7 @@ }, "node_modules/npm/node_modules/cacache/node_modules/p-map": { "version": "7.0.2", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5450,6 +5546,7 @@ }, "node_modules/npm/node_modules/cacache/node_modules/tar": { "version": "7.4.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5466,6 +5563,7 @@ }, "node_modules/npm/node_modules/cacache/node_modules/yallist": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "engines": { @@ -5474,6 +5572,7 @@ }, "node_modules/npm/node_modules/chalk": { "version": "5.3.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5485,6 +5584,7 @@ }, "node_modules/npm/node_modules/chownr": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5493,6 +5593,7 @@ }, "node_modules/npm/node_modules/ci-info": { "version": "4.1.0", + "dev": true, "funding": [ { "type": "github", @@ -5507,6 +5608,7 @@ }, "node_modules/npm/node_modules/cidr-regex": { "version": "4.1.1", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -5518,6 +5620,7 @@ }, "node_modules/npm/node_modules/clean-stack": { "version": "2.2.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5526,6 +5629,7 @@ }, "node_modules/npm/node_modules/cli-columns": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5538,6 +5642,7 @@ }, "node_modules/npm/node_modules/cmd-shim": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5546,6 +5651,7 @@ }, "node_modules/npm/node_modules/color-convert": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5557,16 +5663,19 @@ }, "node_modules/npm/node_modules/color-name": { "version": "1.1.4", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.6", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5580,6 +5689,7 @@ }, "node_modules/npm/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5594,6 +5704,7 @@ }, "node_modules/npm/node_modules/cssesc": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "MIT", "bin": { @@ -5605,6 +5716,7 @@ }, "node_modules/npm/node_modules/debug": { "version": "4.3.7", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5621,6 +5733,7 @@ }, "node_modules/npm/node_modules/diff": { "version": "5.2.0", + "dev": true, "inBundle": true, "license": "BSD-3-Clause", "engines": { @@ -5629,16 +5742,19 @@ }, "node_modules/npm/node_modules/eastasianwidth": { "version": "0.2.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/encoding": { "version": "0.1.13", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -5648,6 +5764,7 @@ }, "node_modules/npm/node_modules/env-paths": { "version": "2.2.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5656,16 +5773,19 @@ }, "node_modules/npm/node_modules/err-code": { "version": "2.0.3", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "Apache-2.0" }, "node_modules/npm/node_modules/fastest-levenshtein": { "version": "1.0.16", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5674,6 +5794,7 @@ }, "node_modules/npm/node_modules/foreground-child": { "version": "3.3.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5689,6 +5810,7 @@ }, "node_modules/npm/node_modules/fs-minipass": { "version": "3.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5700,6 +5822,7 @@ }, "node_modules/npm/node_modules/glob": { "version": "10.4.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5719,11 +5842,13 @@ }, "node_modules/npm/node_modules/graceful-fs": { "version": "4.2.11", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/hosted-git-info": { "version": "8.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5735,11 +5860,13 @@ }, "node_modules/npm/node_modules/http-cache-semantics": { "version": "4.1.1", + "dev": true, "inBundle": true, "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { "version": "7.0.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5752,6 +5879,7 @@ }, "node_modules/npm/node_modules/https-proxy-agent": { "version": "7.0.5", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5764,6 +5892,7 @@ }, "node_modules/npm/node_modules/iconv-lite": { "version": "0.6.3", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -5776,6 +5905,7 @@ }, "node_modules/npm/node_modules/ignore-walk": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5787,6 +5917,7 @@ }, "node_modules/npm/node_modules/imurmurhash": { "version": "0.1.4", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5795,6 +5926,7 @@ }, "node_modules/npm/node_modules/indent-string": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5803,6 +5935,7 @@ }, "node_modules/npm/node_modules/ini": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5811,6 +5944,7 @@ }, "node_modules/npm/node_modules/init-package-json": { "version": "7.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5828,6 +5962,7 @@ }, "node_modules/npm/node_modules/ip-address": { "version": "9.0.5", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5840,6 +5975,7 @@ }, "node_modules/npm/node_modules/ip-regex": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5851,6 +5987,7 @@ }, "node_modules/npm/node_modules/is-cidr": { "version": "5.1.0", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -5862,6 +5999,7 @@ }, "node_modules/npm/node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5870,11 +6008,13 @@ }, "node_modules/npm/node_modules/isexe": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/jackspeak": { "version": "3.4.3", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -5889,11 +6029,13 @@ }, "node_modules/npm/node_modules/jsbn": { "version": "1.1.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5902,6 +6044,7 @@ }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", + "dev": true, "inBundle": true, "license": "ISC", "funding": { @@ -5910,6 +6053,7 @@ }, "node_modules/npm/node_modules/jsonparse": { "version": "1.3.1", + "dev": true, "engines": [ "node >= 0.2.0" ], @@ -5918,16 +6062,19 @@ }, "node_modules/npm/node_modules/just-diff": { "version": "6.0.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { "version": "5.5.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { "version": "9.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5940,6 +6087,7 @@ }, "node_modules/npm/node_modules/libnpmdiff": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5958,6 +6106,7 @@ }, "node_modules/npm/node_modules/libnpmexec": { "version": "9.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5978,6 +6127,7 @@ }, "node_modules/npm/node_modules/libnpmfund": { "version": "6.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5989,6 +6139,7 @@ }, "node_modules/npm/node_modules/libnpmhook": { "version": "11.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6001,6 +6152,7 @@ }, "node_modules/npm/node_modules/libnpmorg": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6013,6 +6165,7 @@ }, "node_modules/npm/node_modules/libnpmpack": { "version": "8.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6027,6 +6180,7 @@ }, "node_modules/npm/node_modules/libnpmpublish": { "version": "10.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6045,6 +6199,7 @@ }, "node_modules/npm/node_modules/libnpmsearch": { "version": "8.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6056,6 +6211,7 @@ }, "node_modules/npm/node_modules/libnpmteam": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6068,6 +6224,7 @@ }, "node_modules/npm/node_modules/libnpmversion": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6083,11 +6240,13 @@ }, "node_modules/npm/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/make-fetch-happen": { "version": "14.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6109,6 +6268,7 @@ }, "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { "version": "1.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6117,6 +6277,7 @@ }, "node_modules/npm/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6131,6 +6292,7 @@ }, "node_modules/npm/node_modules/minipass": { "version": "7.1.2", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6139,6 +6301,7 @@ }, "node_modules/npm/node_modules/minipass-collect": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6150,6 +6313,7 @@ }, "node_modules/npm/node_modules/minipass-fetch": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6166,6 +6330,7 @@ }, "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6178,6 +6343,7 @@ }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6189,6 +6355,7 @@ }, "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6200,6 +6367,7 @@ }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6211,6 +6379,7 @@ }, "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6222,6 +6391,7 @@ }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6233,6 +6403,7 @@ }, "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6244,6 +6415,7 @@ }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6256,6 +6428,7 @@ }, "node_modules/npm/node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6267,6 +6440,7 @@ }, "node_modules/npm/node_modules/mkdirp": { "version": "1.0.4", + "dev": true, "inBundle": true, "license": "MIT", "bin": { @@ -6278,11 +6452,13 @@ }, "node_modules/npm/node_modules/ms": { "version": "2.1.3", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6291,6 +6467,7 @@ }, "node_modules/npm/node_modules/node-gyp": { "version": "11.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6314,6 +6491,7 @@ }, "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "engines": { @@ -6322,6 +6500,7 @@ }, "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6334,6 +6513,7 @@ }, "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "bin": { @@ -6348,6 +6528,7 @@ }, "node_modules/npm/node_modules/node-gyp/node_modules/tar": { "version": "7.4.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6364,6 +6545,7 @@ }, "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "engines": { @@ -6372,6 +6554,7 @@ }, "node_modules/npm/node_modules/nopt": { "version": "8.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6386,6 +6569,7 @@ }, "node_modules/npm/node_modules/nopt/node_modules/abbrev": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6394,6 +6578,7 @@ }, "node_modules/npm/node_modules/normalize-package-data": { "version": "7.0.0", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -6407,6 +6592,7 @@ }, "node_modules/npm/node_modules/npm-audit-report": { "version": "6.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6415,6 +6601,7 @@ }, "node_modules/npm/node_modules/npm-bundled": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6426,6 +6613,7 @@ }, "node_modules/npm/node_modules/npm-install-checks": { "version": "7.1.1", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -6437,6 +6625,7 @@ }, "node_modules/npm/node_modules/npm-normalize-package-bin": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6445,6 +6634,7 @@ }, "node_modules/npm/node_modules/npm-package-arg": { "version": "12.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6459,6 +6649,7 @@ }, "node_modules/npm/node_modules/npm-packlist": { "version": "9.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6470,6 +6661,7 @@ }, "node_modules/npm/node_modules/npm-pick-manifest": { "version": "10.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6484,6 +6676,7 @@ }, "node_modules/npm/node_modules/npm-profile": { "version": "11.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6496,6 +6689,7 @@ }, "node_modules/npm/node_modules/npm-registry-fetch": { "version": "18.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6514,6 +6708,7 @@ }, "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6526,6 +6721,7 @@ }, "node_modules/npm/node_modules/npm-user-validate": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "engines": { @@ -6534,6 +6730,7 @@ }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6548,11 +6745,13 @@ }, "node_modules/npm/node_modules/package-json-from-dist": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0" }, "node_modules/npm/node_modules/pacote": { "version": "19.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6583,6 +6782,7 @@ }, "node_modules/npm/node_modules/parse-conflict-json": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6596,6 +6796,7 @@ }, "node_modules/npm/node_modules/path-key": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6604,6 +6805,7 @@ }, "node_modules/npm/node_modules/path-scurry": { "version": "1.11.1", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -6619,6 +6821,7 @@ }, "node_modules/npm/node_modules/postcss-selector-parser": { "version": "6.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6631,6 +6834,7 @@ }, "node_modules/npm/node_modules/proc-log": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6639,6 +6843,7 @@ }, "node_modules/npm/node_modules/proggy": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6647,6 +6852,7 @@ }, "node_modules/npm/node_modules/promise-all-reject-late": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC", "funding": { @@ -6655,6 +6861,7 @@ }, "node_modules/npm/node_modules/promise-call-limit": { "version": "3.0.2", + "dev": true, "inBundle": true, "license": "ISC", "funding": { @@ -6663,11 +6870,13 @@ }, "node_modules/npm/node_modules/promise-inflight": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/promise-retry": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6680,6 +6889,7 @@ }, "node_modules/npm/node_modules/promzard": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6691,6 +6901,7 @@ }, "node_modules/npm/node_modules/qrcode-terminal": { "version": "0.12.0", + "dev": true, "inBundle": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" @@ -6698,6 +6909,7 @@ }, "node_modules/npm/node_modules/read": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6709,6 +6921,7 @@ }, "node_modules/npm/node_modules/read-cmd-shim": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6717,6 +6930,7 @@ }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6729,6 +6943,7 @@ }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6737,6 +6952,7 @@ }, "node_modules/npm/node_modules/rimraf": { "version": "5.0.10", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6751,12 +6967,14 @@ }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", + "dev": true, "inBundle": true, "license": "MIT", "optional": true }, "node_modules/npm/node_modules/semver": { "version": "7.6.3", + "dev": true, "inBundle": true, "license": "ISC", "bin": { @@ -6768,6 +6986,7 @@ }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6779,6 +6998,7 @@ }, "node_modules/npm/node_modules/shebang-regex": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6787,6 +7007,7 @@ }, "node_modules/npm/node_modules/signal-exit": { "version": "4.1.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6798,6 +7019,7 @@ }, "node_modules/npm/node_modules/sigstore": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -6814,6 +7036,7 @@ }, "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -6825,6 +7048,7 @@ }, "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "engines": { @@ -6833,6 +7057,7 @@ }, "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -6849,6 +7074,7 @@ }, "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -6862,6 +7088,7 @@ }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6871,6 +7098,7 @@ }, "node_modules/npm/node_modules/socks": { "version": "2.8.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6884,6 +7112,7 @@ }, "node_modules/npm/node_modules/socks-proxy-agent": { "version": "8.0.4", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6897,6 +7126,7 @@ }, "node_modules/npm/node_modules/spdx-correct": { "version": "3.2.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -6906,6 +7136,7 @@ }, "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6915,11 +7146,13 @@ }, "node_modules/npm/node_modules/spdx-exceptions": { "version": "2.5.0", + "dev": true, "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6929,16 +7162,19 @@ }, "node_modules/npm/node_modules/spdx-license-ids": { "version": "3.0.20", + "dev": true, "inBundle": true, "license": "CC0-1.0" }, "node_modules/npm/node_modules/sprintf-js": { "version": "1.1.3", + "dev": true, "inBundle": true, "license": "BSD-3-Clause" }, "node_modules/npm/node_modules/ssri": { "version": "12.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6950,6 +7186,7 @@ }, "node_modules/npm/node_modules/string-width": { "version": "4.2.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6964,6 +7201,7 @@ "node_modules/npm/node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6977,6 +7215,7 @@ }, "node_modules/npm/node_modules/strip-ansi": { "version": "6.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6989,6 +7228,7 @@ "node_modules/npm/node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7000,6 +7240,7 @@ }, "node_modules/npm/node_modules/supports-color": { "version": "9.4.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7011,6 +7252,7 @@ }, "node_modules/npm/node_modules/tar": { "version": "6.2.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7027,6 +7269,7 @@ }, "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7038,6 +7281,7 @@ }, "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7049,6 +7293,7 @@ }, "node_modules/npm/node_modules/tar/node_modules/minipass": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7057,16 +7302,19 @@ }, "node_modules/npm/node_modules/text-table": { "version": "0.2.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { "version": "1.3.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7075,6 +7323,7 @@ }, "node_modules/npm/node_modules/tuf-js": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7088,6 +7337,7 @@ }, "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7100,6 +7350,7 @@ }, "node_modules/npm/node_modules/unique-filename": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7111,6 +7362,7 @@ }, "node_modules/npm/node_modules/unique-slug": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7122,11 +7374,13 @@ }, "node_modules/npm/node_modules/util-deprecate": { "version": "1.0.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/validate-npm-package-license": { "version": "3.0.4", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -7136,6 +7390,7 @@ }, "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7145,6 +7400,7 @@ }, "node_modules/npm/node_modules/validate-npm-package-name": { "version": "6.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7153,11 +7409,13 @@ }, "node_modules/npm/node_modules/walk-up-path": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/which": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7172,6 +7430,7 @@ }, "node_modules/npm/node_modules/which/node_modules/isexe": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7180,6 +7439,7 @@ }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7197,6 +7457,7 @@ "node_modules/npm/node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7213,6 +7474,7 @@ }, "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7227,6 +7489,7 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.1.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7238,11 +7501,13 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "9.2.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { "version": "5.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7259,6 +7524,7 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7273,6 +7539,7 @@ }, "node_modules/npm/node_modules/write-file-atomic": { "version": "6.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7285,6 +7552,7 @@ }, "node_modules/npm/node_modules/yallist": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC" }, @@ -7310,6 +7578,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -7696,6 +7973,88 @@ "node": ">=4" } }, + "node_modules/pino": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", + "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^4.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, "node_modules/pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", @@ -7792,6 +8151,22 @@ "dev": true, "license": "MIT" }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -7843,6 +8218,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -7995,6 +8376,15 @@ "node": ">= 6" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/recast": { "version": "0.23.11", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", @@ -8152,11 +8542,36 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/semantic-release": { "version": "24.2.1", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.1.tgz", @@ -8813,6 +9228,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9155,6 +9579,15 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", diff --git a/adminforth/package.json b/adminforth/package.json index 47749f829..2fbbf9ebe 100644 --- a/adminforth/package.json +++ b/adminforth/package.json @@ -82,6 +82,8 @@ "mysql2": "^3.14.2", "node-fetch": "^3.3.2", "pg": "^8.11.5", + "pino": "^10.1.0", + "pino-pretty": "^13.1.3", "rate-limiter-flexible": "^8.1.0", "recast": "^0.23.11", "ws": "^8.18.0" diff --git a/adminforth/servers/express.ts b/adminforth/servers/express.ts index b732ceb67..7c321084e 100644 --- a/adminforth/servers/express.ts +++ b/adminforth/servers/express.ts @@ -10,6 +10,7 @@ import { AdminUser } from '../types/Common.js'; import http from 'http'; import { randomUUID } from 'crypto'; import { listify } from '../modules/utils.js'; +import { afLogger } from '../modules/logger.js'; function replaceAtStart(string, substring) { if (string.startsWith(substring)) { @@ -113,13 +114,12 @@ class ExpressServer implements IExpressHttpServer { } await proxyTo(`http://localhost:${this.adminforth.codeInjector.devServerPort}${req.url}`, res); } catch (e) { - // console.log('Failed to proxy', e); res.status(500).send(respondNoServer('AdminForth SPA is not ready yet', 'Vite is still starting up. Please wait a moment...')); return; } } this.expressApp.get(`${slashedPrefix}assets/*`, handler); - process.env.HEAVY_DEBUG && console.log('ยฎ๏ธ Registering SPA serve handler', `${slashedPrefix}assets/*`); + afLogger.trace(`ยฎ๏ธ Registering SPA serve handler', ${slashedPrefix}assets/*`); this.expressApp.get(`${prefix}*`, handler); @@ -172,7 +172,7 @@ class ExpressServer implements IExpressHttpServer { this.server = http.createServer(this.expressApp); const wss = new WebSocketServer({ server: this.server, path: `${base}/afws` }); - console.log(`${this.adminforth.formatAdminForth()} ๐ŸŒ WebSocket server started`); + afLogger.info(`${this.adminforth.formatAdminForth()} ๐ŸŒ WebSocket server started`); // Handle WebSocket connections wss.on('connection', async (ws, req) => { try { @@ -200,7 +200,7 @@ class ExpressServer implements IExpressHttpServer { }) ); } catch (e) { - console.error('Failed to handle WS connection', e); + afLogger.error(`Failed to handle WS connection ${e}`); } }); @@ -244,7 +244,7 @@ class ExpressServer implements IExpressHttpServer { // check if multiple adminforth_jwt providerd and show warning const jwts = cookies.filter(({key}) => key === `adminforth_${brandSlug}_jwt`); if (jwts.length > 1) { - console.error('Multiple adminforth_jwt cookies provided'); + afLogger.error('Multiple adminforth_jwt cookies provided'); } const jwt = jwts[0]?.value; @@ -260,7 +260,7 @@ class ExpressServer implements IExpressHttpServer { // this might happen if e.g. database intialization in progress. // so we can't answer with 401 (would logout user) // reproduced during usage of listRowsAutoRefreshSeconds - console.error(e.stack); + afLogger.error(e.stack); res.status(500).send('Failed to verify JWT token - something went wrong'); return; } @@ -313,7 +313,7 @@ class ExpressServer implements IExpressHttpServer { try { body = JSON.parse(body); } catch (e) { - console.error('Failed to parse body', e); + afLogger.error(`Failed to parse body, ${e}`); res.status(400).send('Invalid JSON body'); } } @@ -330,7 +330,7 @@ class ExpressServer implements IExpressHttpServer { message: undefined, setHeader(name, value) { - process.env.HEAVY_DEBUG && console.log(' ๐ŸชฒSetting header', name, value); + afLogger.trace(`๐ŸชฒSetting header, ${name}, ${value}`); this.headers.push([name, value]); }, @@ -355,9 +355,9 @@ class ExpressServer implements IExpressHttpServer { try { output = await handler(input); } catch (e) { - console.error('Error in handler', e); + afLogger.error(`Error in handler ${e}`); // print full stack trace - console.error(e.stack); + afLogger.error(e.stack); res.status(500).send('Internal server error'); return; } @@ -376,7 +376,7 @@ class ExpressServer implements IExpressHttpServer { res.json(output); } - process.env.HEAVY_DEBUG && console.log(`๐Ÿ‘‚ Adding endpoint ${method} ${fullPath}`); + afLogger.trace(`๐Ÿ‘‚ Adding endpoint ${method} ${fullPath}`); this.expressApp[method.toLowerCase()](fullPath, noAuth ? expressHandler : this.authorize(expressHandler)); } diff --git a/adminforth/spa/src/afcl/DatePicker.vue b/adminforth/spa/src/afcl/DatePicker.vue index b9ac32fa9..a6dd2390f 100644 --- a/adminforth/spa/src/afcl/DatePicker.vue +++ b/adminforth/spa/src/afcl/DatePicker.vue @@ -134,7 +134,6 @@ onMounted(() => { }) watch(start, () => { - //console.log('โšก emit', start.value) emit('update:valueStart', start.value) }) diff --git a/adminforth/spa/src/components/CustomDateRangePicker.vue b/adminforth/spa/src/components/CustomDateRangePicker.vue index bb929a922..5b3e40d16 100644 --- a/adminforth/spa/src/components/CustomDateRangePicker.vue +++ b/adminforth/spa/src/components/CustomDateRangePicker.vue @@ -197,12 +197,10 @@ onMounted(() => { }) watch(start, () => { - //console.log('โšก emit', start.value) emit('update:valueStart', start.value) }) watch(end, () => { - //console.log('โšก emit', end.value) emit('update:valueEnd', end.value) }) diff --git a/adminforth/spa/src/components/CustomRangePicker.vue b/adminforth/spa/src/components/CustomRangePicker.vue index 6bde0aaa7..ac75b9bde 100644 --- a/adminforth/spa/src/components/CustomRangePicker.vue +++ b/adminforth/spa/src/components/CustomRangePicker.vue @@ -89,12 +89,10 @@ function updateEndFromProps() { } watch(start, () => { - console.log('โšก emit', start.value) emit('update:valueStart', start.value) }) watch(end, () => { - console.log('โšก emit', end.value) emit('update:valueEnd', end.value); }) diff --git a/dev-demo/index.ts b/dev-demo/index.ts index 9eea00f11..717470a87 100644 --- a/dev-demo/index.ts +++ b/dev-demo/index.ts @@ -16,6 +16,7 @@ import { FICTIONAL_CAR_BRANDS, FICTIONAL_CAR_MODELS_BY_BRAND, ENGINE_TYPES, BODY import passkeysResource from './resources/passkeys.js'; import carsDescriptionImage from './resources/cars_description_image.js'; import translations from "./resources/translations.js"; +import { logger } from 'adminforth'; const ADMIN_BASE_URL = ''; @@ -183,7 +184,7 @@ if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) { const port = 3000; admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development' }).then(() => { - console.log('Bundling AdminForth SPA done.'); + logger.info('Bundling AdminForth SPA done.'); }); admin.express.serve(app); @@ -282,6 +283,6 @@ if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) { }); admin.express.listen(port, () => { - console.log(`\nโšก AdminForth is available at http://localhost:${port}${ADMIN_BASE_URL}\n`); + logger.info(`โšก AdminForth is available at http://localhost:${port}${ADMIN_BASE_URL}\n`); }); } diff --git a/dev-demo/resources/adminuser.ts b/dev-demo/resources/adminuser.ts index 89f7d5165..6cc60d787 100644 --- a/dev-demo/resources/adminuser.ts +++ b/dev-demo/resources/adminuser.ts @@ -1,4 +1,4 @@ -import AdminForth, { AdminForthDataTypes } from '../../adminforth/index.js'; +import AdminForth, { AdminForthDataTypes, logger } from '../../adminforth/index.js'; import type { AdminForthResourceInput, AdminForthResource, AdminUser, AdminForthResourceColumn } from '../../adminforth/index.js'; import { randomUUID } from 'crypto'; import TwoFactorsAuthPlugin from '../../plugins/adminforth-two-factors-auth/index.js' @@ -205,7 +205,7 @@ export default { }, edit: { beforeSave: async ({ oldRecord, updates, adminUser, resource }: { oldRecord: any, updates: any, adminUser: AdminUser, resource: AdminForthResource }) => { - console.log('Updating user', updates); + logger.info('Updating user', updates); if (oldRecord.id === adminUser.dbUser.id && updates.role) { return { ok: false, error: 'You cannot change your own role' }; } diff --git a/dev-demo/resources/carsResourseTemplate.ts b/dev-demo/resources/carsResourseTemplate.ts index b96ba61d9..0cad95a67 100644 --- a/dev-demo/resources/carsResourseTemplate.ts +++ b/dev-demo/resources/carsResourseTemplate.ts @@ -17,6 +17,8 @@ import ImageGenerationAdapterOpenAI from '../../adapters/adminforth-image-genera import AdminForthStorageAdapterLocalFilesystem from "../../adapters/adminforth-storage-adapter-local/index.js"; import AdminForthAdapterS3Storage from '../../adapters/adminforth-storage-adapter-amazon-s3/index.js'; import AdminForthImageVisionAdapterOpenAi from '../../adapters/adminforth-image-vision-adapter-openai/index.js'; +import { logger } from 'adminforth' +import { afLogger } from '../../adminforth/modules/logger.js'; export default function carsResourseTemplate(resourceId: string, dataSource: string, pkFileldName: string) { return { @@ -285,7 +287,7 @@ export default function carsResourseTemplate(resourceId: string, dataSource: str if (!record.promo_picture) { return []; } - console.log('Attaching file for analysis:', `http://localhost:3000/static/source/cars_promo_images/${record.promo_picture}`); + afLogger.info(`Attaching file for analysis: http://localhost:3000/static/source/cars_promo_images/${record.promo_picture}`); return [`http://localhost:3000/static/source/${record.promo_picture}`]; }, visionAdapter: new AdminForthImageVisionAdapterOpenAi( @@ -341,6 +343,7 @@ export default function carsResourseTemplate(resourceId: string, dataSource: str name: 'Approve Listing', icon: 'flowbite:check-outline', action: async ({ recordId, adminUser, adminforth, extra, response }) => { + logger.info(`Admin User is approving listing for record ${recordId} in resource ${resourceId}`); //@ts-ignore const verificationResult = extra?.verificationResult if (!verificationResult) {