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.
Files changed (39) hide show
  1. package/dist/app.js +2 -0
  2. package/dist/auth/drivers/ldap.js +9 -13
  3. package/dist/auth/drivers/oauth2.d.ts +1 -1
  4. package/dist/auth/drivers/oauth2.js +2 -2
  5. package/dist/auth/drivers/openid.d.ts +1 -1
  6. package/dist/auth/drivers/openid.js +8 -2
  7. package/dist/controllers/notifications.d.ts +2 -0
  8. package/dist/controllers/notifications.js +147 -0
  9. package/dist/database/migrations/20211118A-add-notifications.d.ts +3 -0
  10. package/dist/database/migrations/20211118A-add-notifications.js +28 -0
  11. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +14 -0
  12. package/dist/database/system-data/collections/collections.yaml +2 -0
  13. package/dist/database/system-data/fields/notifications.yaml +12 -0
  14. package/dist/database/system-data/fields/users.yaml +5 -0
  15. package/dist/database/system-data/relations/relations.yaml +6 -0
  16. package/dist/middleware/get-permissions.js +3 -102
  17. package/dist/services/activity.d.ts +7 -5
  18. package/dist/services/activity.js +82 -3
  19. package/dist/services/collections.js +12 -1
  20. package/dist/services/graphql.js +3 -0
  21. package/dist/services/index.d.ts +1 -0
  22. package/dist/services/index.js +1 -0
  23. package/dist/services/mail/index.js +2 -2
  24. package/dist/services/mail/templates/base.liquid +153 -85
  25. package/dist/services/mail/templates/password-reset.liquid +3 -2
  26. package/dist/services/mail/templates/user-invitation.liquid +4 -4
  27. package/dist/services/notifications.d.ts +12 -0
  28. package/dist/services/notifications.js +41 -0
  29. package/dist/services/users.js +1 -0
  30. package/dist/types/collection.d.ts +1 -0
  31. package/dist/utils/get-local-type.js +12 -7
  32. package/dist/utils/get-permissions.d.ts +3 -0
  33. package/dist/utils/get-permissions.js +106 -0
  34. package/dist/utils/md.d.ts +4 -0
  35. package/dist/utils/md.js +15 -0
  36. package/dist/utils/sanitize-query.js +3 -3
  37. package/dist/utils/user-name.d.ts +2 -0
  38. package/dist/utils/user-name.js +16 -0
  39. package/package.json +26 -23
@@ -11,6 +11,7 @@ export * from './graphql';
11
11
  export * from './import';
12
12
  export * from './mail';
13
13
  export * from './meta';
14
+ export * from './notifications';
14
15
  export * from './panels';
15
16
  export * from './payload';
16
17
  export * from './permissions';
@@ -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 from = options.from || env_1.default.EMAIL_FROM;
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
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2
- <html xmlns="http://www.w3.org/1999/xhtml">
3
-
1
+ <!doctype html>
2
+ <html lang="en">
4
3
  <head>
5
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6
- <meta name="viewport" content="width=device-width">
7
- <meta name="format-detection" content="telephone=no">
8
-
9
- <title>{{ projectName }} Email Service</title>
10
-
11
- <style type="text/css">
12
- body {
13
- margin: 0;
14
- padding: 20px;
15
- background-color: #eceff1;
16
- -webkit-font-smoothing: antialiased;
17
- -webkit-text-size-adjust: none;
18
- -ms-text-size-adjust: none;
19
- font-weight: 400;
20
- font-size: 14px;
21
- color: #546e7a;
22
- font-family: 'Roboto', Helvetica, Helvetica, Arial, sans-serif;
23
- letter-spacing: 0;
24
- }
25
-
26
- a {
27
- display: inline-block;
28
- height: 52px;
29
- width: auto;
30
- min-width: 154px;
31
- padding: 0 20px;
32
- font-size: 16px;
33
- font-weight: bold;
34
- line-height: 52px;
35
- cursor: pointer;
36
- border-radius: 4px;
37
- text-decoration: none !important;
38
- color: white !important;
39
- background-color: {{ projectColor }};
40
- }
41
-
42
- a:hover {
43
- filter: brightness(105%);
44
- }
45
-
46
- p {
47
- margin: 20px 0 20px 0;
48
- }
49
- </style>
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
- <body>
53
- <table border="0" cellpadding="0" cellspacing="0" width="100%">
54
- <tr>
55
- <td style="padding: 0;">
56
- <table align="center" border="0" cellpadding="0" cellspacing="0" width="600"
57
- style="border: 0 solid #263238; border-collapse: collapse;">
58
- <tr>
59
- <td align="center" bgcolor="{{ projectColor }}"
60
- style="padding: 30px 0 30px 0; border-radius: 4px 4px 0 0;">
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;"> &nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌</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
- <td bgcolor="#ffffff" style="padding: 30px; border-radius: 0 0 4px 4px;">
67
- <table border="0" cellpadding="0" cellspacing="0" width="100%">
68
- <tr>
69
- <td
70
- style="color: #546e7a; font-family: Helvetica, Arial, sans-serif; padding: 0; line-height: 1.5; font-size: 16px;">
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
- </td>
73
- </tr>
74
- </table>
75
- </td>
76
- </tr>
77
- <tr>
78
- <td bgcolor="#eceff1" style="padding: 20px 30px 0 32px;">
79
- <table border="0" cellpadding="0" cellspacing="0" width="100%">
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
- </td>
85
- </tr>
86
- </table>
87
- </td>
148
+ </p>
149
+ </td>
150
+ </tr>
151
+ </tbody>
152
+ </table>
153
+ <!--[if (gte mso 9)|(IE)]>
154
+ </td>
88
155
  </tr>
89
- </table>
90
- </td>
91
- </tr>
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
- Click to reset your password
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
- Hello. You have been invited to join {{ projectName }}. Please click the button below to accept this invitation and join the project:
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 You,<br>
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;
@@ -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(0)': 'boolean',
57
- 'tinyint(1)': 'boolean',
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
- const type = column ? localTypeMap[column.data_type.toLowerCase().split('(')[0]] : 'alias';
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 (column && column.data_type === 'numeric' && column.numeric_precision !== null && column.numeric_scale !== null) {
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 (column && column.data_type === 'nvarchar' && column.max_length === -1) {
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 (column && databaseClient === 'mysql' && column.data_type.toLowerCase() === 'tinyint') {
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,3 @@
1
+ import { Accountability } from '@directus/shared/types';
2
+ import { SchemaOverview } from '../types';
3
+ export declare function getPermissions(accountability: Accountability, schema: SchemaOverview): Promise<any>;
@@ -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;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Render and sanitize a markdown string
3
+ */
4
+ export declare function md(str: string): string;
@@ -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
- filters = (0, utils_1.parseFilter)(filters, accountability);
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
- parsedLevel[key] = Object.values(parsedSubQuery)[0];
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,2 @@
1
+ import { User } from '@directus/shared/types';
2
+ export declare function userName(user: Partial<User>): string;
@@ -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;