directus 9.0.1 → 9.1.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/dist/app.js +2 -0
- package/dist/auth/drivers/ldap.js +9 -13
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +2 -2
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +8 -2
- package/dist/controllers/notifications.d.ts +2 -0
- package/dist/controllers/notifications.js +147 -0
- package/dist/database/migrations/20211118A-add-notifications.d.ts +3 -0
- package/dist/database/migrations/20211118A-add-notifications.js +28 -0
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +14 -0
- package/dist/database/system-data/collections/collections.yaml +2 -0
- package/dist/database/system-data/fields/notifications.yaml +12 -0
- package/dist/database/system-data/fields/users.yaml +5 -0
- package/dist/database/system-data/relations/relations.yaml +6 -0
- package/dist/middleware/get-permissions.js +3 -102
- package/dist/services/activity.d.ts +7 -5
- package/dist/services/activity.js +82 -3
- package/dist/services/collections.js +12 -1
- package/dist/services/graphql.js +3 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/mail/index.js +2 -2
- package/dist/services/mail/templates/base.liquid +153 -85
- package/dist/services/mail/templates/password-reset.liquid +3 -2
- package/dist/services/mail/templates/user-invitation.liquid +4 -4
- package/dist/services/notifications.d.ts +12 -0
- package/dist/services/notifications.js +41 -0
- package/dist/services/users.js +1 -0
- package/dist/types/collection.d.ts +1 -0
- package/dist/utils/get-local-type.js +12 -7
- package/dist/utils/get-permissions.d.ts +3 -0
- package/dist/utils/get-permissions.js +106 -0
- package/dist/utils/md.d.ts +4 -0
- package/dist/utils/md.js +15 -0
- package/dist/utils/sanitize-query.js +3 -3
- package/dist/utils/user-name.d.ts +2 -0
- package/dist/utils/user-name.js +16 -0
- package/package.json +26 -23
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __exportStar(require("./graphql"), exports);
|
|
|
24
24
|
__exportStar(require("./import"), exports);
|
|
25
25
|
__exportStar(require("./mail"), exports);
|
|
26
26
|
__exportStar(require("./meta"), exports);
|
|
27
|
+
__exportStar(require("./notifications"), exports);
|
|
27
28
|
__exportStar(require("./panels"), exports);
|
|
28
29
|
__exportStar(require("./payload"), exports);
|
|
29
30
|
__exportStar(require("./permissions"), exports);
|
|
@@ -33,10 +33,10 @@ class MailService {
|
|
|
33
33
|
async send(options) {
|
|
34
34
|
const { template, ...emailOptions } = options;
|
|
35
35
|
let { html } = options;
|
|
36
|
-
const
|
|
36
|
+
const defaultTemplateData = await this.getDefaultTemplateData();
|
|
37
|
+
const from = `${defaultTemplateData.projectName} <${options.from || env_1.default.EMAIL_FROM}>`;
|
|
37
38
|
if (template) {
|
|
38
39
|
let templateData = template.data;
|
|
39
|
-
const defaultTemplateData = await this.getDefaultTemplateData();
|
|
40
40
|
templateData = {
|
|
41
41
|
...defaultTemplateData,
|
|
42
42
|
...templateData,
|
|
@@ -1,95 +1,163 @@
|
|
|
1
|
-
<!
|
|
2
|
-
<html
|
|
3
|
-
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
4
3
|
<head>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
4
|
+
|
|
5
|
+
<title>{{ projectName }} Email Service</title>
|
|
6
|
+
|
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
9
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
10
|
+
|
|
11
|
+
<style type="text/css" id="hs-inline-css">
|
|
12
|
+
/*<![CDATA[*/
|
|
13
|
+
|
|
14
|
+
/* CLIENT-SPECIFIC STYLES */
|
|
15
|
+
body, table, td, a {
|
|
16
|
+
-webkit-text-size-adjust: 100%;
|
|
17
|
+
-ms-text-size-adjust: 100%;
|
|
18
|
+
-webkit-font-smoothing: antialiased;
|
|
19
|
+
}
|
|
20
|
+
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
|
21
|
+
img { -ms-interpolation-mode: bicubic; }
|
|
22
|
+
|
|
23
|
+
/* RESET STYLES */
|
|
24
|
+
img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
|
|
25
|
+
table { border-collapse: collapse !important; }
|
|
26
|
+
body { height: 100% !important; margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
|
27
|
+
|
|
28
|
+
/* iOS BLUE LINKS */
|
|
29
|
+
a[x-apple-data-detectors] {
|
|
30
|
+
color: inherit !important;
|
|
31
|
+
text-decoration: none !important;
|
|
32
|
+
font-size: inherit !important;
|
|
33
|
+
font-family: inherit !important;
|
|
34
|
+
font-weight: inherit !important;
|
|
35
|
+
line-height: inherit !important;
|
|
36
|
+
}
|
|
37
|
+
body a {
|
|
38
|
+
color: #00C897;
|
|
39
|
+
text-decoration: none;
|
|
40
|
+
}
|
|
41
|
+
hr {
|
|
42
|
+
width:66%;
|
|
43
|
+
margin:40px auto;
|
|
44
|
+
border:1px solid #d3dae4;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* MOBILE STYLES */
|
|
48
|
+
@media screen and (max-width: 600px) {
|
|
49
|
+
.img-max {
|
|
50
|
+
width: 100% !important;
|
|
51
|
+
max-width: 100% !important;
|
|
52
|
+
height: auto !important;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.max-width {
|
|
56
|
+
max-width: 100% !important;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#content {
|
|
60
|
+
padding-left: 5% !important;
|
|
61
|
+
padding-right: 5% !important;
|
|
62
|
+
padding-top: 30px !important;
|
|
63
|
+
padding-bottom: 30px !important;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* DARK MODE */
|
|
68
|
+
@media (prefers-color-scheme: dark) {
|
|
69
|
+
#background {
|
|
70
|
+
background-color: #172940 !important;
|
|
71
|
+
}
|
|
72
|
+
#content {
|
|
73
|
+
background-color: #071930 !important;
|
|
74
|
+
color: #FFFFFF !important;
|
|
75
|
+
}
|
|
76
|
+
.link {
|
|
77
|
+
color: #00c897 !important;
|
|
78
|
+
}
|
|
79
|
+
.button {
|
|
80
|
+
background-color:#0BA582 !important;
|
|
81
|
+
}
|
|
82
|
+
hr {
|
|
83
|
+
border:1px solid #172940 !important;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ANDROID CENTER FIX */
|
|
88
|
+
div[style*="margin: 16px 0;"] {
|
|
89
|
+
margin: 0 !important;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
blockquote {
|
|
93
|
+
background: #f0f4f9 !important;
|
|
94
|
+
border-radius: 4px !important;
|
|
95
|
+
margin: 0 !important;
|
|
96
|
+
padding: 24px !important;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
blockquote > p {
|
|
100
|
+
margin: 0 !important;
|
|
101
|
+
}
|
|
102
|
+
/*]]>*/
|
|
103
|
+
</style>
|
|
104
|
+
|
|
105
|
+
<meta name="generator" content="Directus">
|
|
106
|
+
<meta property="og:url" content="http://directus-20534155.hs-sites.com/7dea362b-3fac-3e00-956a-4952a3d4f474">
|
|
107
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
108
|
+
<meta name="robots" content="noindex,follow">
|
|
109
|
+
|
|
50
110
|
</head>
|
|
111
|
+
<body style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; height:100% !important; width:100% !important; margin:0 !important; padding:0; !important background-color:#f6f6f6" bgcolor="#f6f6f6">
|
|
51
112
|
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<img src="{{ projectLogo }}" alt="{{ projectName }}" width="130"
|
|
62
|
-
style="display: block;" />
|
|
63
|
-
</td>
|
|
64
|
-
</tr>
|
|
113
|
+
<!-- HIDDEN PREHEADER TEXT -->
|
|
114
|
+
<div class="preview-text" style="display:none;font-size:1px;color:#172940;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;"> </div>
|
|
115
|
+
|
|
116
|
+
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; border-collapse:collapse !important">
|
|
117
|
+
<tbody>
|
|
118
|
+
<tr>
|
|
119
|
+
<td id="background" align="center" valign="top" width="100%" bgcolor="#172940" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; background-size:cover; padding:50px 15px 0 15px; background-color:#172940">
|
|
120
|
+
<!--[if (gte mso 9)|(IE)]>
|
|
121
|
+
<table role="presentation" align="center" border="0" cellspacing="0" cellpadding="0" width="600">
|
|
65
122
|
<tr>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
123
|
+
<td align="center" valign="top" width="600">
|
|
124
|
+
<![endif]-->
|
|
125
|
+
<table role="presentation" align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; border-collapse:collapse !important; max-width:600px">
|
|
126
|
+
<tbody>
|
|
127
|
+
<tr>
|
|
128
|
+
<td align="left" valign="top" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:0 0 30px 0">
|
|
129
|
+
<table><tbody><tr><td align="center" valign="middle" style="background-color:{{ projectColor }};width:48px;height:48px;border-radius:4px;padding:6px;">
|
|
130
|
+
<img id="logo" src="{{ projectLogo }}" alt="{{ projectName }} Logo" width="40" height="auto" border="0" style="-ms-interpolation-mode:bicubic; border:0; height:auto; line-height:100%; outline:none; text-decoration:none; display:block; width:40px;object-fit:contain;">
|
|
131
|
+
</td></tr></tbody></table>
|
|
132
|
+
</td>
|
|
133
|
+
</tr>
|
|
134
|
+
<tr>
|
|
135
|
+
<td id="content" align="left" valign="top" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:40px 50px 50px 50px; font-family:Open Sans, Helvetica, Arial, sans-serif; border-radius:4px; box-shadow:0 4px 0 #15253A; background-color:#FFFFFE; color:#172940; font-size:15px; line-height:26px; margin:0" bgcolor="#FFFFFE">
|
|
136
|
+
<div id="hs_cos_wrapper_email_template_main_email_body" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module" style="color: inherit; font-size: inherit; line-height: inherit;" data-hs-cos-general-type="widget" data-hs-cos-type="module">
|
|
137
|
+
|
|
71
138
|
{% block content %}{{ html }}{% endblock %}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<tr>
|
|
81
|
-
<td style="color:#b0bec5; font-family:Helvetica, Arial, sans-serif; font-size:12px;"
|
|
82
|
-
width="75%">
|
|
139
|
+
|
|
140
|
+
</div>
|
|
141
|
+
</td>
|
|
142
|
+
</tr>
|
|
143
|
+
<tr>
|
|
144
|
+
<td align="center" valign="middle" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:25px 0; font-family:Open Sans, Helvetica, Arial, sans-serif; color:#FFFFFE">
|
|
145
|
+
<p style="margin-bottom: 1em; color: #A2B5CD;font-size: 12px; line-height: 16px;">
|
|
146
|
+
Sent by the team at {{ projectName }} — <a style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; text-decoration:none; color:#A2B5CD" class="unsubscribe" data-unsubscribe="true" href="{{ url }}" data-hs-link-id="0" target="_blank">Manage Emails</a><br>
|
|
83
147
|
{% block footer %}{% endblock %}
|
|
84
|
-
</
|
|
85
|
-
</
|
|
86
|
-
</
|
|
87
|
-
</
|
|
148
|
+
</p>
|
|
149
|
+
</td>
|
|
150
|
+
</tr>
|
|
151
|
+
</tbody>
|
|
152
|
+
</table>
|
|
153
|
+
<!--[if (gte mso 9)|(IE)]>
|
|
154
|
+
</td>
|
|
88
155
|
</tr>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
156
|
+
</table>
|
|
157
|
+
<![endif]-->
|
|
158
|
+
</td>
|
|
159
|
+
</tr>
|
|
160
|
+
</tbody>
|
|
92
161
|
</table>
|
|
93
162
|
</body>
|
|
94
|
-
|
|
95
163
|
</html>
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
|
|
8
8
|
<p style="text-align: center; padding: 20px 0;">
|
|
9
9
|
<a href="{{ url }}">
|
|
10
|
-
|
|
10
|
+
<b>Reset Your Password</b>
|
|
11
11
|
</a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p>
|
|
15
15
|
<b>Important: This link will expire in 24 hours.</b>
|
|
16
|
+
<br>
|
|
16
17
|
</p>
|
|
17
18
|
|
|
18
19
|
<p>
|
|
19
20
|
Thank you,<br>
|
|
20
|
-
{{ projectName }}
|
|
21
|
+
The {{ projectName }} Team
|
|
21
22
|
</p>
|
|
22
23
|
|
|
23
24
|
{% endblock %}
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
{% block content %}
|
|
3
3
|
|
|
4
4
|
<p>
|
|
5
|
-
|
|
5
|
+
You have been invited to join <i>{{ projectName }}</i>. Please click the button below to accept this invitation and join the project:
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p style="text-align: center; padding: 20px 0;">
|
|
9
9
|
<a href="{{ url }}">
|
|
10
|
-
Join {{ projectName }}
|
|
10
|
+
<b>Join {{ projectName }}</b>
|
|
11
11
|
</a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p>
|
|
15
|
-
Thank
|
|
16
|
-
{{ projectName }}
|
|
15
|
+
Thank you,<br>
|
|
16
|
+
The {{ projectName }} Team
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
{% endblock %}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { UsersService, MailService } from '.';
|
|
2
|
+
import { AbstractServiceOptions, PrimaryKey } from '../types';
|
|
3
|
+
import { ItemsService, MutationOptions } from './items';
|
|
4
|
+
import { Notification } from '@directus/shared/types';
|
|
5
|
+
export declare class NotificationsService extends ItemsService {
|
|
6
|
+
usersService: UsersService;
|
|
7
|
+
mailService: MailService;
|
|
8
|
+
constructor(options: AbstractServiceOptions);
|
|
9
|
+
createOne(data: Partial<Notification>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
10
|
+
createMany(data: Partial<Notification>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
11
|
+
sendEmail(data: Partial<Notification>): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NotificationsService = void 0;
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const items_1 = require("./items");
|
|
6
|
+
const md_1 = require("../utils/md");
|
|
7
|
+
class NotificationsService extends items_1.ItemsService {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
super('directus_notifications', options);
|
|
10
|
+
this.usersService = new _1.UsersService({ schema: this.schema });
|
|
11
|
+
this.mailService = new _1.MailService({ schema: this.schema, accountability: this.accountability });
|
|
12
|
+
}
|
|
13
|
+
async createOne(data, opts) {
|
|
14
|
+
await this.sendEmail(data);
|
|
15
|
+
return super.createOne(data, opts);
|
|
16
|
+
}
|
|
17
|
+
async createMany(data, opts) {
|
|
18
|
+
for (const notification of data) {
|
|
19
|
+
await this.sendEmail(notification);
|
|
20
|
+
}
|
|
21
|
+
return super.createMany(data, opts);
|
|
22
|
+
}
|
|
23
|
+
async sendEmail(data) {
|
|
24
|
+
if (data.recipient) {
|
|
25
|
+
const user = await this.usersService.readOne(data.recipient, { fields: ['email', 'email_notifications'] });
|
|
26
|
+
if (user.email && user.email_notifications === true) {
|
|
27
|
+
await this.mailService.send({
|
|
28
|
+
template: {
|
|
29
|
+
name: 'base',
|
|
30
|
+
data: {
|
|
31
|
+
html: data.message ? (0, md_1.md)(data.message) : '',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
to: user.email,
|
|
35
|
+
subject: data.subject,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.NotificationsService = NotificationsService;
|
package/dist/services/users.js
CHANGED
|
@@ -181,6 +181,7 @@ class UsersService extends items_1.ItemsService {
|
|
|
181
181
|
*/
|
|
182
182
|
async deleteMany(keys, opts) {
|
|
183
183
|
await this.checkRemainingAdminExistence(keys);
|
|
184
|
+
await this.knex('directus_notifications').update({ sender: null }).whereIn('sender', keys);
|
|
184
185
|
await super.deleteMany(keys, opts);
|
|
185
186
|
return keys;
|
|
186
187
|
}
|
|
@@ -9,6 +9,7 @@ export declare type CollectionMeta = {
|
|
|
9
9
|
translations: Record<string, string>;
|
|
10
10
|
item_duplication_fields: string[] | null;
|
|
11
11
|
accountability: 'all' | 'accountability' | null;
|
|
12
|
+
group: string | null;
|
|
12
13
|
};
|
|
13
14
|
export declare type Collection = {
|
|
14
15
|
collection: string;
|
|
@@ -53,8 +53,10 @@ const localTypeMap = {
|
|
|
53
53
|
blob: 'binary',
|
|
54
54
|
mediumblob: 'binary',
|
|
55
55
|
'int unsigned': 'integer',
|
|
56
|
-
'tinyint
|
|
57
|
-
'
|
|
56
|
+
'tinyint unsigned': 'integer',
|
|
57
|
+
'smallint unsigned': 'integer',
|
|
58
|
+
'mediumint unsigned': 'integer',
|
|
59
|
+
'bigint unsigned': 'integer',
|
|
58
60
|
// MS SQL
|
|
59
61
|
bit: 'boolean',
|
|
60
62
|
smallmoney: 'float',
|
|
@@ -100,7 +102,10 @@ const localTypeMap = {
|
|
|
100
102
|
function getLocalType(column, field) {
|
|
101
103
|
const database = (0, database_2.default)();
|
|
102
104
|
const databaseClient = (0, database_1.getDatabaseClient)(database);
|
|
103
|
-
|
|
105
|
+
if (!column)
|
|
106
|
+
return 'alias';
|
|
107
|
+
const dataType = column.data_type.toLowerCase();
|
|
108
|
+
const type = localTypeMap[dataType.split('(')[0]];
|
|
104
109
|
const special = field === null || field === void 0 ? void 0 : field.special;
|
|
105
110
|
if (special) {
|
|
106
111
|
if (special.includes('json'))
|
|
@@ -111,20 +116,20 @@ function getLocalType(column, field) {
|
|
|
111
116
|
return 'csv';
|
|
112
117
|
if (special.includes('uuid'))
|
|
113
118
|
return 'uuid';
|
|
114
|
-
if (type.startsWith('geometry')) {
|
|
119
|
+
if (type === null || type === void 0 ? void 0 : type.startsWith('geometry')) {
|
|
115
120
|
return special[0] || 'geometry';
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
/** Handle Postgres numeric decimals */
|
|
119
|
-
if (
|
|
124
|
+
if (dataType === 'numeric' && column.numeric_precision !== null && column.numeric_scale !== null) {
|
|
120
125
|
return 'decimal';
|
|
121
126
|
}
|
|
122
127
|
/** Handle MS SQL varchar(MAX) (eg TEXT) types */
|
|
123
|
-
if (
|
|
128
|
+
if (dataType === 'nvarchar(MAX)') {
|
|
124
129
|
return 'text';
|
|
125
130
|
}
|
|
126
131
|
/** Handle Boolean as TINYINT and edgecase MySQL where it still is just tinyint */
|
|
127
|
-
if (
|
|
132
|
+
if (databaseClient === 'mysql' && dataType === 'tinyint(1)') {
|
|
128
133
|
return 'boolean';
|
|
129
134
|
}
|
|
130
135
|
return type !== null && type !== void 0 ? type : 'unknown';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getPermissions = void 0;
|
|
7
|
+
const utils_1 = require("@directus/shared/utils");
|
|
8
|
+
const lodash_1 = require("lodash");
|
|
9
|
+
const database_1 = __importDefault(require("../database"));
|
|
10
|
+
const app_access_permissions_1 = require("../database/system-data/app-access-permissions");
|
|
11
|
+
const merge_permissions_1 = require("../utils/merge-permissions");
|
|
12
|
+
const users_1 = require("../services/users");
|
|
13
|
+
const roles_1 = require("../services/roles");
|
|
14
|
+
const cache_1 = require("../cache");
|
|
15
|
+
const object_hash_1 = __importDefault(require("object-hash"));
|
|
16
|
+
const env_1 = __importDefault(require("../env"));
|
|
17
|
+
async function getPermissions(accountability, schema) {
|
|
18
|
+
const database = (0, database_1.default)();
|
|
19
|
+
const { systemCache } = (0, cache_1.getCache)();
|
|
20
|
+
let permissions = [];
|
|
21
|
+
const { user, role, app, admin } = accountability;
|
|
22
|
+
const cacheKey = `permissions-${(0, object_hash_1.default)({ user, role, app, admin })}`;
|
|
23
|
+
if (env_1.default.CACHE_PERMISSIONS !== false) {
|
|
24
|
+
const cachedPermissions = await systemCache.get(cacheKey);
|
|
25
|
+
if (cachedPermissions) {
|
|
26
|
+
return cachedPermissions;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (accountability.admin !== true) {
|
|
30
|
+
const permissionsForRole = await database
|
|
31
|
+
.select('*')
|
|
32
|
+
.from('directus_permissions')
|
|
33
|
+
.where({ role: accountability.role });
|
|
34
|
+
const requiredPermissionData = {
|
|
35
|
+
$CURRENT_USER: [],
|
|
36
|
+
$CURRENT_ROLE: [],
|
|
37
|
+
};
|
|
38
|
+
permissions = permissionsForRole.map((permissionRaw) => {
|
|
39
|
+
const permission = (0, lodash_1.cloneDeep)(permissionRaw);
|
|
40
|
+
if (permission.permissions && typeof permission.permissions === 'string') {
|
|
41
|
+
permission.permissions = JSON.parse(permission.permissions);
|
|
42
|
+
}
|
|
43
|
+
else if (permission.permissions === null) {
|
|
44
|
+
permission.permissions = {};
|
|
45
|
+
}
|
|
46
|
+
if (permission.validation && typeof permission.validation === 'string') {
|
|
47
|
+
permission.validation = JSON.parse(permission.validation);
|
|
48
|
+
}
|
|
49
|
+
else if (permission.validation === null) {
|
|
50
|
+
permission.validation = {};
|
|
51
|
+
}
|
|
52
|
+
if (permission.presets && typeof permission.presets === 'string') {
|
|
53
|
+
permission.presets = JSON.parse(permission.presets);
|
|
54
|
+
}
|
|
55
|
+
else if (permission.presets === null) {
|
|
56
|
+
permission.presets = {};
|
|
57
|
+
}
|
|
58
|
+
if (permission.fields && typeof permission.fields === 'string') {
|
|
59
|
+
permission.fields = permission.fields.split(',');
|
|
60
|
+
}
|
|
61
|
+
else if (permission.fields === null) {
|
|
62
|
+
permission.fields = [];
|
|
63
|
+
}
|
|
64
|
+
const extractPermissionData = (val) => {
|
|
65
|
+
if (typeof val === 'string' && val.startsWith('$CURRENT_USER.')) {
|
|
66
|
+
requiredPermissionData.$CURRENT_USER.push(val.replace('$CURRENT_USER.', ''));
|
|
67
|
+
}
|
|
68
|
+
if (typeof val === 'string' && val.startsWith('$CURRENT_ROLE.')) {
|
|
69
|
+
requiredPermissionData.$CURRENT_ROLE.push(val.replace('$CURRENT_ROLE.', ''));
|
|
70
|
+
}
|
|
71
|
+
return val;
|
|
72
|
+
};
|
|
73
|
+
(0, utils_1.deepMap)(permission.permissions, extractPermissionData);
|
|
74
|
+
(0, utils_1.deepMap)(permission.validation, extractPermissionData);
|
|
75
|
+
(0, utils_1.deepMap)(permission.presets, extractPermissionData);
|
|
76
|
+
return permission;
|
|
77
|
+
});
|
|
78
|
+
if (accountability.app === true) {
|
|
79
|
+
permissions = (0, merge_permissions_1.mergePermissions)(permissions, app_access_permissions_1.appAccessMinimalPermissions.map((perm) => ({ ...perm, role: accountability.role })));
|
|
80
|
+
}
|
|
81
|
+
const usersService = new users_1.UsersService({ schema });
|
|
82
|
+
const rolesService = new roles_1.RolesService({ schema });
|
|
83
|
+
const filterContext = {};
|
|
84
|
+
if (accountability.user && requiredPermissionData.$CURRENT_USER.length > 0) {
|
|
85
|
+
filterContext.$CURRENT_USER = await usersService.readOne(accountability.user, {
|
|
86
|
+
fields: requiredPermissionData.$CURRENT_USER,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (accountability.role && requiredPermissionData.$CURRENT_ROLE.length > 0) {
|
|
90
|
+
filterContext.$CURRENT_ROLE = await rolesService.readOne(accountability.role, {
|
|
91
|
+
fields: requiredPermissionData.$CURRENT_ROLE,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
permissions = permissions.map((permission) => {
|
|
95
|
+
permission.permissions = (0, utils_1.parseFilter)(permission.permissions, accountability, filterContext);
|
|
96
|
+
permission.validation = (0, utils_1.parseFilter)(permission.validation, accountability, filterContext);
|
|
97
|
+
permission.presets = (0, utils_1.parseFilter)(permission.presets, accountability, filterContext);
|
|
98
|
+
return permission;
|
|
99
|
+
});
|
|
100
|
+
if (env_1.default.CACHE_PERMISSIONS !== false) {
|
|
101
|
+
await systemCache.set(cacheKey, permissions);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return permissions;
|
|
105
|
+
}
|
|
106
|
+
exports.getPermissions = getPermissions;
|
package/dist/utils/md.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.md = void 0;
|
|
7
|
+
const marked_1 = require("marked");
|
|
8
|
+
const sanitize_html_1 = __importDefault(require("sanitize-html"));
|
|
9
|
+
/**
|
|
10
|
+
* Render and sanitize a markdown string
|
|
11
|
+
*/
|
|
12
|
+
function md(str) {
|
|
13
|
+
return (0, sanitize_html_1.default)((0, marked_1.parse)(str));
|
|
14
|
+
}
|
|
15
|
+
exports.md = md;
|
|
@@ -117,8 +117,7 @@ function sanitizeFilter(rawFilter, accountability) {
|
|
|
117
117
|
return val;
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
|
-
|
|
121
|
-
return filters;
|
|
120
|
+
return (0, utils_1.parseFilter)(filters, accountability);
|
|
122
121
|
}
|
|
123
122
|
function sanitizeLimit(rawLimit) {
|
|
124
123
|
if (rawLimit === undefined || rawLimit === null)
|
|
@@ -165,7 +164,8 @@ function sanitizeDeep(deep, accountability) {
|
|
|
165
164
|
const parsedSubQuery = sanitizeQuery({ [key.substring(1)]: value }, accountability);
|
|
166
165
|
// ...however we want to keep them for the nested structure of deep, otherwise there's no
|
|
167
166
|
// way of knowing when to keep nesting and when to stop
|
|
168
|
-
|
|
167
|
+
const [parsedKey, parsedValue] = Object.entries(parsedSubQuery)[0];
|
|
168
|
+
parsedLevel[`_${parsedKey}`] = parsedValue;
|
|
169
169
|
}
|
|
170
170
|
else {
|
|
171
171
|
parse(value, [...path, key]);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.userName = void 0;
|
|
4
|
+
function userName(user) {
|
|
5
|
+
if (user.first_name && user.last_name) {
|
|
6
|
+
return `${user.first_name} ${user.last_name}`;
|
|
7
|
+
}
|
|
8
|
+
if (user.first_name) {
|
|
9
|
+
return user.first_name;
|
|
10
|
+
}
|
|
11
|
+
if (user.email) {
|
|
12
|
+
return user.email;
|
|
13
|
+
}
|
|
14
|
+
return 'Unknown User';
|
|
15
|
+
}
|
|
16
|
+
exports.userName = userName;
|