@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.
Files changed (61) hide show
  1. package/config.js +7 -0
  2. package/module/pay/pt/marketplace-service-payment-client-template.html +7 -20
  3. package/package.json +15 -31
  4. package/plugin.js +13 -10
  5. package/server/plugins/crud/dataDelete.js +82 -0
  6. package/server/plugins/crud/dataInsert.js +76 -0
  7. package/server/plugins/crud/dataUpdate.js +112 -0
  8. package/server/plugins/{cron.js → hook.js} +24 -22
  9. package/server/plugins/pg/funcs/autoIndex.js +102 -0
  10. package/server/plugins/pg/funcs/getDBParams.js +15 -0
  11. package/server/plugins/pg/funcs/getMeta.js +48 -0
  12. package/server/plugins/pg/funcs/getPG.js +39 -0
  13. package/server/plugins/pg/funcs/getPGAsync.js +40 -0
  14. package/server/plugins/pg/funcs/init.js +113 -0
  15. package/server/plugins/pg/index.js +24 -0
  16. package/server/plugins/pg/pgClients.js +22 -0
  17. package/server/plugins/redis/client.js +8 -0
  18. package/server/plugins/redis/funcs/getRedis.js +24 -0
  19. package/server/plugins/redis/funcs/redisClients.js +3 -0
  20. package/server/plugins/redis/index.js +11 -0
  21. package/server/routes/liqpay/controllers/liqpay.redirect.js +7 -11
  22. package/server/routes/liqpay/controllers/liqpay.status.js +26 -15
  23. package/server/routes/liqpay/utils/check.status.js +1 -1
  24. package/server/routes/monopay/controllers/monopay.redirect.js +141 -0
  25. package/server/routes/monopay/controllers/monopay.status.js +70 -0
  26. package/server/routes/monopay/index.mjs +32 -0
  27. package/server/routes/portmone/controllers/payment.info.js +9 -9
  28. package/server/routes/portmone/controllers/portmone.redirect.js +23 -17
  29. package/server/routes/portmone/controllers/portmone.status.js +49 -38
  30. package/server/routes/portmone/utils/portmone.check.status.js +34 -33
  31. package/server/utils/cron/addCron.js +48 -0
  32. package/server/utils/cron/cronList.js +1 -0
  33. package/server/utils/cron/interval2ms.js +40 -0
  34. package/server/utils/cron/runCron.js +24 -0
  35. package/server/utils/cron/verifyUnique.js +23 -0
  36. package/server/utils/getFolder.js +11 -0
  37. package/server/utils/getInsertQuery.js +44 -0
  38. package/server/utils/migration/exec.migrations.js +38 -0
  39. package/server/utils/migration/exec.sql.js +48 -0
  40. package/server/utils/sendNotification.js +69 -0
  41. package/module/pay1/card/billing.accounts.table/general_info.hbs +0 -11
  42. package/module/pay1/card/billing.accounts.table/index.yml +0 -15
  43. package/module/pay1/card/billing.accounts.table/users.hbs +0 -12
  44. package/module/pay1/card/billing.payments.table/general_info.hbs +0 -13
  45. package/module/pay1/card/billing.payments.table/index.yml +0 -10
  46. package/module/pay1/card/billing.users.table/general_info.hbs +0 -11
  47. package/module/pay1/card/billing.users.table/index.yml +0 -10
  48. package/module/pay1/cls/billing.payment_status.json +0 -22
  49. package/module/pay1/form/billing.account_user.form.json +0 -15
  50. package/module/pay1/form/billing.accounts.form.json +0 -31
  51. package/module/pay1/form/billing.payment.form.json +0 -23
  52. package/module/pay1/form/billing.users.form.json +0 -39
  53. package/module/pay1/menu.json +0 -24
  54. package/module/pay1/pt/marketplace-service-payment-client-template.html +0 -147
  55. package/module/pay1/select/billing.account_id.sql +0 -1
  56. package/module/pay1/select/billing.service_id.sql +0 -1
  57. package/module/pay1/select/billing.subscription_id.sql +0 -1
  58. package/module/pay1/select/billing.user_id.sql +0 -1
  59. package/module/pay1/table/billing.accounts.table.json +0 -61
  60. package/module/pay1/table/billing.payments.table.json +0 -68
  61. package/module/pay1/table/billing.users.table.json +0 -71
