|
|
|
@ -1,20 +1,18 @@
|
|
|
|
|
import Handlebars from "handlebars"; |
|
|
|
|
import {IWebhookNotificationAdapter} from "nc-plugin"; |
|
|
|
|
import ejs from "ejs"; |
|
|
|
|
import IEmailAdapter from "../../../interface/IEmailAdapter"; |
|
|
|
|
import {BaseModelSql} from "../../dataMapper"; |
|
|
|
|
import Handlebars from 'handlebars'; |
|
|
|
|
import { IWebhookNotificationAdapter } from 'nc-plugin'; |
|
|
|
|
import ejs from 'ejs'; |
|
|
|
|
import IEmailAdapter from '../../../interface/IEmailAdapter'; |
|
|
|
|
import { BaseModelSql } from '../../dataMapper'; |
|
|
|
|
|
|
|
|
|
// import axios from "axios";
|
|
|
|
|
import BaseApiBuilder from "./BaseApiBuilder"; |
|
|
|
|
import formSubmissionEmailTemplate from "./formSubmissionEmailTemplate"; |
|
|
|
|
import BaseApiBuilder from './BaseApiBuilder'; |
|
|
|
|
import formSubmissionEmailTemplate from './formSubmissionEmailTemplate'; |
|
|
|
|
|
|
|
|
|
Handlebars.registerHelper('json', function (context) { |
|
|
|
|
Handlebars.registerHelper('json', function(context) { |
|
|
|
|
return JSON.stringify(context); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private builder: T; |
|
|
|
|
|
|
|
|
|
constructor(args: any, builder: T) { |
|
|
|
@ -23,72 +21,82 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public async beforeInsert(data: any, _trx: any, req): Promise<void> { |
|
|
|
|
await this.handleHooks('before.insert', data, req) |
|
|
|
|
await this.handleHooks('before.insert', data, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async afterInsert(data: any, _trx: any, req): Promise<void> { |
|
|
|
|
await this.handleHooks('after.insert', data, req); |
|
|
|
|
if (req?.headers?.['xc-gui']) { |
|
|
|
|
const id = this._extractPksValues(data); |
|
|
|
|
this.builder.getXcMeta().audit( |
|
|
|
|
this.builder?.getProjectId(), |
|
|
|
|
this.builder?.getDbAlias(), |
|
|
|
|
'nc_audit', |
|
|
|
|
{ |
|
|
|
|
model_name: this._tn, |
|
|
|
|
model_id: id, |
|
|
|
|
op_type: 'DATA', |
|
|
|
|
op_sub_type: 'INSERT', |
|
|
|
|
description: `${id} inserted into ${this._tn}`, |
|
|
|
|
// details: JSON.stringify(data),
|
|
|
|
|
ip: req?.clientIp, |
|
|
|
|
user: req?.user?.email |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
this.builder |
|
|
|
|
.getXcMeta() |
|
|
|
|
.audit( |
|
|
|
|
this.builder?.getProjectId(), |
|
|
|
|
this.builder?.getDbAlias(), |
|
|
|
|
'nc_audit', |
|
|
|
|
{ |
|
|
|
|
model_name: this._tn, |
|
|
|
|
model_id: id, |
|
|
|
|
op_type: 'DATA', |
|
|
|
|
op_sub_type: 'INSERT', |
|
|
|
|
description: `${id} inserted into ${this._tn}`, |
|
|
|
|
// details: JSON.stringify(data),
|
|
|
|
|
ip: req?.clientIp, |
|
|
|
|
user: req?.user?.email |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public async beforeUpdate(data: any, _trx: any, req): Promise<void> { |
|
|
|
|
await this.handleHooks('before.update', data, req) |
|
|
|
|
await this.handleHooks('before.update', data, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public async afterUpdate(data: any, _trx: any, req): Promise<void> { |
|
|
|
|
await this.handleHooks('after.update', data, req) |
|
|
|
|
await this.handleHooks('after.update', data, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public async beforeDelete(data: any, _trx: any, req): Promise<void> { |
|
|
|
|
await this.handleHooks('before.delete', data, req) |
|
|
|
|
await this.handleHooks('before.delete', data, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public async afterDelete(data: any, _trx: any, req): Promise<void> { |
|
|
|
|
if (req?.headers?.['xc-gui']) { |
|
|
|
|
this.builder.getXcMeta().audit( |
|
|
|
|
this.builder?.getProjectId(), |
|
|
|
|
this.builder?.getDbAlias(), |
|
|
|
|
'nc_audit', |
|
|
|
|
{ |
|
|
|
|
model_name: this._tn, |
|
|
|
|
model_id: req?.params?.id, |
|
|
|
|
op_type: 'DATA', |
|
|
|
|
op_sub_type: 'DELETE', |
|
|
|
|
description: `${req?.params.id} deleted from ${this._tn}`, |
|
|
|
|
ip: req?.clientIp, |
|
|
|
|
user: req?.user?.email |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
this.builder |
|
|
|
|
.getXcMeta() |
|
|
|
|
.audit( |
|
|
|
|
this.builder?.getProjectId(), |
|
|
|
|
this.builder?.getDbAlias(), |
|
|
|
|
'nc_audit', |
|
|
|
|
{ |
|
|
|
|
model_name: this._tn, |
|
|
|
|
model_id: req?.params?.id, |
|
|
|
|
op_type: 'DATA', |
|
|
|
|
op_sub_type: 'DELETE', |
|
|
|
|
description: `${req?.params.id} deleted from ${this._tn}`, |
|
|
|
|
ip: req?.clientIp, |
|
|
|
|
user: req?.user?.email |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
await this.handleHooks('after.delete', data, req) |
|
|
|
|
await this.handleHooks('after.delete', data, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private async handleHooks(hookName, data, req): Promise<void> { |
|
|
|
|
// const data = _data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// handle form view data submission
|
|
|
|
|
if (hookName === 'after.insert' && req?.query?.form && this.builder?.formViews?.[this.tn]?.[req.query.form]) { |
|
|
|
|
if ( |
|
|
|
|
hookName === 'after.insert' && |
|
|
|
|
req?.query?.form && |
|
|
|
|
this.builder?.formViews?.[this.tn]?.[req.query.form] |
|
|
|
|
) { |
|
|
|
|
const formView = this.builder?.formViews?.[this.tn]?.[req.query.form]; |
|
|
|
|
const emails = Object.entries(formView?.query_params?.extraViewParams?.formParams?.emailMe || {}).filter(a => a[1]).map(a => a[0]) |
|
|
|
|
const emails = Object.entries( |
|
|
|
|
formView?.query_params?.extraViewParams?.formParams?.emailMe || {} |
|
|
|
|
) |
|
|
|
|
.filter(a => a[1]) |
|
|
|
|
.map(a => a[0]); |
|
|
|
|
if (emails?.length) { |
|
|
|
|
const transformedData = data; |
|
|
|
|
for (const col of this.columns) { |
|
|
|
@ -96,12 +104,27 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
if (typeof transformedData[col._cn] === 'string') { |
|
|
|
|
transformedData[col._cn] = JSON.parse(transformedData[col._cn]); |
|
|
|
|
} |
|
|
|
|
transformedData[col._cn] = (transformedData[col._cn] || []).map((attachment) => { |
|
|
|
|
if (['jpeg', 'gif', 'png', 'apng', 'svg', 'bmp', 'ico', 'jpg'].includes(attachment.title.split('.').pop())) { |
|
|
|
|
return `<a href="${attachment.url}" target="_blank"><img height="50px" src="${attachment.url}"/></a>` |
|
|
|
|
} |
|
|
|
|
return `<a href="${attachment.url}" target="_blank">${attachment.title}</a>` |
|
|
|
|
}).join(' '); |
|
|
|
|
transformedData[col._cn] = (transformedData[col._cn] || []) |
|
|
|
|
.map(attachment => { |
|
|
|
|
if ( |
|
|
|
|
[ |
|
|
|
|
'jpeg', |
|
|
|
|
'gif', |
|
|
|
|
'png', |
|
|
|
|
'apng', |
|
|
|
|
'svg', |
|
|
|
|
'bmp', |
|
|
|
|
'ico', |
|
|
|
|
'jpg' |
|
|
|
|
].includes(attachment.title.split('.').pop()) |
|
|
|
|
) { |
|
|
|
|
return `<a href="${attachment.url}" target="_blank"><img height="50px" src="${attachment.url}"/></a>`; |
|
|
|
|
} |
|
|
|
|
return `<a href="${attachment.url}" target="_blank">${attachment.title}</a>`; |
|
|
|
|
}) |
|
|
|
|
.join(' '); |
|
|
|
|
} else if (typeof transformedData[col._cn] === 'object') { |
|
|
|
|
transformedData[col._cn] = JSON.stringify(transformedData[col._cn]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// todo: notification template
|
|
|
|
@ -113,16 +136,16 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
tn: this.tn, |
|
|
|
|
_tn: this._tn |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
if (this.tn in this.builder.hooks |
|
|
|
|
&& hookName in this.builder.hooks[this.tn] |
|
|
|
|
&& this.builder.hooks[this.tn][hookName] |
|
|
|
|
if ( |
|
|
|
|
this.tn in this.builder.hooks && |
|
|
|
|
hookName in this.builder.hooks[this.tn] && |
|
|
|
|
this.builder.hooks[this.tn][hookName] |
|
|
|
|
) { |
|
|
|
|
|
|
|
|
|
/* if (hookName === 'after.update') { |
|
|
|
|
try { |
|
|
|
|
data = await this.nestedRead(req.params.id, this.defaultNestedQueryParams) |
|
|
|
@ -131,36 +154,65 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
} |
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const hook of this.builder.hooks[this.tn][hookName]) { |
|
|
|
|
if (!hook.active) { |
|
|
|
|
continue |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
console.log('Hook handler ::::' + this.tn + '::::', this.builder.hooks[this.tn][hookName]) |
|
|
|
|
console.log('Hook handler ::::' + this.tn + '::::', data) |
|
|
|
|
|
|
|
|
|
console.log( |
|
|
|
|
'Hook handler ::::' + this.tn + '::::', |
|
|
|
|
this.builder.hooks[this.tn][hookName] |
|
|
|
|
); |
|
|
|
|
console.log('Hook handler ::::' + this.tn + '::::', data); |
|
|
|
|
|
|
|
|
|
if (!this.validateCondition(hook.condition, data, req)) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (hook.notification?.type) { |
|
|
|
|
case 'Email': |
|
|
|
|
this.emailAdapter?.mailSend({ |
|
|
|
|
to: this.parseBody(hook.notification?.payload?.to, req, data, hook.notification?.payload), |
|
|
|
|
subject: this.parseBody(hook.notification?.payload?.subject, req, data, hook.notification?.payload), |
|
|
|
|
html: this.parseBody(hook.notification?.payload?.body, req, data, hook.notification?.payload) |
|
|
|
|
}) |
|
|
|
|
to: this.parseBody( |
|
|
|
|
hook.notification?.payload?.to, |
|
|
|
|
req, |
|
|
|
|
data, |
|
|
|
|
hook.notification?.payload |
|
|
|
|
), |
|
|
|
|
subject: this.parseBody( |
|
|
|
|
hook.notification?.payload?.subject, |
|
|
|
|
req, |
|
|
|
|
data, |
|
|
|
|
hook.notification?.payload |
|
|
|
|
), |
|
|
|
|
html: this.parseBody( |
|
|
|
|
hook.notification?.payload?.body, |
|
|
|
|
req, |
|
|
|
|
data, |
|
|
|
|
hook.notification?.payload |
|
|
|
|
) |
|
|
|
|
}); |
|
|
|
|
break; |
|
|
|
|
case 'URL': |
|
|
|
|
this.handleHttpWebHook(hook.notification?.payload, req, data) |
|
|
|
|
this.handleHttpWebHook(hook.notification?.payload, req, data); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
if (this.webhookNotificationAdapters && hook.notification?.type && hook.notification?.type in this.webhookNotificationAdapters) { |
|
|
|
|
this.webhookNotificationAdapters[hook.notification.type].sendMessage(this.parseBody(hook.notification?.payload?.body, req, data, hook.notification?.payload), hook.notification?.payload) |
|
|
|
|
if ( |
|
|
|
|
this.webhookNotificationAdapters && |
|
|
|
|
hook.notification?.type && |
|
|
|
|
hook.notification?.type in this.webhookNotificationAdapters |
|
|
|
|
) { |
|
|
|
|
this.webhookNotificationAdapters[ |
|
|
|
|
hook.notification.type |
|
|
|
|
].sendMessage( |
|
|
|
|
this.parseBody( |
|
|
|
|
hook.notification?.payload?.body, |
|
|
|
|
req, |
|
|
|
|
data, |
|
|
|
|
hook.notification?.payload |
|
|
|
|
), |
|
|
|
|
hook.notification?.payload |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
break |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// await axios.post(this.builder.hooks[this.tn][hookName].url, {data}, {
|
|
|
|
@ -169,7 +221,7 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch (e) { |
|
|
|
|
console.log('hooks :: error', hookName, e.message) |
|
|
|
|
console.log('hooks :: error', hookName, e.message); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -178,7 +230,7 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
const req = this.axiosRequestMake(apiMeta, apiReq, data); |
|
|
|
|
await require('axios')(req); |
|
|
|
|
} catch (e) { |
|
|
|
|
console.log(e) |
|
|
|
|
console.log(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -186,54 +238,72 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
if (apiMeta.body) { |
|
|
|
|
try { |
|
|
|
|
apiMeta.body = JSON.parse(apiMeta.body, (_key, value) => { |
|
|
|
|
return typeof value === 'string' ? this.parseBody(value, apiReq, data, apiMeta) : value; |
|
|
|
|
return typeof value === 'string' |
|
|
|
|
? this.parseBody(value, apiReq, data, apiMeta) |
|
|
|
|
: value; |
|
|
|
|
}); |
|
|
|
|
} catch (e) { |
|
|
|
|
apiMeta.body = this.parseBody(apiMeta.body, apiReq, data, apiMeta) |
|
|
|
|
apiMeta.body = this.parseBody(apiMeta.body, apiReq, data, apiMeta); |
|
|
|
|
console.log(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (apiMeta.auth) { |
|
|
|
|
try { |
|
|
|
|
apiMeta.auth = JSON.parse(apiMeta.auth, (_key, value) => { |
|
|
|
|
return typeof value === 'string' ? this.parseBody(value, apiReq, data, apiMeta) : value; |
|
|
|
|
return typeof value === 'string' |
|
|
|
|
? this.parseBody(value, apiReq, data, apiMeta) |
|
|
|
|
: value; |
|
|
|
|
}); |
|
|
|
|
} catch (e) { |
|
|
|
|
apiMeta.auth = this.parseBody(apiMeta.auth, apiReq, data, apiMeta) |
|
|
|
|
apiMeta.auth = this.parseBody(apiMeta.auth, apiReq, data, apiMeta); |
|
|
|
|
console.log(e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
apiMeta.response = {}; |
|
|
|
|
const req = { |
|
|
|
|
params: apiMeta.parameters ? apiMeta.parameters.reduce((paramsObj, param) => { |
|
|
|
|
if (param.name && param.enabled) { |
|
|
|
|
paramsObj[param.name] = this.parseBody(param.value, apiReq, data, apiMeta); |
|
|
|
|
} |
|
|
|
|
return paramsObj; |
|
|
|
|
}, {}) : {}, |
|
|
|
|
params: apiMeta.parameters |
|
|
|
|
? apiMeta.parameters.reduce((paramsObj, param) => { |
|
|
|
|
if (param.name && param.enabled) { |
|
|
|
|
paramsObj[param.name] = this.parseBody( |
|
|
|
|
param.value, |
|
|
|
|
apiReq, |
|
|
|
|
data, |
|
|
|
|
apiMeta |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
return paramsObj; |
|
|
|
|
}, {}) |
|
|
|
|
: {}, |
|
|
|
|
url: this.parseBody(apiMeta.path, apiReq, data, apiMeta), |
|
|
|
|
method: apiMeta.method, |
|
|
|
|
data: apiMeta.body, |
|
|
|
|
headers: apiMeta.headers ? apiMeta.headers.reduce((headersObj, header) => { |
|
|
|
|
if (header.name && header.enabled) { |
|
|
|
|
headersObj[header.name] = this.parseBody(header.value, apiReq, data, apiMeta); |
|
|
|
|
} |
|
|
|
|
return headersObj; |
|
|
|
|
}, {}) : {}, |
|
|
|
|
headers: apiMeta.headers |
|
|
|
|
? apiMeta.headers.reduce((headersObj, header) => { |
|
|
|
|
if (header.name && header.enabled) { |
|
|
|
|
headersObj[header.name] = this.parseBody( |
|
|
|
|
header.value, |
|
|
|
|
apiReq, |
|
|
|
|
data, |
|
|
|
|
apiMeta |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
return headersObj; |
|
|
|
|
}, {}) |
|
|
|
|
: {}, |
|
|
|
|
withCredentials: true |
|
|
|
|
}; |
|
|
|
|
return req; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
private get emailAdapter(): IEmailAdapter { |
|
|
|
|
return this.builder?.app?.metaMgr?.emailAdapter; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
private get webhookNotificationAdapters(): { [key: string]: IWebhookNotificationAdapter } { |
|
|
|
|
private get webhookNotificationAdapters(): { |
|
|
|
|
[key: string]: IWebhookNotificationAdapter; |
|
|
|
|
} { |
|
|
|
|
return this.builder?.app?.metaMgr?.webhookNotificationAdapters; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -253,28 +323,34 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
res = data[field] !== con.value; |
|
|
|
|
break; |
|
|
|
|
case 'is like': |
|
|
|
|
res = data[field]?.toLowerCase()?.indexOf(con.value?.toLowerCase()) > -1; |
|
|
|
|
res = |
|
|
|
|
data[field]?.toLowerCase()?.indexOf(con.value?.toLowerCase()) > -1; |
|
|
|
|
break; |
|
|
|
|
case 'is not like': |
|
|
|
|
res = data[field]?.toLowerCase()?.indexOf(con.value?.toLowerCase()) === -1; |
|
|
|
|
res = |
|
|
|
|
data[field]?.toLowerCase()?.indexOf(con.value?.toLowerCase()) === |
|
|
|
|
-1; |
|
|
|
|
break; |
|
|
|
|
case 'is empty': |
|
|
|
|
res = data[field] === '' || data[field] === null || data[field] === undefined; |
|
|
|
|
res = |
|
|
|
|
data[field] === '' || |
|
|
|
|
data[field] === null || |
|
|
|
|
data[field] === undefined; |
|
|
|
|
break; |
|
|
|
|
case 'is not empty': |
|
|
|
|
res = !(data[field] === '' || data[field] === null || data[field] === undefined); |
|
|
|
|
res = !( |
|
|
|
|
data[field] === '' || |
|
|
|
|
data[field] === null || |
|
|
|
|
data[field] === undefined |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
case 'is null': |
|
|
|
|
res = |
|
|
|
|
res = data[field] === null; |
|
|
|
|
res = res = data[field] === null; |
|
|
|
|
break; |
|
|
|
|
case 'is not null': |
|
|
|
|
res = data[field] !== null; |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* todo: case '<': |
|
|
|
|
return condition + `~not(${filt.field},lt,${filt.value})`; |
|
|
|
|
case '<=': |
|
|
|
@ -285,25 +361,28 @@ class BaseModel<T extends BaseApiBuilder<any>> extends BaseModelSql {
|
|
|
|
|
return condition + `~not(${filt.field},ge,${filt.value})`;*/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return con.logicOp === 'or' ? valid || res : valid && res; |
|
|
|
|
|
|
|
|
|
}, true); |
|
|
|
|
|
|
|
|
|
return isValid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private parseBody(template: string, req: any, data: any, payload: any): string { |
|
|
|
|
private parseBody( |
|
|
|
|
template: string, |
|
|
|
|
req: any, |
|
|
|
|
data: any, |
|
|
|
|
payload: any |
|
|
|
|
): string { |
|
|
|
|
if (!template) { |
|
|
|
|
return template; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Handlebars.compile(template, {noEscape: true})({ |
|
|
|
|
return Handlebars.compile(template, { noEscape: true })({ |
|
|
|
|
data, |
|
|
|
|
user: req?.user, |
|
|
|
|
payload, |
|
|
|
|
env: process.env |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|