@opengis/pay 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.js +7 -0
- package/module/pay/pt/marketplace-service-payment-client-template.html +7 -20
- package/package.json +15 -31
- package/plugin.js +13 -10
- package/server/plugins/crud/dataDelete.js +82 -0
- package/server/plugins/crud/dataInsert.js +76 -0
- package/server/plugins/crud/dataUpdate.js +112 -0
- package/server/plugins/{cron.js → hook.js} +24 -22
- package/server/plugins/pg/funcs/autoIndex.js +102 -0
- package/server/plugins/pg/funcs/getDBParams.js +15 -0
- package/server/plugins/pg/funcs/getMeta.js +48 -0
- package/server/plugins/pg/funcs/getPG.js +39 -0
- package/server/plugins/pg/funcs/getPGAsync.js +40 -0
- package/server/plugins/pg/funcs/init.js +113 -0
- package/server/plugins/pg/index.js +24 -0
- package/server/plugins/pg/pgClients.js +22 -0
- package/server/plugins/redis/client.js +8 -0
- package/server/plugins/redis/funcs/getRedis.js +24 -0
- package/server/plugins/redis/funcs/redisClients.js +3 -0
- package/server/plugins/redis/index.js +11 -0
- package/server/routes/liqpay/controllers/liqpay.redirect.js +7 -11
- package/server/routes/liqpay/controllers/liqpay.status.js +26 -15
- package/server/routes/liqpay/utils/check.status.js +1 -1
- package/server/routes/monopay/controllers/monopay.redirect.js +141 -0
- package/server/routes/monopay/controllers/monopay.status.js +70 -0
- package/server/routes/monopay/index.mjs +32 -0
- package/server/routes/portmone/controllers/payment.info.js +9 -9
- package/server/routes/portmone/controllers/portmone.redirect.js +23 -17
- package/server/routes/portmone/controllers/portmone.status.js +49 -38
- package/server/routes/portmone/utils/portmone.check.status.js +34 -33
- package/server/utils/cron/addCron.js +48 -0
- package/server/utils/cron/cronList.js +1 -0
- package/server/utils/cron/interval2ms.js +40 -0
- package/server/utils/cron/runCron.js +24 -0
- package/server/utils/cron/verifyUnique.js +23 -0
- package/server/utils/getFolder.js +11 -0
- package/server/utils/getInsertQuery.js +44 -0
- package/server/utils/migration/exec.migrations.js +38 -0
- package/server/utils/migration/exec.sql.js +48 -0
- package/server/utils/sendNotification.js +69 -0
- package/module/pay1/card/billing.accounts.table/general_info.hbs +0 -11
- package/module/pay1/card/billing.accounts.table/index.yml +0 -15
- package/module/pay1/card/billing.accounts.table/users.hbs +0 -12
- package/module/pay1/card/billing.payments.table/general_info.hbs +0 -13
- package/module/pay1/card/billing.payments.table/index.yml +0 -10
- package/module/pay1/card/billing.users.table/general_info.hbs +0 -11
- package/module/pay1/card/billing.users.table/index.yml +0 -10
- package/module/pay1/cls/billing.payment_status.json +0 -22
- package/module/pay1/form/billing.account_user.form.json +0 -15
- package/module/pay1/form/billing.accounts.form.json +0 -31
- package/module/pay1/form/billing.payment.form.json +0 -23
- package/module/pay1/form/billing.users.form.json +0 -39
- package/module/pay1/menu.json +0 -24
- package/module/pay1/pt/marketplace-service-payment-client-template.html +0 -147
- package/module/pay1/select/billing.account_id.sql +0 -1
- package/module/pay1/select/billing.service_id.sql +0 -1
- package/module/pay1/select/billing.subscription_id.sql +0 -1
- package/module/pay1/select/billing.user_id.sql +0 -1
- package/module/pay1/table/billing.accounts.table.json +0 -61
- package/module/pay1/table/billing.payments.table.json +0 -68
- package/module/pay1/table/billing.users.table.json +0 -71
package/config.js
ADDED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
<tr>
|
|
18
18
|
<td style="padding:7px 0" align="center"><span style="color:#555454;font-family:'Open-sans',sans-serif;font-size:small">
|
|
19
|
-
<span style="font-weight:500;font-size:16px;text-transform:uppercase;line-height:25px">{{
|
|
19
|
+
<span style="font-weight:500;font-size:16px;text-transform:uppercase;line-height:25px">{{username}}, дякуємо за здійснення замовлення на порталі <a href="//{{domain}}">{{domain}}</a></span> </span>
|
|
20
20
|
</td>
|
|
21
21
|
</tr>
|
|
22
22
|
<tr>
|
|
@@ -30,24 +30,17 @@
|
|
|
30
30
|
<td style="padding:7px 0" width="10"> </td>
|
|
31
31
|
<td style="padding:7px 0">
|
|
32
32
|
<span style="color:#555454;font-family:'Open-sans',sans-serif;font-size:small"> </span>
|
|
33
|
-
{{#if service_id}}
|
|
34
|
-
<p style="color:#61677b;border-bottom:1px solid #61677b;margin:3px 0 7px;text-transform:uppercase;font-weight:500;font-size:18px;padding-bottom:10px">Послуга: {{service_id_text}}</p>
|
|
35
|
-
{{/if}}
|
|
36
33
|
<br>
|
|
37
|
-
<span style="color:#61677b"><strong>Дата
|
|
34
|
+
<span style="color:#61677b"><strong>Дата створення платежу:</strong> {{created_at}}</span>
|
|
38
35
|
<br>
|
|
39
|
-
<span style="color:#61677b"><strong>Статус
|
|
40
|
-
{{#if withdrawal_sum}}
|
|
41
|
-
<br>
|
|
42
|
-
<span style="color:#61677b"><strong>Вартість послуги:</strong> {{num_format withdrawal_sum dec="2"}}</span>
|
|
43
|
-
{{/if}}
|
|
36
|
+
<span style="color:#61677b"><strong>Статус платежу:</strong> {{payment_status}}</span>
|
|
44
37
|
{{#if payment_amount}}
|
|
45
38
|
<br>
|
|
46
|
-
<span style="color:#61677b"><strong>Поповнення рахунку:</strong> {{
|
|
39
|
+
<span style="color:#61677b"><strong>Поповнення рахунку:</strong> {{payment_amount}}</span>
|
|
47
40
|
{{/if}}
|
|
48
41
|
{{#if total_balance}}
|
|
49
42
|
<br>
|
|
50
|
-
<span style="color:#61677b"><strong>Поточний баланс:</strong> {{
|
|
43
|
+
<span style="color:#61677b"><strong>Поточний баланс:</strong> {{total_balance}}</span>
|
|
51
44
|
{{/if}}
|
|
52
45
|
</td>
|
|
53
46
|
<td style="padding:7px 0" width="10"> </td>
|
|
@@ -73,13 +66,7 @@
|
|
|
73
66
|
<tr>
|
|
74
67
|
<td style="padding: 0 10px">
|
|
75
68
|
<font size="2" face="Open-sans, sans-serif" color="#555454">
|
|
76
|
-
<strong>
|
|
77
|
-
{{#if service_id}}
|
|
78
|
-
{{service_id_text}}
|
|
79
|
-
{{^}}
|
|
80
|
-
Поповнення рахунку
|
|
81
|
-
{{/if}}
|
|
82
|
-
</strong>
|
|
69
|
+
<strong>Поповнення рахунку</strong>
|
|
83
70
|
</font>
|
|
84
71
|
</td>
|
|
85
72
|
</tr>
|
|
@@ -92,7 +79,7 @@
|
|
|
92
79
|
<tr>
|
|
93
80
|
<td width="10"> </td>
|
|
94
81
|
<td align="right">
|
|
95
|
-
<font size="2" face="Open-sans, sans-serif" color="#555454"> {{
|
|
82
|
+
<font size="2" face="Open-sans, sans-serif" color="#555454"> {{payment_amount}} грн.</font>
|
|
96
83
|
</td>
|
|
97
84
|
<td width="10"> </td>
|
|
98
85
|
</tr>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengis/pay",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "pay plugins for billing",
|
|
6
6
|
"main": "plugin.js",
|
|
@@ -8,44 +8,28 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"server/*",
|
|
10
10
|
"module/*",
|
|
11
|
-
"plugin.js"
|
|
11
|
+
"plugin.js",
|
|
12
|
+
"config.js",
|
|
13
|
+
"utils.js"
|
|
12
14
|
],
|
|
13
15
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"docs:build": "vitepress build docs",
|
|
16
|
-
"docs:preview": "vitepress preview docs",
|
|
17
|
-
"test": "node --test test/**/*.test.js",
|
|
16
|
+
"test": "node --test",
|
|
18
17
|
"start": "node --watch-path=server server",
|
|
19
|
-
"admin": "
|
|
18
|
+
"admin": "set NODE_ENV=admin&& npm run start",
|
|
20
19
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
|
21
20
|
},
|
|
22
21
|
"dependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"request-promise": "
|
|
22
|
+
"fastify": "5.3.3",
|
|
23
|
+
"fastify-plugin": "5.0.1",
|
|
24
|
+
"ioredis": "5.3.2",
|
|
25
|
+
"liqpayjs-sdk": "3.3.1",
|
|
26
|
+
"nodemailer": "6.5.0",
|
|
27
|
+
"pg": "8.11.3",
|
|
28
|
+
"request-promise": "4.2.6"
|
|
30
29
|
},
|
|
31
30
|
"devDependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"@vue/eslint-config-prettier": "^9.0.0",
|
|
35
|
-
"@vue/eslint-config-typescript": "^12.0.0",
|
|
36
|
-
"eslint": "^8.49.0",
|
|
37
|
-
"eslint-config-airbnb": "^19.0.4",
|
|
38
|
-
"eslint-plugin-import": "^2.25.3",
|
|
39
|
-
"eslint-plugin-playwright": "^1.5.2",
|
|
40
|
-
"eslint-plugin-vue": "^9.17.0",
|
|
41
|
-
"markdown-it-abbr": "^2.0.0",
|
|
42
|
-
"mermaid": "^10.9.0",
|
|
43
|
-
"sass": "^1.72.0",
|
|
44
|
-
"vitepress": "^1.1.4",
|
|
45
|
-
"vitepress-plugin-mermaid": "^2.0.16",
|
|
46
|
-
"vitepress-plugin-tabs": "^0.5.0",
|
|
47
|
-
"vitepress-sidebar": "^1.22.0",
|
|
48
|
-
"vue": "^3.4.21"
|
|
31
|
+
"eslint": "8.49.0",
|
|
32
|
+
"eslint-config-airbnb": "19.0.4"
|
|
49
33
|
},
|
|
50
34
|
"author": "Softpro",
|
|
51
35
|
"license": "ISC"
|
package/plugin.js
CHANGED
|
@@ -2,12 +2,10 @@ import fp from 'fastify-plugin';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import getPGAsync from './server/plugins/pg/funcs/getPGAsync.js';
|
|
6
|
+
import execMigrations from './server/utils/migration/exec.migrations.js';
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const filename = fileURLToPath(import.meta.url);
|
|
10
|
-
const dirname = path.dirname(filename);
|
|
8
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* This plugins adds some utilities to handle http errors
|
|
@@ -15,12 +13,17 @@ const dirname = path.dirname(filename);
|
|
|
15
13
|
* @see https://github.com/fastify/fastify-sensible
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
|
-
async function plugin(app) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
async function plugin(app, config = {}) {
|
|
17
|
+
config.prefix = config.prefix || '/api';
|
|
18
|
+
|
|
19
|
+
const pg = await getPGAsync(config.pg || {});
|
|
20
|
+
|
|
21
|
+
app.register(import('./server/plugins/hook.js'), config);
|
|
22
|
+
app.register(import('./server/routes/portmone/index.mjs'), config);
|
|
23
|
+
app.register(import('./server/routes/liqpay/index.mjs'), config);
|
|
24
|
+
app.register(import('./server/routes/monopay/index.mjs'), config);
|
|
22
25
|
|
|
23
|
-
execMigrations(path.join(dirname, 'server/migrations')).catch(err => console.log(err));
|
|
26
|
+
execMigrations(path.join(dirname, 'server/migrations'), pg).catch(err => console.log(err));
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export default fp(plugin);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import config from '../../../config.js';
|
|
2
|
+
import getPG from '../pg/funcs/getPG.js';
|
|
3
|
+
import getMeta from '../pg/funcs/getMeta.js';
|
|
4
|
+
import getRedis from '../redis/funcs/getRedis.js';
|
|
5
|
+
import pgClients from '../pg/pgClients.js';
|
|
6
|
+
|
|
7
|
+
const rclient = getRedis();
|
|
8
|
+
|
|
9
|
+
export default async function dataDelete({
|
|
10
|
+
table: table1, tokenData, referer, id, pg: pg1, uid,
|
|
11
|
+
}) {
|
|
12
|
+
const pg = pg1 || getPG({ name: 'client' });
|
|
13
|
+
|
|
14
|
+
// pg client single transaction support
|
|
15
|
+
if (!pg?.pk && config.pg) {
|
|
16
|
+
pg.options = pgClients.client?.options;
|
|
17
|
+
pg.tlist = pgClients.client?.tlist;
|
|
18
|
+
pg.pgType = pgClients.client?.pgType;
|
|
19
|
+
pg.relkinds = pgClients.client?.relkinds;
|
|
20
|
+
pg.pk = pgClients.client?.pk;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const table = table1.replace(/"/g, '');
|
|
24
|
+
|
|
25
|
+
const { pk } = await getMeta({ pg, table });
|
|
26
|
+
|
|
27
|
+
if (!pg.tlist?.includes(table1)) return 'table not exist';
|
|
28
|
+
|
|
29
|
+
const delQuery = `delete from ${table} WHERE ${pk}::text = $1::text returning *`;
|
|
30
|
+
|
|
31
|
+
// for transactions
|
|
32
|
+
const isClient = typeof pg.query === 'function' && typeof pg.release === 'function';
|
|
33
|
+
const client = isClient ? pg : await pg.connect();
|
|
34
|
+
|
|
35
|
+
if (isClient || !client.pk) {
|
|
36
|
+
client.options = pg.options;
|
|
37
|
+
client.tlist = pg.tlist;
|
|
38
|
+
client.pgType = pg.pgType;
|
|
39
|
+
client.relkinds = pg.relkinds;
|
|
40
|
+
client.pk = pg.pk;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
if (!isClient) {
|
|
45
|
+
await client.query('begin;');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const row = {};
|
|
49
|
+
|
|
50
|
+
const res = await client.query(delQuery, [id])
|
|
51
|
+
.then(el => (el.rows?.[0] ? { rowCount: 1, ...el.rows[0] } : {}));
|
|
52
|
+
|
|
53
|
+
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
|
|
54
|
+
|
|
55
|
+
if (!isClient) {
|
|
56
|
+
await client.query('commit;');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { ...res, ...row };
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.error(
|
|
63
|
+
'crud/delete',
|
|
64
|
+
err.toString(),
|
|
65
|
+
err.stack,
|
|
66
|
+
table,
|
|
67
|
+
id,
|
|
68
|
+
referer,
|
|
69
|
+
uid,
|
|
70
|
+
tokenData?.form,
|
|
71
|
+
);
|
|
72
|
+
if (!isClient) {
|
|
73
|
+
await client.query('rollback;');
|
|
74
|
+
}
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
if (!isClient) {
|
|
79
|
+
await client.query('begin;');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import config from '../../../config.js';
|
|
2
|
+
import getPG from '../pg/funcs/getPG.js';
|
|
3
|
+
import getRedis from '../redis/funcs/getRedis.js';
|
|
4
|
+
import pgClients from '../pg/pgClients.js';
|
|
5
|
+
import getInsertQuery from '../../utils/getInsertQuery.js';
|
|
6
|
+
|
|
7
|
+
const rclient = getRedis();
|
|
8
|
+
|
|
9
|
+
export default async function dataInsert({
|
|
10
|
+
id, table: table1, referer, data, pg: pg1, uid, tokenData = {},
|
|
11
|
+
}) {
|
|
12
|
+
const pg = pg1 || getPG({ name: 'client' });
|
|
13
|
+
if (!pg) return null;
|
|
14
|
+
|
|
15
|
+
// pg client single transaction support
|
|
16
|
+
if (!pg?.pk && config.pg) {
|
|
17
|
+
pg.options = pgClients.client?.options;
|
|
18
|
+
pg.tlist = pgClients.client?.tlist;
|
|
19
|
+
pg.pgType = pgClients.client?.pgType;
|
|
20
|
+
pg.relkinds = pgClients.client?.relkinds;
|
|
21
|
+
pg.pk = pgClients.client?.pk;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { unittest } = tokenData || {};
|
|
25
|
+
|
|
26
|
+
if (config.trace || unittest) console.log('form', tokenData?.form);
|
|
27
|
+
|
|
28
|
+
const table = pg.pk[table1] ? table1 : table1.replace(/"/g, '');
|
|
29
|
+
|
|
30
|
+
const { insertQuery, args = [] } = await getInsertQuery({
|
|
31
|
+
pg, table, data, id, uid,
|
|
32
|
+
}) || {};
|
|
33
|
+
|
|
34
|
+
if (!insertQuery || !args.length) return null;
|
|
35
|
+
|
|
36
|
+
// for transactions
|
|
37
|
+
const isClient = typeof pg.query === 'function' && typeof pg.release === 'function';
|
|
38
|
+
const client = isClient ? pg : await pg.connect();
|
|
39
|
+
|
|
40
|
+
if (isClient || !client.pk) {
|
|
41
|
+
client.options = pg.options;
|
|
42
|
+
client.tlist = pg.tlist;
|
|
43
|
+
client.pgType = pg.pgType;
|
|
44
|
+
client.relkinds = pg.relkinds;
|
|
45
|
+
client.pk = pg.pk;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
if (!isClient) {
|
|
50
|
+
await client.query('begin;');
|
|
51
|
+
}
|
|
52
|
+
const res = await client.query(insertQuery, args).then(el => el || {});
|
|
53
|
+
|
|
54
|
+
const id1 = res.rows?.[0]?.[pg.pk[table1]];
|
|
55
|
+
|
|
56
|
+
if (!id1) return null;
|
|
57
|
+
|
|
58
|
+
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
|
|
59
|
+
if (!isClient) {
|
|
60
|
+
await client.query('commit;');
|
|
61
|
+
}
|
|
62
|
+
return res;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error('crud/insert', err.toString(), err.stack, table, id, referer, uid, tokenData?.form);
|
|
66
|
+
if (!isClient) {
|
|
67
|
+
await client.query('rollback;');
|
|
68
|
+
}
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (!isClient) {
|
|
73
|
+
client.release();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import getPG from '../pg/funcs/getPG.js';
|
|
2
|
+
import getMeta from '../pg/funcs/getMeta.js';
|
|
3
|
+
import getRedis from '../redis/funcs/getRedis.js';
|
|
4
|
+
import pgClients from '../pg/pgClients.js';
|
|
5
|
+
import config from '../../../config.js';
|
|
6
|
+
|
|
7
|
+
const rclient = getRedis();
|
|
8
|
+
|
|
9
|
+
export default async function dataUpdate({
|
|
10
|
+
table, tokenData, referer, id, data, pg: pg1, uid,
|
|
11
|
+
}) {
|
|
12
|
+
if (!data || !table || !id) return null;
|
|
13
|
+
|
|
14
|
+
const pg = pg1 || getPG({ name: 'client' });
|
|
15
|
+
if (!pg) return null;
|
|
16
|
+
|
|
17
|
+
// pg client single transaction support
|
|
18
|
+
if (!pg?.pk && config.pg) {
|
|
19
|
+
pg.options = pgClients.client?.options;
|
|
20
|
+
pg.tlist = pgClients.client?.tlist;
|
|
21
|
+
pg.pgType = pgClients.client?.pgType;
|
|
22
|
+
pg.relkinds = pgClients.client?.relkinds;
|
|
23
|
+
pg.pk = pgClients.client?.pk;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { columns, pk } = await getMeta({ pg, table });
|
|
27
|
+
|
|
28
|
+
if (!columns) return null;
|
|
29
|
+
|
|
30
|
+
const names = columns.map((el) => el.name);
|
|
31
|
+
const types = columns.reduce((acc, { name, dataTypeID }) => ({ ...acc, [name]: pg.pgType?.[dataTypeID] }), {});
|
|
32
|
+
|
|
33
|
+
const filterData = Object.keys(data)
|
|
34
|
+
.filter((el) => (/* typeof data[el] === 'boolean' ? true : data[el] && */ names?.includes(el) && !['editor_date', 'editor_id', 'updated_by', 'updated_at'].includes(el)));
|
|
35
|
+
|
|
36
|
+
const systemColumns = [['editor_date', 'now()'], ['updated_at', 'now()'], uid ? ['editor_id', `'${uid.replace(/'/g, "''")}'`] : null, uid ? ['updated_by', `'${uid.replace(/'/g, "''")}'`] : null].filter((el) => el && names.includes(el[0])).map((el) => `${el[0]} = ${el[1]}`).join(',');
|
|
37
|
+
|
|
38
|
+
const filterValue = filterData.map((el) => {
|
|
39
|
+
const { dataTypeID = 25 } = columns.find((col) => col?.name === el) || {};
|
|
40
|
+
if (pg.pgType[dataTypeID]?.endsWith('[]') && ['string', 'number'].includes(typeof data[el])) {
|
|
41
|
+
Object.assign(data, { [el]: data[el].split(',') });
|
|
42
|
+
}
|
|
43
|
+
return [el, data[el]];
|
|
44
|
+
}).map((el) => (typeof el[1] === 'object' && types[el[0]]?.includes?.('json') && el[1] ? JSON.stringify(el[1]) : el[1]));
|
|
45
|
+
|
|
46
|
+
const updateQuery = `UPDATE ${table} SET ${systemColumns ? `${systemColumns}${filterData?.length ? ',' : ''}` : ''}
|
|
47
|
+
${filterData?.map((key, i) => `"${key}"=$${i + 2}`)?.join(',')}
|
|
48
|
+
WHERE ${pk}::text = $1::text returning *`;
|
|
49
|
+
// console.log(updateQuery, filterValue);
|
|
50
|
+
|
|
51
|
+
// for transactions
|
|
52
|
+
const isClient = typeof pg.query === 'function' && typeof pg.release === 'function';
|
|
53
|
+
const client = isClient ? pg : await pg.connect();
|
|
54
|
+
|
|
55
|
+
if (isClient || !client.pk) {
|
|
56
|
+
client.options = pg.options;
|
|
57
|
+
client.tlist = pg.tlist;
|
|
58
|
+
client.pgType = pg.pgType;
|
|
59
|
+
client.relkinds = pg.relkinds;
|
|
60
|
+
client.pk = pg.pk;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
if (!isClient) {
|
|
65
|
+
await client.query('begin;');
|
|
66
|
+
}
|
|
67
|
+
const res = await client.query(updateQuery, [id, ...filterValue])
|
|
68
|
+
.catch(err => {
|
|
69
|
+
console.error(
|
|
70
|
+
'crud/update',
|
|
71
|
+
err.toString(),
|
|
72
|
+
err.stack,
|
|
73
|
+
table,
|
|
74
|
+
id,
|
|
75
|
+
referer,
|
|
76
|
+
uid,
|
|
77
|
+
data,
|
|
78
|
+
updateQuery,
|
|
79
|
+
);
|
|
80
|
+
throw err;
|
|
81
|
+
})
|
|
82
|
+
.then(el => el?.rows?.[0]) || {};
|
|
83
|
+
|
|
84
|
+
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
|
|
85
|
+
|
|
86
|
+
if (!isClient) {
|
|
87
|
+
await client.query('commit;');
|
|
88
|
+
}
|
|
89
|
+
return res || {};
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
console.error(
|
|
93
|
+
'crud/update',
|
|
94
|
+
err.toString(),
|
|
95
|
+
err.stack,
|
|
96
|
+
table,
|
|
97
|
+
id,
|
|
98
|
+
referer,
|
|
99
|
+
uid,
|
|
100
|
+
tokenData?.form,
|
|
101
|
+
);
|
|
102
|
+
if (!isClient) {
|
|
103
|
+
await client.query('rollback;');
|
|
104
|
+
}
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
if (!isClient) {
|
|
109
|
+
client.release();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
// import addCron from '../utils/cron/addCron.js';
|
|
2
|
+
import pgClients from './pg/pgClients.js';
|
|
3
|
+
|
|
4
|
+
import updateTrxStatus from '../routes/portmone/utils/portmone.check.status.js';
|
|
5
|
+
|
|
6
|
+
// to do: for liqpay and monopay
|
|
7
|
+
async function updateOldTrxsStatus() {
|
|
8
|
+
const inprogress = pgClients.client.pk?.['billing.payments']
|
|
9
|
+
? await pgClients.client.query(`select array_agg(payment_id) as inprogress from billing.payments
|
|
10
|
+
where payment_service='portmone' and payment_status = 'inprogress' and (now() - cdate) > '1 hour'::interval`).then(el => el.rows?.[0]?.inprogress || [])
|
|
11
|
+
: [];
|
|
12
|
+
|
|
13
|
+
if (!inprogress?.length) return 'empty rows';
|
|
14
|
+
|
|
15
|
+
const res = await Promise.all(inprogress.map(async (paymentId) => {
|
|
16
|
+
const data = await updateTrxStatus({ paymentId });
|
|
17
|
+
return { paymentId, data };
|
|
18
|
+
}));
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default async function cron() {
|
|
23
|
+
// addCron(updateOldTrxsStatus, 60 * 60 * 24);
|
|
24
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import config from '../../../../config.js';
|
|
2
|
+
import pgClient from '../pgClients.js';
|
|
3
|
+
|
|
4
|
+
// subfunc
|
|
5
|
+
const getTable = ({ table }) => table.toLowerCase().replace(/[\n\r]+/g, ' ').split(' from ')
|
|
6
|
+
.filter((el) => /^[a-z0-9_]+\.[a-z0-9_]+/.test(el))
|
|
7
|
+
.map((el) => el.split(/[ )]/)[0]);
|
|
8
|
+
|
|
9
|
+
const loadedIndex = {};
|
|
10
|
+
|
|
11
|
+
// main func
|
|
12
|
+
async function autoIndex({
|
|
13
|
+
table, columns: filter1, pg = pgClient.client, gin,
|
|
14
|
+
}) {
|
|
15
|
+
const filter = filter1.filter(el => el);
|
|
16
|
+
|
|
17
|
+
if (!filter?.length || !table || process.env.NODE_ENV === 'unit test') return null;
|
|
18
|
+
|
|
19
|
+
const attrs = filter.map((el) => (el.name || el).replace(/'/g, '')).filter(el => el?.indexOf('(') === -1).sort();
|
|
20
|
+
const types = filter.filter(el => (el?.name || el)?.indexOf?.('(') === -1).map((el) => (el.name ? el : { name: el }))
|
|
21
|
+
.reduce((p, el) => ({ ...p, [el.name]: el.type || '-' }), {});
|
|
22
|
+
|
|
23
|
+
const redisKey = `autoindex1:${table}:${attrs.join(';')}`;
|
|
24
|
+
const tableList = getTable({ table });
|
|
25
|
+
const existsCache = loadedIndex[redisKey];
|
|
26
|
+
if (existsCache && !config.disableCache) { return 'ok'; }
|
|
27
|
+
|
|
28
|
+
const tbs = tableList[0];
|
|
29
|
+
const [ns, tbl] = tableList[0].split('.');
|
|
30
|
+
const { rows: index } = await pg.query(`select * from pg_indexes where tablename = '${tbl}' and schemaname = '${ns}'`);
|
|
31
|
+
|
|
32
|
+
if (existsCache && !config.disableCache && index?.length > 0) { return null; }
|
|
33
|
+
// console.log('autoindex', table, filter?.map((el) => el.name || el));
|
|
34
|
+
const { rows: cols } = await pg.query(`SELECT attrelid::regclass AS tbl, attname AS aname, atttypid::regtype AS datatype, atttypid
|
|
35
|
+
FROM pg_attribute WHERE attrelid = '${tbs}'::regclass and attname = any('{ ${attrs} }')`);
|
|
36
|
+
|
|
37
|
+
const qIndex = [];
|
|
38
|
+
const indexAr = {};
|
|
39
|
+
|
|
40
|
+
// console.log(cols);
|
|
41
|
+
for (let i = 0; i < cols.length; i += 1) {
|
|
42
|
+
const el = cols[i];
|
|
43
|
+
el.relname = tbl;
|
|
44
|
+
el.nspname = ns;
|
|
45
|
+
|
|
46
|
+
const isGin = types[el.aname] === 'Text' || gin;
|
|
47
|
+
const indexUsing = (el.datatype === 'geometry' ? 'gist' : null)
|
|
48
|
+
|| (isGin ? 'gin' : null)
|
|
49
|
+
|| 'btree';
|
|
50
|
+
|
|
51
|
+
const name = `${el.nspname}_${el.relname}_${el.aname}_${indexUsing}_idx`;
|
|
52
|
+
|
|
53
|
+
const exists = index.filter((ind) => ind.tablename === el.relname && ind.schemaname === el.nspname && ind.indexdef.includes(
|
|
54
|
+
isGin ? `gin (${el.aname} gin_trgm_ops)` : `btree (${el.aname})`,
|
|
55
|
+
));
|
|
56
|
+
|
|
57
|
+
indexAr[el.aname] = {
|
|
58
|
+
name, type: el.datatype, exists, // exists1,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// drop
|
|
62
|
+
/* if (exists?.length > 1) {
|
|
63
|
+
exists.filter((ind, i1) => i1).forEach((ind) => qIndex.push(`DROP INDEX if exists ${ind.schemaname}.${ind.indexname} `));
|
|
64
|
+
} */
|
|
65
|
+
|
|
66
|
+
// add
|
|
67
|
+
if (exists?.length <= (isGin ? 2 : 1)) {
|
|
68
|
+
// console.log(`${name} - ${el.datatype}`);
|
|
69
|
+
|
|
70
|
+
const suffix = isGin ? 'gin_trgm_ops' : '';
|
|
71
|
+
|
|
72
|
+
qIndex.push(`CREATE INDEX if not exists ${name} ON ${el.nspname}.${el.relname} USING ${indexUsing} (${el.aname} ${suffix})`);
|
|
73
|
+
if (isGin) {
|
|
74
|
+
qIndex.push(`CREATE INDEX if not exists ${name}_lower ON ${el.nspname}.${el.relname} USING ${indexUsing} (lower(${el.aname}) ${suffix})`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (indexUsing === 'gist') {
|
|
78
|
+
qIndex.push(`CREATE INDEX if not exists ${name.replace('gist', 'gist1')}
|
|
79
|
+
ON ${el.nspname}.${el.relname} USING gist (${el.aname})`);
|
|
80
|
+
|
|
81
|
+
qIndex.push(`CREATE INDEX if not exists ${name.replace('gist', 'area')}
|
|
82
|
+
ON ${el.nspname}.${el.relname} USING btree (st_area(st_transform(${el.aname},3857)))`);
|
|
83
|
+
qIndex.push(`CREATE INDEX if not exists ${name.replace('gist', 'area_true')}
|
|
84
|
+
ON ${el.nspname}.${el.relname} USING btree (st_area(${el.aname}, true))`);
|
|
85
|
+
qIndex.push(`CREATE INDEX if not exists ${name.replace('gist', '4326')}
|
|
86
|
+
ON ${el.nspname}.${el.relname} USING gist (st_transform(${el.aname},4326))`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
loadedIndex[redisKey] = 1;
|
|
92
|
+
// throw qIndex;
|
|
93
|
+
if (!qIndex.length) return null;
|
|
94
|
+
if (config.local) {
|
|
95
|
+
// console.log(qIndex.filter((v, i, a) => a.indexOf(v) === i));
|
|
96
|
+
}
|
|
97
|
+
// logger.file('index', { table, filter, sql: qIndex.filter((v, i, a) => a.indexOf(v) === i) });
|
|
98
|
+
qIndex.filter((v, i, a) => a.indexOf(v) === i).map((el) => pg.one(el).catch((err) => console.log(err.toString())));
|
|
99
|
+
return 'ok';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default autoIndex;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function getDBParams(str) {
|
|
2
|
+
if (typeof str !== 'string') return str;
|
|
3
|
+
if (str.includes('/')) {
|
|
4
|
+
const arr = str.match(/(([A-z_0-9]+):([A-z0-9_]+)@)?([a-z0-9._]+):([0-9]+)(\/([a-z0-9_]+))?/) || [];
|
|
5
|
+
const [, , user, password, host, port, , db] = arr;
|
|
6
|
+
return {
|
|
7
|
+
name: str, user, password, host, port, db,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
if (str.includes(':')) {
|
|
11
|
+
const [db, port] = str.split(':') || [];
|
|
12
|
+
return { name: str, port, db };
|
|
13
|
+
}
|
|
14
|
+
return { name: str, db: str };
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import getPG from './getPG.js';
|
|
2
|
+
|
|
3
|
+
const data = {};
|
|
4
|
+
|
|
5
|
+
// decorator
|
|
6
|
+
export default async function getMeta(opt, nocache) {
|
|
7
|
+
const pg = opt?.pg || getPG({ name: 'client' });
|
|
8
|
+
if (!pg) return { error: 'pg connection not established', status: 400 };
|
|
9
|
+
const table = opt?.table || opt;
|
|
10
|
+
|
|
11
|
+
if (pg?.options?.database && data[pg.options.database]?.[table] && !nocache) return data[pg.options.database][table];
|
|
12
|
+
|
|
13
|
+
if (!pg?.tlist?.includes(table?.replace?.(/"/g, ''))) {
|
|
14
|
+
return { error: `${table} - not found`, status: 400 };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { fields = [] } = await pg.query(`select * from ${table} limit 0`);
|
|
18
|
+
const pks1 = await pg.query(`SELECT json_object_agg(c.conrelid::regclass, a.attname) as pks1
|
|
19
|
+
FROM pg_constraint c
|
|
20
|
+
left join pg_attribute a on c.conrelid=a.attrelid and a.attnum = c.conkey[1]
|
|
21
|
+
WHERE c.contype='p'::"char" and c.conrelid::regclass = $1::regclass`, [table]).then(el => el.rows[0].pks1 || {});
|
|
22
|
+
|
|
23
|
+
const pk = table.startsWith('public.')
|
|
24
|
+
? (pks1[table.replace('public.', '')] || pks1[table.replace('public.', '').replace(/"/g, '')])
|
|
25
|
+
: (pks1[table] || pks1[table.replace(/"/g, '')]);
|
|
26
|
+
|
|
27
|
+
const geomAttr = fields.find((el) => pg.pgType?.[el.dataTypeID] === 'geometry')?.name; // change geometry text to geometry code
|
|
28
|
+
|
|
29
|
+
const dbColumns = await pg.query(`select json_object_agg(
|
|
30
|
+
attname,
|
|
31
|
+
json_build_object(
|
|
32
|
+
'title', pg_catalog.col_description(attrelid,attnum)
|
|
33
|
+
/*,'type', atttypid::regtype */
|
|
34
|
+
)
|
|
35
|
+
) from pg_catalog.pg_attribute a
|
|
36
|
+
where attrelid=$1::regclass and attnum>0`, [table]).then(el => el.rows?.[0]?.json_object_agg || {});
|
|
37
|
+
|
|
38
|
+
fields.forEach(el => Object.assign(el, { ...dbColumns[el.name] || {} }));
|
|
39
|
+
|
|
40
|
+
const res = {
|
|
41
|
+
pk, columns: fields, geom: geomAttr, view: pg.relkinds?.[table] === 'v',
|
|
42
|
+
};
|
|
43
|
+
if (!data[pg.options.database]) {
|
|
44
|
+
data[pg.options.database] = {};
|
|
45
|
+
}
|
|
46
|
+
data[pg.options.database][table] = res;
|
|
47
|
+
return res;
|
|
48
|
+
}
|