@opengis/fastify-table 2.0.5 → 2.0.7
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/dist/server/migrations/users.sql +2 -0
- package/dist/server/plugins/auth/funcs/authorizeUser.js +16 -8
- package/dist/server/plugins/auth/funcs/loginFile.js +24 -19
- package/dist/server/plugins/auth/funcs/loginUser.js +4 -3
- package/dist/server/plugins/auth/index.js +6 -1
- package/dist/server/plugins/crud/funcs/getAccess.js +28 -27
- package/dist/server/plugins/file/providers/fs.js +2 -2
- package/dist/server/plugins/file/providers/utils/typeguards/isPath.js +1 -1
- package/dist/server/plugins/file/utils/isFileExists.js +2 -2
- package/dist/server/plugins/hook/index.js +38 -6
- package/dist/server/plugins/logger/errorMessage.js +1 -1
- package/dist/server/plugins/logger/errorStatus.js +5 -5
- package/dist/server/plugins/table/funcs/getFilterSQL/util/formatValue.js +1 -1
- package/dist/server/routes/auth/controllers/core/getUserInfo.js +1 -1
- package/dist/server/routes/auth/controllers/euSign/authByData.js +57 -43
- package/dist/server/routes/auth/controllers/jwt/authorize.js +43 -28
- package/dist/server/routes/auth/controllers/jwt/token.js +54 -29
- package/dist/server/routes/menu/controllers/getMenu.js +1 -1
- package/dist/server/routes/table/controllers/filter.js +1 -1
- package/dist/server/routes/table/functions/getData.js +1 -1
- package/dist/server/routes/widget/controllers/widget.set.js +1 -1
- package/dist/server/routes/widget/index.js +1 -1
- package/dist/utils.js +1 -3
- package/package.json +1 -1
- package/dist/server/plugins/hook/funcs/addHook.js +0 -7
- package/dist/server/plugins/hook/funcs/applyHook.js +0 -25
- package/dist/server/plugins/hook/funcs/applyHookSync.js +0 -7
- package/dist/server/plugins/hook/hookList.js +0 -2
|
@@ -27,6 +27,7 @@ ALTER TABLE admin.users add column if not exists salt text;
|
|
|
27
27
|
ALTER TABLE admin.users add column if not exists cdate timestamp without time zone DEFAULT date_trunc('seconds'::text, now());
|
|
28
28
|
ALTER TABLE admin.users add column if not exists editor_id text;
|
|
29
29
|
ALTER TABLE admin.users add column if not exists editor_date timestamp without time zone;
|
|
30
|
+
ALTER TABLE admin.users add column if not exists twofa boolean not null DEFAULT true;
|
|
30
31
|
|
|
31
32
|
ALTER TABLE admin.users add CONSTRAINT admin_user_uid_pkey PRIMARY KEY (uid);
|
|
32
33
|
ALTER TABLE admin.users add CONSTRAINT admin_user_user_rnokpp UNIQUE (user_rnokpp);
|
|
@@ -46,6 +47,7 @@ COMMENT ON COLUMN admin.users.enabled IS 'On / Off';
|
|
|
46
47
|
COMMENT ON COLUMN admin.users.last_activity_date IS 'Дата останньої активності';
|
|
47
48
|
COMMENT ON COLUMN admin.users.user_type IS 'Тип користувача';
|
|
48
49
|
COMMENT ON COLUMN admin.users.salt IS 'Сіль';
|
|
50
|
+
COMMENT ON COLUMN admin.users.twofa IS 'Двофакторна авторизація';
|
|
49
51
|
|
|
50
52
|
CREATE EXTENSION if not exists pgcrypto SCHEMA public VERSION "1.3";
|
|
51
53
|
CREATE OR REPLACE FUNCTION admin.crypt(text, text) RETURNS text AS '$libdir/pgcrypto', 'pg_crypt' LANGUAGE c IMMUTABLE STRICT COST 1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import config from "../../../../config.js";
|
|
2
2
|
import logger from "../../logger/getLogger.js";
|
|
3
|
-
import applyHook from "../../hook/
|
|
3
|
+
import { applyHook } from "../../hook/index.js";
|
|
4
4
|
import logAuth from "./logAuth.js";
|
|
5
5
|
/*
|
|
6
6
|
session duration by default
|
|
@@ -15,9 +15,10 @@ const getIp = (req) => (req.headers?.["x-real-ip"] ||
|
|
|
15
15
|
"")
|
|
16
16
|
.split(":")
|
|
17
17
|
.pop();
|
|
18
|
-
export default async function authorizeUser(user, req,
|
|
18
|
+
export default async function authorizeUser(user, req, authType = "creds-user", expire) {
|
|
19
19
|
if (!user?.uid)
|
|
20
20
|
return "/logout";
|
|
21
|
+
Object.assign(user, { auth_type: authType });
|
|
21
22
|
// fastify/passport
|
|
22
23
|
await req.login(user);
|
|
23
24
|
const st = (req.body?.keep || req.query?.keep) === "on"
|
|
@@ -29,21 +30,26 @@ export default async function authorizeUser(user, req, loginType = "login", expi
|
|
|
29
30
|
}
|
|
30
31
|
logger.file("auth", {
|
|
31
32
|
level: "DEBUG",
|
|
32
|
-
name:
|
|
33
|
-
response: {
|
|
33
|
+
name: authType,
|
|
34
|
+
response: {
|
|
35
|
+
uid: user?.uid,
|
|
36
|
+
name: [user.sur_name, user.user_name, user.name]
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.join(" "),
|
|
39
|
+
},
|
|
34
40
|
}, req);
|
|
35
41
|
const ip = getIp(req);
|
|
36
|
-
if (
|
|
42
|
+
if (authType && authType.startsWith("creds")) {
|
|
37
43
|
await logAuth({
|
|
38
44
|
uid: user?.uid,
|
|
39
|
-
type:
|
|
45
|
+
type: authType,
|
|
40
46
|
ip,
|
|
41
47
|
});
|
|
42
48
|
}
|
|
43
49
|
const { href } = (await applyHook("afterAuth", {
|
|
44
50
|
ip,
|
|
45
51
|
user,
|
|
46
|
-
name:
|
|
52
|
+
name: authType,
|
|
47
53
|
referer: req.headers?.referer,
|
|
48
54
|
})) ||
|
|
49
55
|
{};
|
|
@@ -52,7 +58,9 @@ export default async function authorizeUser(user, req, loginType = "login", expi
|
|
|
52
58
|
return { message: "ok", status: "200" };
|
|
53
59
|
}
|
|
54
60
|
const redirectUrl = req.headers?.referer?.match?.(/[?&]redirect=([^&]+)/)?.[1] || "/";
|
|
55
|
-
|
|
61
|
+
// by default, disable 2factor for id.gov.ua auth
|
|
62
|
+
const check = authType === "govid" ? config.auth?.["2factor"]?.govid : true;
|
|
63
|
+
if (config.auth?.["2factor"] && user?.twofa && check) {
|
|
56
64
|
return ("/2factor?redirect=" +
|
|
57
65
|
(href ||
|
|
58
66
|
config.auth?.redirectAfter ||
|
|
@@ -1,41 +1,46 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { existsSync, readFileSync } from
|
|
3
|
-
import { createHash } from
|
|
4
|
-
import config from
|
|
5
|
-
import users from
|
|
6
|
-
import authorizeUser from
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import config from "../../../../config.js";
|
|
5
|
+
import users from "./users.js";
|
|
6
|
+
import authorizeUser from "./authorizeUser.js";
|
|
7
7
|
export default async function loginFile(req, reply) {
|
|
8
|
-
const { username, password } = req.method ===
|
|
9
|
-
const filepath = path.join(process.cwd(),
|
|
8
|
+
const { username, password } = req.method === "POST" ? req.body : req.query;
|
|
9
|
+
const filepath = path.join(process.cwd(), "passwd");
|
|
10
10
|
if (!users?.length) {
|
|
11
11
|
const fileExists = existsSync(filepath);
|
|
12
12
|
if (!fileExists) {
|
|
13
|
-
req.log.error(req,
|
|
14
|
-
return { error:
|
|
13
|
+
req.log.error(req, "passwd file not exists");
|
|
14
|
+
return { error: "login error", status: 500 };
|
|
15
15
|
}
|
|
16
16
|
// parse file on start up
|
|
17
|
-
const data = readFileSync(filepath,
|
|
18
|
-
const separator = data.indexOf(
|
|
17
|
+
const data = readFileSync(filepath, "utf8");
|
|
18
|
+
const separator = data.indexOf("\\r\\n") !== -1 ? "\r\n" : "\n";
|
|
19
19
|
const rows = data.split(separator).map((row) => {
|
|
20
|
-
const [name, passwd, usertype =
|
|
20
|
+
const [name, passwd, usertype = "regular", uid] = row.split(":");
|
|
21
21
|
return { username: name, password: passwd, usertype, uid };
|
|
22
22
|
});
|
|
23
23
|
rows.forEach((row) => users.push(row));
|
|
24
24
|
}
|
|
25
25
|
// check user / password
|
|
26
26
|
const user = users.find((el) => el.username === username);
|
|
27
|
-
const hashPasswd = createHash(
|
|
27
|
+
const hashPasswd = createHash("sha1")
|
|
28
|
+
.update(`${password}${user?.salt || ""}`)
|
|
29
|
+
.digest("hex");
|
|
28
30
|
if (!user?.password || user.password !== hashPasswd) {
|
|
29
|
-
const txt =
|
|
30
|
-
return req.method ===
|
|
31
|
+
const txt = "Invalid user or password";
|
|
32
|
+
return req.method === "GET"
|
|
31
33
|
? reply.status(302).redirect(`/login?confirm=wrong_pass&message=${txt}`)
|
|
32
34
|
: reply.status(400).send({ message: txt });
|
|
33
35
|
}
|
|
34
36
|
const resultUser = {
|
|
35
37
|
uid: user?.uid || config?.auth?.uid || username,
|
|
36
38
|
user_name: username,
|
|
37
|
-
user_type: user.usertype ||
|
|
39
|
+
user_type: user.usertype || "regular",
|
|
38
40
|
};
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
+
const authType = "creds-" + (user.usertype === "admin" ? "admin" : "user");
|
|
42
|
+
const href = await authorizeUser(resultUser, req, authType); // creds-admin / creds-admin
|
|
43
|
+
return req.method === "GET"
|
|
44
|
+
? reply.status(302).redirect(href)
|
|
45
|
+
: reply.status(200).send(href);
|
|
41
46
|
}
|
|
@@ -18,11 +18,11 @@ export default async function loginUser(req, reply) {
|
|
|
18
18
|
.redirect("/login?confirm=wrong_pass&message=empty redis")
|
|
19
19
|
: reply.status(400).send({ message: "empty redis" });
|
|
20
20
|
}
|
|
21
|
-
const { user, message = "invalid user" } = await verifyPassword({
|
|
21
|
+
const { user, message = "invalid user" } = (await verifyPassword({
|
|
22
22
|
pg,
|
|
23
23
|
username,
|
|
24
24
|
password,
|
|
25
|
-
}) || {};
|
|
25
|
+
})) || {};
|
|
26
26
|
if (!user) {
|
|
27
27
|
return req.method === "GET"
|
|
28
28
|
? reply
|
|
@@ -38,7 +38,8 @@ export default async function loginUser(req, reply) {
|
|
|
38
38
|
: reply.status(400).send({ message });
|
|
39
39
|
}
|
|
40
40
|
logger.metrics("user.login");
|
|
41
|
-
const
|
|
41
|
+
const authType = "creds-" + (user.user_type === "admin" ? "admin" : "user");
|
|
42
|
+
const href = await authorizeUser(user, req, authType); // creds-admin / creds-admin
|
|
42
43
|
return req.method === "GET"
|
|
43
44
|
? reply.status(302).redirect(href)
|
|
44
45
|
: reply.status(200).send(href);
|
|
@@ -83,13 +83,18 @@ async function plugin(fastify) {
|
|
|
83
83
|
!req.url.startsWith("/login")) {
|
|
84
84
|
return reply.redirect(`${config?.auth?.redirect || "/login"}` + `?redirect=${req.url}`);
|
|
85
85
|
}
|
|
86
|
+
// by default, disable 2factor for id.gov.ua auth
|
|
87
|
+
const check = passport.user?.auth_type === "govid"
|
|
88
|
+
? config.auth?.["2factor"]?.govid
|
|
89
|
+
: true;
|
|
86
90
|
if (passport.user?.uid &&
|
|
87
91
|
passport.user?.twofa &&
|
|
88
92
|
config.auth?.["2factor"] &&
|
|
89
93
|
!isPublic &&
|
|
90
94
|
(routeOptions?.method || "GET") === "GET" &&
|
|
91
95
|
!secondFactorPassed &&
|
|
92
|
-
!ispasswd
|
|
96
|
+
!ispasswd &&
|
|
97
|
+
check) {
|
|
93
98
|
const href = config.auth?.["2factorRedirect"] || "/2factor";
|
|
94
99
|
if (!href.includes(req.url)) {
|
|
95
100
|
return reply.redirect(href);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import pgClients from
|
|
2
|
-
import getTemplate from
|
|
3
|
-
import applyHook from
|
|
4
|
-
const allActions = [
|
|
1
|
+
import pgClients from "../../pg/pgClients.js";
|
|
2
|
+
import getTemplate from "../../table/funcs/getTemplate.js";
|
|
3
|
+
import { applyHook } from "../../hook/index.js";
|
|
4
|
+
const allActions = ["view", "edit", "add", "del"];
|
|
5
5
|
const q = `select a.route_id as id, d.actions as user_roles, d.actions as role_actions, coalesce(b.actions, array['view']) as interface_actions, b.scope, c.role_id
|
|
6
6
|
from admin.routes a
|
|
7
7
|
left join admin.role_access b on
|
|
@@ -30,47 +30,48 @@ where $1 in (a.route_id, a.alias, a.table_name) and $2 in (b.user_uid, d.user_ui
|
|
|
30
30
|
export default async function getAccess({ table, form, user = {} }, pg = pgClients.client) {
|
|
31
31
|
if (!table)
|
|
32
32
|
return null;
|
|
33
|
-
const hookData = await applyHook(
|
|
33
|
+
const hookData = await applyHook("getAccess", { table, user, pg });
|
|
34
34
|
if (hookData)
|
|
35
35
|
return hookData;
|
|
36
|
-
const { uid, user_type: userType =
|
|
37
|
-
if (userType ===
|
|
38
|
-
return { actions: allActions, query:
|
|
36
|
+
const { uid, user_type: userType = "regular" } = user;
|
|
37
|
+
if (userType === "superadmin") {
|
|
38
|
+
return { actions: allActions, query: "1=1" };
|
|
39
39
|
}
|
|
40
|
-
const body = await getTemplate(
|
|
40
|
+
const body = await getTemplate("table", table);
|
|
41
41
|
const tableActions = !body && form
|
|
42
42
|
? allActions // if db table and form => full access (token)
|
|
43
|
-
: [
|
|
44
|
-
if (userType ===
|
|
43
|
+
: ["view"].concat(body?.actions || body?.action_default || []);
|
|
44
|
+
if (userType === "admin") {
|
|
45
45
|
if (!(body?.actions || body?.action_default) && (body?.form || form)) {
|
|
46
|
-
return { actions: allActions, query:
|
|
46
|
+
return { actions: allActions, query: "1=1" };
|
|
47
47
|
}
|
|
48
|
-
return { actions: tableActions, query:
|
|
48
|
+
return { actions: tableActions, query: "1=1" };
|
|
49
49
|
}
|
|
50
|
-
if (body?.public || body?.access ===
|
|
51
|
-
return { actions: tableActions, query:
|
|
50
|
+
if (body?.public || body?.access === "public") {
|
|
51
|
+
return { actions: tableActions, query: "1=1" };
|
|
52
52
|
}
|
|
53
|
-
if (body?.access ===
|
|
54
|
-
return { actions: tableActions, query:
|
|
53
|
+
if (body?.access === "user" && uid) {
|
|
54
|
+
return { actions: tableActions, query: "1=1" };
|
|
55
55
|
}
|
|
56
56
|
if (!uid) {
|
|
57
|
-
return { actions: [], query:
|
|
57
|
+
return { actions: [], query: "1=1" };
|
|
58
58
|
}
|
|
59
|
-
const userAccess = pg?.pk?.[
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
? await pg.query(q, [table, uid])
|
|
64
|
-
.then((el) => ({
|
|
59
|
+
const userAccess = pg?.pk?.["admin.routes"] &&
|
|
60
|
+
pg.pk?.["admin.role_access"] &&
|
|
61
|
+
pg.pk?.["admin.roles"] &&
|
|
62
|
+
pg.pk?.["admin.user_roles"]
|
|
63
|
+
? await pg.query(q, [table, uid]).then((el) => ({
|
|
65
64
|
...(el.rows[0] || {}),
|
|
66
65
|
roles: el.rows?.map?.((row) => row.role_id) || [],
|
|
67
66
|
actions: el.rows?.map?.((row) => row.actions).flat() || [],
|
|
68
|
-
interface_actions: el.rows?.map?.((row) => row.interface_actions).flat() || []
|
|
67
|
+
interface_actions: el.rows?.map?.((row) => row.interface_actions).flat() || [],
|
|
69
68
|
}))
|
|
70
69
|
: {};
|
|
71
|
-
const query = userAccess?.scope ===
|
|
70
|
+
const query = userAccess?.scope === "my" ? `uid='${uid}'` : "1=1";
|
|
72
71
|
const actions = userAccess?.interface_actions
|
|
73
|
-
?.filter((el) => userAccess?.role_actions?.length
|
|
72
|
+
?.filter((el) => userAccess?.role_actions?.length
|
|
73
|
+
? userAccess?.role_actions.includes(el)
|
|
74
|
+
: true)
|
|
74
75
|
?.filter((el) => tableActions.includes(el))
|
|
75
76
|
?.filter?.((el, idx, arr) => arr.indexOf(el) === idx);
|
|
76
77
|
return {
|
|
@@ -29,7 +29,7 @@ const downloadFile = () => async (fp, options = {}) => {
|
|
|
29
29
|
if (options.debug) {
|
|
30
30
|
return { original: fp, full: filepath };
|
|
31
31
|
}
|
|
32
|
-
if (!filepath || !(await isFileExists(filepath
|
|
32
|
+
if (!filepath || !(await isFileExists(filepath))) {
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
if (options.buffer) {
|
|
@@ -38,7 +38,7 @@ const downloadFile = () => async (fp, options = {}) => {
|
|
|
38
38
|
const fileStream = fs.createReadStream(filepath);
|
|
39
39
|
return fileStream;
|
|
40
40
|
};
|
|
41
|
-
const fileExists = () => async (filepath, opt = {}) => isFileExists(getPath(filepath, opt)
|
|
41
|
+
const fileExists = () => async (filepath, opt = {}) => isFileExists(getPath(filepath, opt));
|
|
42
42
|
const uploadFile = () => async (fp, data, opt = {}) => {
|
|
43
43
|
const filepath = getPath(fp, opt);
|
|
44
44
|
const validData = await getValidData({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access } from "node:fs/promises";
|
|
2
2
|
import getPath from "./getPath.js";
|
|
3
|
-
const isFileExists = async (filepath
|
|
4
|
-
const fullPath = getPath(filepath,
|
|
3
|
+
const isFileExists = async (filepath) => {
|
|
4
|
+
const fullPath = getPath(filepath, { check: true });
|
|
5
5
|
if (!fullPath)
|
|
6
6
|
return false;
|
|
7
7
|
try {
|
|
@@ -1,7 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
async function
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import config from "../../../config.js";
|
|
2
|
+
export const hookList = {};
|
|
3
|
+
export async function applyHook(name, data) {
|
|
4
|
+
if (config.trace)
|
|
5
|
+
console.log("applyHook", name);
|
|
6
|
+
if (!hookList[name]?.length)
|
|
7
|
+
return null;
|
|
8
|
+
const result = {};
|
|
9
|
+
await Promise.all(hookList[name].map(async (hook) => {
|
|
10
|
+
const hookData = await hook({ ...data, config });
|
|
11
|
+
if (hookData) {
|
|
12
|
+
if (config.trace)
|
|
13
|
+
console.log("applyHook", name, hookData);
|
|
14
|
+
Object.assign(result, hookData);
|
|
15
|
+
}
|
|
16
|
+
})).catch((err) => {
|
|
17
|
+
console.error("applyHook", name, err.toString());
|
|
18
|
+
});
|
|
19
|
+
if (Object.keys(result).length) {
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
export function addHook(name, fn) {
|
|
25
|
+
if (!hookList[name]) {
|
|
26
|
+
hookList[name] = [];
|
|
27
|
+
}
|
|
28
|
+
if (config.trace)
|
|
29
|
+
console.log("addHook", name);
|
|
30
|
+
hookList[name].push(fn);
|
|
31
|
+
}
|
|
32
|
+
export function applyHookSync(name, data) {
|
|
33
|
+
if (!hookList[name]?.length)
|
|
34
|
+
return null;
|
|
35
|
+
if (config.trace)
|
|
36
|
+
console.log("applyHookSync", name);
|
|
37
|
+
const hookData = hookList[name].map((hook) => hook(data))[0];
|
|
38
|
+
return hookData;
|
|
6
39
|
}
|
|
7
|
-
export default plugin;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import applyHookSync from
|
|
1
|
+
import { applyHookSync } from "../hook/index.js";
|
|
2
2
|
function errorStatus(error) {
|
|
3
|
-
const hook = applyHookSync(
|
|
3
|
+
const hook = applyHookSync("errorStatus", error);
|
|
4
4
|
if (hook)
|
|
5
5
|
return hook;
|
|
6
|
-
if (error.routine ===
|
|
6
|
+
if (error.routine === "exec_stmt_raise" && error.file === "pl_exec.c") {
|
|
7
7
|
return 601;
|
|
8
8
|
}
|
|
9
|
-
if (error.routine ===
|
|
9
|
+
if (error.routine === "ExecConstraints") {
|
|
10
10
|
return 602;
|
|
11
11
|
}
|
|
12
|
-
if (error.type ===
|
|
12
|
+
if (error.type === "DatabaseError") {
|
|
13
13
|
return 600;
|
|
14
14
|
}
|
|
15
15
|
return 500;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import applyHookSync from "../../../../hook/
|
|
1
|
+
import { applyHookSync } from "../../../../hook/index.js";
|
|
2
2
|
import getRangeQuery from "./getRangeQuery.js";
|
|
3
3
|
export default function formatValue({ pg, table, filter = {}, name, value, dataTypeID, uid = 1, optimize, }) {
|
|
4
4
|
const { extra, sql, select, strict, options /* default: defaultValue, */ } = filter;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import config from "../../../../../config.js";
|
|
2
|
-
import applyHook from "
|
|
2
|
+
import { applyHook } from "../../../../../utils.js";
|
|
3
3
|
import getRedis from "../../../../plugins/redis/funcs/getRedis.js";
|
|
4
4
|
const rclient2 = getRedis({ db: 2 });
|
|
5
5
|
export default async function getUserInfo(req) {
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { Agent } from
|
|
2
|
-
import config from
|
|
3
|
-
import applyHook from
|
|
4
|
-
import logger from
|
|
5
|
-
import pgClients from
|
|
6
|
-
import checkReferer from
|
|
7
|
-
import getQuery from
|
|
8
|
-
import logAuth from
|
|
9
|
-
import authorizeUser from
|
|
1
|
+
import { Agent } from "undici";
|
|
2
|
+
import config from "../../../../../config.js";
|
|
3
|
+
import { applyHook } from "../../../../../utils.js";
|
|
4
|
+
import logger from "../../../../plugins/logger/getLogger.js";
|
|
5
|
+
import pgClients from "../../../../plugins/pg/pgClients.js";
|
|
6
|
+
import checkReferer from "../../../../plugins/auth/funcs/checkReferer.js";
|
|
7
|
+
import getQuery from "../../../../plugins/auth/funcs/getQuery.js";
|
|
8
|
+
import logAuth from "../../../../plugins/auth/funcs/logAuth.js";
|
|
9
|
+
import authorizeUser from "../../../../plugins/auth/funcs/authorizeUser.js";
|
|
10
10
|
function fetchWithoutSSL(url, options) {
|
|
11
11
|
const httpsAgent = new Agent({
|
|
12
12
|
connect: {
|
|
13
|
-
rejectUnauthorized: false
|
|
14
|
-
}
|
|
13
|
+
rejectUnauthorized: false,
|
|
14
|
+
},
|
|
15
15
|
});
|
|
16
16
|
return fetch(url, {
|
|
17
|
-
...options || {},
|
|
18
|
-
dispatcher: httpsAgent
|
|
17
|
+
...(options || {}),
|
|
18
|
+
dispatcher: httpsAgent,
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
const getIp = (req) => (req.headers?.[
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.split(
|
|
21
|
+
const getIp = (req) => (req.headers?.["x-real-ip"] ||
|
|
22
|
+
req.headers?.["x-forwarded-for"] ||
|
|
23
|
+
req.ip ||
|
|
24
|
+
req.connection?.remoteAddress ||
|
|
25
|
+
"")
|
|
26
|
+
.split(":")
|
|
27
27
|
.pop();
|
|
28
28
|
export default async function authByData(req, reply) {
|
|
29
29
|
const { pg = pgClients.client, query = {}, headers = {} } = req;
|
|
@@ -33,22 +33,22 @@ export default async function authByData(req, reply) {
|
|
|
33
33
|
const ip = getIp(req);
|
|
34
34
|
const { data: code, type } = query; // token
|
|
35
35
|
const { security } = config;
|
|
36
|
-
const authType = type
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const authType = type ||
|
|
37
|
+
(referer?.includes("softpro.ua") ? "google" : null) ||
|
|
38
|
+
(referer?.includes("nsdi.gov.ua") ? "govid" : null) ||
|
|
39
|
+
(referer?.includes("admin.nsdi.gki.com.ua") ? "govid" : null); // fix admin auth
|
|
40
40
|
const hostOauth = {
|
|
41
|
-
google: security?.social_auth_host_local ||
|
|
42
|
-
govid: security?.id_gov_ua_host_local ||
|
|
43
|
-
}[authType ||
|
|
41
|
+
google: security?.social_auth_host_local || "https://id.softpro.ua",
|
|
42
|
+
govid: security?.id_gov_ua_host_local || "https://nsdi.gov.ua",
|
|
43
|
+
}[authType || ""];
|
|
44
44
|
if (!hostOauth) {
|
|
45
|
-
logger.file(
|
|
46
|
-
error:
|
|
45
|
+
logger.file("auth/by_data/warn", {
|
|
46
|
+
error: "invalid oauth params",
|
|
47
47
|
referer,
|
|
48
48
|
authType,
|
|
49
49
|
hostOauth,
|
|
50
50
|
});
|
|
51
|
-
return reply.status(400).send(
|
|
51
|
+
return reply.status(400).send("Невалідний парметр тип авторизації");
|
|
52
52
|
}
|
|
53
53
|
// referer + token check
|
|
54
54
|
const invalidReferer = await checkReferer({
|
|
@@ -57,39 +57,48 @@ export default async function authByData(req, reply) {
|
|
|
57
57
|
hostOauth,
|
|
58
58
|
});
|
|
59
59
|
if (!code || invalidReferer) {
|
|
60
|
-
logger.file(
|
|
61
|
-
error:
|
|
60
|
+
logger.file("auth/by_data/warn", {
|
|
61
|
+
error: "invalid request",
|
|
62
|
+
referer,
|
|
63
|
+
code,
|
|
62
64
|
});
|
|
63
|
-
return reply
|
|
65
|
+
return reply
|
|
66
|
+
.status(403)
|
|
67
|
+
.send("Параметри data / code / state мають невірний формат, або Ви перейшли за прямим посиланням.");
|
|
64
68
|
}
|
|
65
|
-
const url = authType ===
|
|
69
|
+
const url = authType === "govid"
|
|
66
70
|
? `${hostOauth}/api-user/auth_data?token=${code}`
|
|
67
71
|
: `${hostOauth}/oauth/token?code=${code}`;
|
|
68
72
|
try {
|
|
69
73
|
const response = await fetchWithoutSSL(url);
|
|
70
74
|
const body = await response.text();
|
|
71
|
-
console.log(`${authType} login: ${code} ${config.debug ? body :
|
|
72
|
-
if (response.status !== 200 || !body?.startsWith?.(
|
|
75
|
+
console.log(`${authType} login: ${code} ${config.debug ? body : ""}`);
|
|
76
|
+
if (response.status !== 200 || !body?.startsWith?.("{")) {
|
|
73
77
|
return reply.status(response.status).send(body);
|
|
74
78
|
}
|
|
75
79
|
const data = JSON.parse(body);
|
|
76
|
-
const hookData = await applyHook(
|
|
80
|
+
const hookData = (await applyHook("onAuthByData", {
|
|
81
|
+
req,
|
|
82
|
+
pg,
|
|
83
|
+
data,
|
|
84
|
+
authType,
|
|
85
|
+
}));
|
|
77
86
|
if (hookData?.href) {
|
|
78
87
|
return reply.redirect(hookData.href);
|
|
79
88
|
}
|
|
80
89
|
// get user / create new user
|
|
81
90
|
const _user = config.pg && !hookData?.user
|
|
82
91
|
? await getQuery({ pg, data })
|
|
83
|
-
:
|
|
92
|
+
: hookData?.user || {};
|
|
84
93
|
await logAuth({
|
|
85
94
|
uid: _user?.uid,
|
|
86
|
-
type:
|
|
95
|
+
type: "byData",
|
|
87
96
|
data,
|
|
88
97
|
ip,
|
|
89
98
|
}, pg);
|
|
90
|
-
console.log(
|
|
99
|
+
console.log("register user sql");
|
|
91
100
|
timeList.push(Date.now());
|
|
92
|
-
logger.file(
|
|
101
|
+
logger.file("auth/by_data", {
|
|
93
102
|
msec: Date.now() - d1,
|
|
94
103
|
time: {
|
|
95
104
|
total: timeList[5] - timeList[0],
|
|
@@ -104,12 +113,17 @@ export default async function authByData(req, reply) {
|
|
|
104
113
|
ip,
|
|
105
114
|
});
|
|
106
115
|
// console.log(user)
|
|
107
|
-
const href = await authorizeUser(_user, req, authType);
|
|
116
|
+
const href = await authorizeUser(_user, req, authType); // govid / google(social)
|
|
108
117
|
// console.log(href)
|
|
109
118
|
return reply.redirect(href);
|
|
110
119
|
}
|
|
111
120
|
catch (err) {
|
|
112
|
-
logger.file(
|
|
113
|
-
|
|
121
|
+
logger.file("auth/by_data/error", {
|
|
122
|
+
error: err.toString(),
|
|
123
|
+
stack: err.stack,
|
|
124
|
+
});
|
|
125
|
+
return reply
|
|
126
|
+
.status(500)
|
|
127
|
+
.send(`Помилка авторизації через ${authType === "govid" ? "id.gov.ua" : "google"}. Зверніться до Адміністратора!`);
|
|
114
128
|
}
|
|
115
129
|
}
|
|
@@ -1,56 +1,69 @@
|
|
|
1
|
-
import pgClients from
|
|
2
|
-
import dataInsert from
|
|
3
|
-
import { sign, scryptHash } from
|
|
4
|
-
import authorizeUser from
|
|
5
|
-
const getIp = (req) => (req.headers?.[
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.split(
|
|
1
|
+
import pgClients from "../../../../plugins/pg/pgClients.js";
|
|
2
|
+
import dataInsert from "../../../../plugins/crud/funcs/dataInsert.js";
|
|
3
|
+
import { sign, scryptHash } from "../../../../plugins/auth/funcs/jwt.js";
|
|
4
|
+
import authorizeUser from "../../../../plugins/auth/funcs/authorizeUser.js";
|
|
5
|
+
const getIp = (req) => (req.headers?.["x-real-ip"] ||
|
|
6
|
+
req.headers?.["x-forwarded-for"] ||
|
|
7
|
+
req.ip ||
|
|
8
|
+
req.connection?.remoteAddress ||
|
|
9
|
+
"")
|
|
10
|
+
.split(":")
|
|
11
11
|
.pop();
|
|
12
12
|
const expireMsec = 1000 * 60 * 60;
|
|
13
13
|
// ? Request example: http://localhost:3000/oauth/authorize?response_type=code&client_id=2835134164020233999&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Foauth%2Ftoken%3Fclient_id%3D2835134164020233999%26noredirect%3D1%26grant_type%3Dauthorization_code
|
|
14
14
|
// ? add &noredirect=1 to retrieve code via GET request or simply send POST requests
|
|
15
15
|
export default async function authorize(req, reply) {
|
|
16
16
|
const { pg = pgClients.client, query, body } = req;
|
|
17
|
-
const payload = req.method ===
|
|
17
|
+
const payload = req.method === "POST" ? body : query;
|
|
18
18
|
const { response_type, client_id, redirect_uri, scope } = payload;
|
|
19
19
|
if (response_type !== "code") {
|
|
20
|
-
return reply
|
|
20
|
+
return reply
|
|
21
|
+
.code(400)
|
|
22
|
+
.send({ message: "unsupported response_type", code: 400 });
|
|
21
23
|
}
|
|
22
24
|
if (!client_id) {
|
|
23
|
-
return reply
|
|
25
|
+
return reply
|
|
26
|
+
.code(400)
|
|
27
|
+
.send({ message: "not enough query params: client_id", code: 400 });
|
|
24
28
|
}
|
|
25
|
-
const q = `select owner_user_id, client_secret_hash, redirect_uris from oauth.clients where client_id=$1 and token_endpoint_auth_method=$2 and ${scope ?
|
|
26
|
-
const { owner_user_id: userId, client_secret_hash: secret, redirect_uris = [] } = pg.pk?.[
|
|
27
|
-
? await pg
|
|
29
|
+
const q = `select owner_user_id, client_secret_hash, redirect_uris from oauth.clients where client_id=$1 and token_endpoint_auth_method=$2 and ${scope ? "$1=any(scopes)" : "1=1"}`;
|
|
30
|
+
const { owner_user_id: userId, client_secret_hash: secret, redirect_uris = [], } = pg.pk?.["oauth.clients"]
|
|
31
|
+
? await pg
|
|
32
|
+
.query(q, [client_id, "private_key_jwt"])
|
|
33
|
+
.then((el) => el.rows?.[0] || {})
|
|
28
34
|
: {};
|
|
29
35
|
if (!userId) {
|
|
30
36
|
return reply.code(400).send({ message: "invalid client id", code: 400 });
|
|
31
37
|
}
|
|
32
|
-
if (redirect_uri &&
|
|
38
|
+
if (redirect_uri &&
|
|
39
|
+
Array.isArray(redirect_uris) &&
|
|
40
|
+
!redirect_uris.includes(redirect_uri)) {
|
|
33
41
|
return reply.code(400).send({ message: "invalid redirect_uri", code: 400 });
|
|
34
42
|
}
|
|
35
|
-
const user = pg.pk?.[
|
|
43
|
+
const user = pg.pk?.["admin.users"]
|
|
44
|
+
? await pg
|
|
45
|
+
.query("select * from admin.users where uid=$1 and enabled limit 1", [
|
|
46
|
+
userId,
|
|
47
|
+
])
|
|
48
|
+
.then((el) => el.rows[0])
|
|
49
|
+
: null;
|
|
36
50
|
if (!user) {
|
|
37
51
|
return reply.code(404).send({ message: "user not found", code: 404 });
|
|
38
52
|
}
|
|
39
|
-
|
|
40
|
-
const href1 = await authorizeUser(user, req, 'jwt', expireMsec);
|
|
53
|
+
const href1 = await authorizeUser(user, req, "jwt", expireMsec);
|
|
41
54
|
// Generate authorization code
|
|
42
55
|
const code = sign(userId, secret, expireMsec);
|
|
43
56
|
const tokenHash = await scryptHash(code);
|
|
44
57
|
const ip = getIp(req);
|
|
45
58
|
// disable access via old tokens
|
|
46
|
-
if (pg.pk?.[
|
|
47
|
-
await pg.query(
|
|
59
|
+
if (pg.pk?.["oauth.tokens"]) {
|
|
60
|
+
await pg.query("update oauth.tokens set revoked_at = now(), revocation_reason='refresh' where client_id=$1", [client_id]);
|
|
48
61
|
}
|
|
49
62
|
await dataInsert({
|
|
50
63
|
pg,
|
|
51
|
-
table:
|
|
64
|
+
table: "oauth.tokens",
|
|
52
65
|
data: {
|
|
53
|
-
token_type:
|
|
66
|
+
token_type: "access",
|
|
54
67
|
token_hash: tokenHash,
|
|
55
68
|
token_hint: code.slice(-6),
|
|
56
69
|
// jti: undefined,
|
|
@@ -67,11 +80,13 @@ export default async function authorize(req, reply) {
|
|
|
67
80
|
},
|
|
68
81
|
uid: userId,
|
|
69
82
|
});
|
|
70
|
-
const backUrl = redirect_uri ? `${redirect_uri}?code=${code}` :
|
|
71
|
-
const href = redirect_uri && !redirect_uri.includes(
|
|
83
|
+
const backUrl = redirect_uri ? `${redirect_uri}?code=${code}` : "";
|
|
84
|
+
const href = redirect_uri && !redirect_uri.includes("?")
|
|
72
85
|
? backUrl
|
|
73
|
-
:
|
|
74
|
-
if (req.method ===
|
|
86
|
+
: backUrl?.replace?.(/\?code=/, "&code=") || href1;
|
|
87
|
+
if (req.method === "POST" ||
|
|
88
|
+
payload.noredirect ||
|
|
89
|
+
process.env.NODE_ENV === "test") {
|
|
75
90
|
return reply.code(200).send(code);
|
|
76
91
|
}
|
|
77
92
|
return reply.redirect(href);
|
|
@@ -1,67 +1,92 @@
|
|
|
1
1
|
import { verify, scryptVerify } from "../../../../plugins/auth/funcs/jwt.js";
|
|
2
|
-
import pgClients from
|
|
3
|
-
import authorizeUser from
|
|
2
|
+
import pgClients from "../../../../plugins/pg/pgClients.js";
|
|
3
|
+
import authorizeUser from "../../../../plugins/auth/funcs/authorizeUser.js";
|
|
4
4
|
const expireMsec = 1000 * 60 * 60;
|
|
5
|
-
const getIp = (req) => (req.headers?.[
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.split(
|
|
5
|
+
const getIp = (req) => (req.headers?.["x-real-ip"] ||
|
|
6
|
+
req.headers?.["x-forwarded-for"] ||
|
|
7
|
+
req.ip ||
|
|
8
|
+
req.connection?.remoteAddress ||
|
|
9
|
+
"")
|
|
10
|
+
.split(":")
|
|
11
11
|
.pop();
|
|
12
12
|
export default async function oauthToken(req, reply) {
|
|
13
13
|
const { pg = pgClients.client, query, body } = req;
|
|
14
|
-
const payload = req.method ===
|
|
14
|
+
const payload = req.method === "POST" ? body : query;
|
|
15
15
|
const { grant_type, client_id, code, redirect_uri, code_verifier } = payload;
|
|
16
16
|
if (grant_type !== "authorization_code") {
|
|
17
|
-
return reply
|
|
17
|
+
return reply
|
|
18
|
+
.code(400)
|
|
19
|
+
.send({ message: "unsupported grant_type", code: 400 });
|
|
18
20
|
}
|
|
19
21
|
if (!client_id) {
|
|
20
|
-
return reply
|
|
22
|
+
return reply
|
|
23
|
+
.code(400)
|
|
24
|
+
.send({ message: "not enough params: client_id", code: 400 });
|
|
21
25
|
}
|
|
22
26
|
if (!code) {
|
|
23
|
-
return reply
|
|
27
|
+
return reply
|
|
28
|
+
.code(400)
|
|
29
|
+
.send({ message: "not enough params: code", code: 400 });
|
|
24
30
|
}
|
|
25
31
|
const q = `select owner_user_id, client_secret_hash, redirect_uris from oauth.clients where client_id=$1 and token_endpoint_auth_method=$2`;
|
|
26
|
-
const { owner_user_id: userId, client_secret_hash: secret, redirect_uris = [] } = pg.pk?.[
|
|
27
|
-
? await pg
|
|
32
|
+
const { owner_user_id: userId, client_secret_hash: secret, redirect_uris = [], } = pg.pk?.["oauth.clients"]
|
|
33
|
+
? await pg
|
|
34
|
+
.query(q, [client_id, "private_key_jwt"])
|
|
35
|
+
.then((el) => el.rows?.[0] || {})
|
|
28
36
|
: {};
|
|
29
37
|
const isCodeValid = verify(code, secret);
|
|
30
|
-
const q1 =
|
|
31
|
-
const { token_hash: stored, expires_at, ip: storedIp } = await pg.query(q1, [client_id]).then((el) => el.rows?.[0] || {});
|
|
38
|
+
const q1 = "select token_hash, expires_at, ip from oauth.tokens where client_id=$1 and revoked_at is null and expires_at > now()";
|
|
39
|
+
const { token_hash: stored, expires_at, ip: storedIp, } = await pg.query(q1, [client_id]).then((el) => el.rows?.[0] || {});
|
|
32
40
|
const ip = getIp(req);
|
|
33
41
|
if (storedIp !== ip) {
|
|
34
|
-
return reply
|
|
42
|
+
return reply
|
|
43
|
+
.code(403)
|
|
44
|
+
.send({ message: "access restricted: wrong IP address", code: 403 });
|
|
35
45
|
}
|
|
36
46
|
if (!stored) {
|
|
37
|
-
return reply
|
|
47
|
+
return reply
|
|
48
|
+
.code(403)
|
|
49
|
+
.send({ message: "access restricted: code expired", code: 403 });
|
|
38
50
|
}
|
|
39
51
|
const isValid = await scryptVerify(stored, code);
|
|
40
52
|
if (!isValid) {
|
|
41
|
-
return reply
|
|
53
|
+
return reply
|
|
54
|
+
.code(403)
|
|
55
|
+
.send({ message: "access restricted: stored code mismatch", code: 403 });
|
|
42
56
|
}
|
|
43
57
|
if (!isCodeValid) {
|
|
44
|
-
return reply
|
|
58
|
+
return reply
|
|
59
|
+
.code(403)
|
|
60
|
+
.send({ message: "access restricted: invalid code", code: 403 });
|
|
45
61
|
}
|
|
46
62
|
if (!userId) {
|
|
47
63
|
return reply.code(400).send({ message: "invalid client id", code: 400 });
|
|
48
64
|
}
|
|
49
|
-
if (redirect_uri &&
|
|
65
|
+
if (redirect_uri &&
|
|
66
|
+
Array.isArray(redirect_uris) &&
|
|
67
|
+
!redirect_uris.includes(redirect_uri)) {
|
|
50
68
|
return reply.code(400).send({ message: "invalid redirect_uri", code: 400 });
|
|
51
69
|
}
|
|
52
|
-
const user = pg.pk?.[
|
|
70
|
+
const user = pg.pk?.["admin.users"]
|
|
71
|
+
? await pg
|
|
72
|
+
.query("select * from admin.users where uid=$1 and enabled limit 1", [
|
|
73
|
+
userId,
|
|
74
|
+
])
|
|
75
|
+
.then((el) => el.rows[0])
|
|
76
|
+
: null;
|
|
53
77
|
if (!user) {
|
|
54
78
|
return reply.code(404).send({ message: "user not found", code: 404 });
|
|
55
79
|
}
|
|
56
|
-
Object.assign(user, { auth_type: 'jwt' });
|
|
57
80
|
const expire = expires_at ? expires_at - Date.now() : expireMsec;
|
|
58
|
-
const href1 = await authorizeUser(user, req,
|
|
59
|
-
const backUrl = redirect_uri ? `${redirect_uri}?code=${code}` :
|
|
60
|
-
const href = redirect_uri && !redirect_uri.includes(
|
|
81
|
+
const href1 = await authorizeUser(user, req, "jwt", expire);
|
|
82
|
+
const backUrl = redirect_uri ? `${redirect_uri}?code=${code}` : "";
|
|
83
|
+
const href = redirect_uri && !redirect_uri.includes("?")
|
|
61
84
|
? backUrl
|
|
62
|
-
:
|
|
63
|
-
if (req.method ===
|
|
64
|
-
|
|
85
|
+
: backUrl?.replace?.(/\?code=/, "&code=") || href1;
|
|
86
|
+
if (req.method === "POST" ||
|
|
87
|
+
payload.noredirect ||
|
|
88
|
+
process.env.NODE_ENV === "test") {
|
|
89
|
+
return reply.code(200).send("auth success");
|
|
65
90
|
}
|
|
66
91
|
return reply.redirect(href);
|
|
67
92
|
}
|
|
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
6
6
|
import config from "../../../../config.js";
|
|
7
7
|
import pgClients from "../../../plugins/pg/pgClients.js";
|
|
8
|
-
import applyHook from "
|
|
8
|
+
import { applyHook } from "../../../../utils.js";
|
|
9
9
|
import menuDirs from "../../../plugins/table/funcs/menuDirs.js";
|
|
10
10
|
const menuCache = [];
|
|
11
11
|
// check module dir
|
|
@@ -4,7 +4,7 @@ import autoIndex from "../../../plugins/pg/funcs/autoIndex.js";
|
|
|
4
4
|
import getSelect from "../../../plugins/table/funcs/getSelect.js";
|
|
5
5
|
import getSelectVal from "../../../plugins/table/funcs/metaFormat/getSelectVal.js";
|
|
6
6
|
import pgClients from "../../../plugins/pg/pgClients.js";
|
|
7
|
-
import applyHook from "
|
|
7
|
+
import { applyHook } from "../../../../utils.js";
|
|
8
8
|
import getTemplate from "../../../plugins/table/funcs/getTemplate.js";
|
|
9
9
|
export default async function filterAPI(req, reply, iscalled) {
|
|
10
10
|
const time = Date.now();
|
|
@@ -4,7 +4,7 @@ import getFilterSQL from "../../../plugins/table/funcs/getFilterSQL/index.js";
|
|
|
4
4
|
import getAccess from "../../../plugins/crud/funcs/getAccess.js";
|
|
5
5
|
import setToken from "../../../plugins/crud/funcs/setToken.js";
|
|
6
6
|
import gisIRColumn from "../../../plugins/table/funcs/gisIRColumn.js";
|
|
7
|
-
import applyHook from "
|
|
7
|
+
import { applyHook } from "../../../../utils.js";
|
|
8
8
|
import getSelect from "../../../plugins/table/funcs/getSelect.js";
|
|
9
9
|
import setOpt from "../../../plugins/crud/funcs/setOpt.js";
|
|
10
10
|
import getOpt from "../../../plugins/crud/funcs/getOpt.js";
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import getMeta from "../../../plugins/pg/funcs/getMeta.js";
|
|
3
3
|
import dataInsert from "../../../plugins/crud/funcs/dataInsert.js";
|
|
4
4
|
import dataUpdate from "../../../plugins/crud/funcs/dataUpdate.js";
|
|
5
|
-
import applyHook from "
|
|
5
|
+
import { applyHook } from "../../../../utils.js";
|
|
6
6
|
import uploadMultiPart from "../../../plugins/file/uploadMultiPart.js";
|
|
7
7
|
const tableList = {
|
|
8
8
|
comment: "crm.communications",
|
package/dist/utils.js
CHANGED
|
@@ -52,9 +52,7 @@ export { default as validateData } from "./server/plugins/crud/funcs/validateDat
|
|
|
52
52
|
// policy
|
|
53
53
|
export { default as checkXSS } from "./server/plugins/policy/funcs/checkXSS.js";
|
|
54
54
|
// hook
|
|
55
|
-
export
|
|
56
|
-
export { default as applyHookSync } from "./server/plugins/hook/funcs/applyHookSync.js";
|
|
57
|
-
export { default as addHook } from "./server/plugins/hook/funcs/addHook.js";
|
|
55
|
+
export * from "./server/plugins/hook/index.js";
|
|
58
56
|
export { default as execMigrations } from "./server/plugins/migration/exec.migrations.js";
|
|
59
57
|
export { default as execSql } from "./server/plugins/migration/exec.sql.js";
|
|
60
58
|
// cron
|
package/package.json
CHANGED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
import config from "../../../../config.js";
|
|
3
|
-
import hookList from "../hookList.js";
|
|
4
|
-
export default async function applyHook(name, data) {
|
|
5
|
-
const { trace } = config;
|
|
6
|
-
if (trace)
|
|
7
|
-
console.log("applyHook", name);
|
|
8
|
-
if (!hookList[name]?.length)
|
|
9
|
-
return null;
|
|
10
|
-
const result = {};
|
|
11
|
-
await Promise.all(hookList[name].map(async (hook) => {
|
|
12
|
-
const hookData = await hook({ ...data, config });
|
|
13
|
-
if (hookData) {
|
|
14
|
-
if (trace)
|
|
15
|
-
console.log("applyHook", name, hookData);
|
|
16
|
-
Object.assign(result, hookData);
|
|
17
|
-
}
|
|
18
|
-
})).catch((err) => {
|
|
19
|
-
console.error("applyHook", name, err.toString());
|
|
20
|
-
});
|
|
21
|
-
if (Object.keys(result).length) {
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|