package/config.js ADDED
@@ -0,0 +1,7 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+
3
+ const config = existsSync('config.json') ? JSON.parse(readFileSync('config.json')) : {};
4
+ // console.log(config);
5
+ config.storageList = {};
6
+
7
+ export default config;
@@ -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">{{select account_id data="billing.account_id"}}, дякуємо за здійснення замовлення на порталі <a href="//{{domain}}">{{domain}}</a></span> </span>
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">&nbsp;</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>Дата замовлення:</strong> {{formatDate cdate format='dd.mm.yy hh:mi'}}</span>
34
+ <span style="color:#61677b"><strong>Дата створення платежу:</strong> {{created_at}}</span>
38
35
  <br>
39
- <span style="color:#61677b"><strong>Статус замовлення:</strong> {{select payment_status data="billing.payment_status"}}</span>
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> {{num_format payment_amount dec="2"}}</span>
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> {{num_format total_balance dec="2"}}</span>
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">&nbsp;</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">&nbsp;</td>
94
81
  <td align="right">
95
- <font size="2" face="Open-sans, sans-serif" color="#555454"> {{num_format (coalesce withdrawal_sum payment_amount)}} грн.</font>
82
+ <font size="2" face="Open-sans, sans-serif" color="#555454"> {{payment_amount}} грн.</font>
96
83
  </td>
97
84
  <td width="10">&nbsp;</td>
98
85
  </tr>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/pay",
3
- "version": "1.2.3",
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
- "docs:dev": "vitepress dev docs",
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": "cross-env NODE_ENV=admin npm run start",
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
- "@opengis/admin": "^0.2.14",
24
- "@opengis/fastify-table": "^1.2.64",
25
- "cross-env": "^7.0.3",
26
- "fastify": "^4.26.1",
27
- "fastify-plugin": "^4.0.0",
28
- "liqpayjs-sdk": "^3.3.1",
29
- "request-promise": "^4.2.6"
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
- "@opengis/fastify-auth": "^1.0.57",
33
- "@panzoom/panzoom": "^4.5.1",
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 { config, execMigrations } from '@opengis/fastify-table/utils.js';
5
+ import getPGAsync from './server/plugins/pg/funcs/getPGAsync.js';
6
+ import execMigrations from './server/utils/migration/exec.migrations.js';
6
7
 
7
- const { prefix = '/api' } = config;
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
- app.register(import('./server/plugins/cron.js'));
20
- app.register(import('./server/routes/portmone/index.mjs'), { prefix });
21
- app.register(import('./server/routes/liqpay/index.mjs'), { prefix });
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 { addCron, pgClients } from '@opengis/fastify-table/utils.js';
2
-
3
- import updateTrxStatus from '../routes/portmone/utils/portmone.check.status.js';
4
-
5
- async function updateOldTrxsStatus() {
6
- const inprogress = pgClients.client.pk?.['billing.payments']
7
- ? await pgClients.client.query(`select array_agg(payment_id) as inprogress from billing.payments
8
- where payment_status = 'inprogress' and (now() - cdate) > '1 hour'::interval`).then(el => el.rows?.[0]?.inprogress || [])
9
- : [];
10
-
11
- if (!inprogress?.length) return 'empty rows';
12
-
13
- const res = await Promise.all(inprogress.map(async (paymentId) => {
14
- const data = await updateTrxStatus({ paymentId });
15
- return { paymentId, data };
16
- }));
17
- return res;
18
- }
19
-
20
- export default async function cron() {
21
- // addCron(updateOldTrxsStatus, 60 * 60 * 24);
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
+ }