diff --git a/.gitignore b/.gitignore index 4a19ff9466..e2fda62fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,4 @@ mongod .history /packages/nocodb/docker/main.js.LICENSE.txt +/packages/nocodb/noco_log.db diff --git a/packages/nocodb/src/lib/noco/NcProjectBuilder.ts b/packages/nocodb/src/lib/noco/NcProjectBuilder.ts index 65333c49fb..13abff369e 100644 --- a/packages/nocodb/src/lib/noco/NcProjectBuilder.ts +++ b/packages/nocodb/src/lib/noco/NcProjectBuilder.ts @@ -718,13 +718,15 @@ export default class NcProjectBuilder { } } - res.json({ + const result = { info, aggregatedInfo: { list: this.apiInfInfoList, aggregated: this.aggregatedApiInfo } - }); + } + + res.json(result); }); } diff --git a/packages/nocodb/src/lib/noco/Noco.ts b/packages/nocodb/src/lib/noco/Noco.ts index bcd4cc4385..7b16b1b085 100644 --- a/packages/nocodb/src/lib/noco/Noco.ts +++ b/packages/nocodb/src/lib/noco/Noco.ts @@ -32,6 +32,8 @@ import {RestApiBuilder} from "./rest/RestApiBuilder"; import RestAuthCtrlCE from "./rest/RestAuthCtrl"; import RestAuthCtrlEE from "./rest/RestAuthCtrlEE"; import mkdirp from 'mkdirp'; +import MetaAPILogger from "./meta/MetaAPILogger"; + const log = debug('nc:app'); require('dotenv').config(); @@ -177,7 +179,7 @@ export default class Noco { /******************* Middlewares : start *******************/ this.router.use((req: any, _res, next) => { req.nc = this.requestContext; - req.ncSiteUrl = this.config?.envs?.[this.env]?.publicUrl || this.config?.publicUrl || (req.protocol + '://' + req.get('host')); + req.ncSiteUrl = this.config?.envs?.[this.env]?.publicUrl || this.config?.publicUrl || (req.protocol + '://' + req.get('host')); req.ncFullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; next(); }); @@ -205,6 +207,7 @@ export default class Noco { req.ncProjectId = req.ncProjectId || req.params.project_id; next(); }) + this.router.use(MetaAPILogger.mw); /******************* Middlewares : end *******************/ diff --git a/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts b/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts new file mode 100644 index 0000000000..10ead5cc32 --- /dev/null +++ b/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts @@ -0,0 +1,115 @@ +import {XKnex} from "../../dataMapper"; +import {Request} from 'express'; + +export default class MetaAPILogger { + + static _instance: MetaAPILogger; + knex: XKnex; + + constructor() { + this.knex = XKnex({ + client: 'sqlite3', + connection: { + filename: 'noco_log.db' + }, + useNullAsDefault: true + }) + } + + async init() { + await this.knex.migrate.latest({ + migrationSource: new XcLoggerMigrationSource(), + tableName: 'xc_knex_migrations' + }); + } + + + static async mw(req, res, next) { + + if (process.env.NC_LOGGER) { + const oldWrite = res.write, + oldEnd = res.end; + + const chunks = []; + + res.write = function (chunk) { + chunks.push(chunk); + + // eslint-disable-next-line prefer-rest-params + oldWrite.apply(res, arguments); + }; + + res.end = function (chunk) { + if (chunk) + chunks.push(chunk); + + const body = Buffer.concat(chunks).toString('utf8'); + + MetaAPILogger.log(req, body) + // eslint-disable-next-line prefer-rest-params + oldEnd.apply(res, arguments); + }; + } + next(); + } + + private static async log(req: Request, res: any) { + if (!process.env.NC_LOGGER) { + return; + } + if (!this._instance) { + this._instance = new MetaAPILogger(); + await this._instance.init() + } + await this._instance.knex('nc_log').insert({ + path: req.url, + params: JSON.stringify(req.query), + body: JSON.stringify(req.body), + headers: JSON.stringify(req.headers), + method: req.method, + operation: req.body?.api, + response: typeof res === 'string' ? res : JSON.stringify(res) + }) + } + + +} + +class XcLoggerMigrationSource { + // Must return a Promise containing a list of migrations. + // Migrations can be whatever you want, they will be passed as + // arguments to getMigrationName and getMigration + public getMigrations(): Promise { + // In this example we are just returning migration names + return Promise.resolve(['logger']) + } + + public getMigrationName(migration): string { + return migration; + } + + public getMigration(migration): any { + switch (migration) { + case 'logger': + return { + async up(knex: XKnex) { + await knex.schema.createTable('nc_log', table => { + table.increments(); + table.string('path'); + table.string('method'); + table.string('operation'); + table.string('params'); + table.text('headers'); + table.text('body'); + table.text('response'); + table.text('comments'); + table.timestamps(true, true); + }) + }, + async down(knex) { + await knex.schema.dropTable('nc_log') + } + }; + } + } +} diff --git a/packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts b/packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts index 0d4d8075e4..e3a2ead38e 100644 --- a/packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts +++ b/packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts @@ -37,7 +37,6 @@ import {RestApiBuilder} from "../rest/RestApiBuilder"; import RestAuthCtrl from "../rest/RestAuthCtrlEE"; import {packageVersion} from 'nc-help'; import NcMetaIO, {META_TABLES} from "./NcMetaIO"; -// import NcConnectionMgr from "../common/NcConnectionMgr"; const XC_PLUGIN_DET = 'XC_PLUGIN_DET'; @@ -57,7 +56,6 @@ export default class NcMetaMgr { protected projectMgr: any; // @ts-ignore protected isEe = false; - 4 constructor(app: Noco, config: NcConfig, xcMeta: NcMetaIO) { this.app = app; @@ -212,20 +210,20 @@ export default class NcMetaMgr { let projectHasAdmin = false; projectHasAdmin = !!(await knex('xc_users').first()) - - return res.json({ - authType: 'jwt', - projectHasAdmin, - firstUser: !projectHasAdmin, - projectHasDb, - type: this.config.type, - env: this.config.workingEnv, - googleAuthEnabled: !!(process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET), - githubAuthEnabled: !!(process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET), - oneClick: !!process.env.NC_ONE_CLICK, - connectToExternalDB: !process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED, - version: packageVersion - }) +const result = { + authType: 'jwt', + projectHasAdmin, + firstUser: !projectHasAdmin, + projectHasDb, + type: this.config.type, + env: this.config.workingEnv, + googleAuthEnabled: !!(process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET), + githubAuthEnabled: !!(process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET), + oneClick: !!process.env.NC_ONE_CLICK, + connectToExternalDB: !process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED, + version: packageVersion +}; + return res.json(result) } if (this.config.auth.masterKey) { return res.json({ @@ -292,6 +290,7 @@ export default class NcMetaMgr { return res.status(400).json({msg: e.message}) } + if (this.listener) { await this.listener({ req: req.body, @@ -303,6 +302,8 @@ export default class NcMetaMgr { }); } + + return res.json(result); } @@ -1019,6 +1020,7 @@ export default class NcMetaMgr { } catch (e) { return next(e); } + res.json(result); } @@ -1496,7 +1498,6 @@ export default class NcMetaMgr { default: return next(); } - if (this.listener) { await this.listener({ user: req.user,