directus 9.0.0 → 9.1.2
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 +8 -4
- package/dist/auth/drivers/ldap.js +9 -13
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +3 -3
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +9 -3
- package/dist/controllers/notifications.d.ts +2 -0
- package/dist/controllers/notifications.js +147 -0
- package/dist/database/helpers/geometry.js +4 -1
- package/dist/database/migrations/20211103B-update-special-geometry.js +2 -2
- 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/settings.yaml +7 -7
- package/dist/database/system-data/fields/users.yaml +5 -0
- package/dist/database/system-data/fields/webhooks.yaml +4 -4
- package/dist/database/system-data/relations/relations.yaml +6 -0
- package/dist/mailer.js +12 -3
- package/dist/middleware/get-permissions.js +3 -102
- package/dist/server.js +2 -2
- package/dist/services/activity.d.ts +7 -5
- package/dist/services/activity.js +82 -3
- package/dist/services/authentication.js +15 -1
- 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/apply-query.js +10 -7
- package/dist/utils/apply-snapshot.js +2 -2
- 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 +27 -23
package/dist/app.js
CHANGED
|
@@ -40,6 +40,7 @@ const graphql_1 = __importDefault(require("./controllers/graphql"));
|
|
|
40
40
|
const items_1 = __importDefault(require("./controllers/items"));
|
|
41
41
|
const not_found_1 = __importDefault(require("./controllers/not-found"));
|
|
42
42
|
const panels_1 = __importDefault(require("./controllers/panels"));
|
|
43
|
+
const notifications_1 = __importDefault(require("./controllers/notifications"));
|
|
43
44
|
const permissions_1 = __importDefault(require("./controllers/permissions"));
|
|
44
45
|
const presets_1 = __importDefault(require("./controllers/presets"));
|
|
45
46
|
const relations_1 = __importDefault(require("./controllers/relations"));
|
|
@@ -132,11 +133,13 @@ async function createApp() {
|
|
|
132
133
|
// Set the App's base path according to the APIs public URL
|
|
133
134
|
const html = await fs_extra_1.default.readFile(adminPath, 'utf8');
|
|
134
135
|
const htmlWithBase = html.replace(/<base \/>/, `<base href="${adminUrl.toString({ rootRelative: true })}/" />`);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
app.use('/admin/*', (req, res) => {
|
|
136
|
+
const noCacheIndexHtmlHandler = (req, res) => {
|
|
137
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
138
138
|
res.send(htmlWithBase);
|
|
139
|
-
}
|
|
139
|
+
};
|
|
140
|
+
app.get('/admin', noCacheIndexHtmlHandler);
|
|
141
|
+
app.use('/admin', express_1.default.static(path_1.default.join(adminPath, '..')));
|
|
142
|
+
app.use('/admin/*', noCacheIndexHtmlHandler);
|
|
140
143
|
}
|
|
141
144
|
// use the rate limiter - all routes for now
|
|
142
145
|
if (env_1.default.RATE_LIMITER_ENABLED === true) {
|
|
@@ -161,6 +164,7 @@ async function createApp() {
|
|
|
161
164
|
app.use('/files', files_1.default);
|
|
162
165
|
app.use('/folders', folders_1.default);
|
|
163
166
|
app.use('/items', items_1.default);
|
|
167
|
+
app.use('/notifications', notifications_1.default);
|
|
164
168
|
app.use('/panels', panels_1.default);
|
|
165
169
|
app.use('/permissions', permissions_1.default);
|
|
166
170
|
app.use('/presets', presets_1.default);
|
|
@@ -91,21 +91,16 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
async fetchUserDn(identifier) {
|
|
94
|
-
const { userDn, userAttribute } = this.config;
|
|
94
|
+
const { userDn, userAttribute, userScope } = this.config;
|
|
95
95
|
return new Promise((resolve, reject) => {
|
|
96
96
|
// Search for the user in LDAP by attribute
|
|
97
|
-
this.bindClient.search(userDn, {
|
|
98
|
-
attributes: ['cn'],
|
|
99
|
-
filter: `(${userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn'}=${identifier})`,
|
|
100
|
-
scope: 'one',
|
|
101
|
-
}, (err, res) => {
|
|
97
|
+
this.bindClient.search(userDn, { filter: `(${userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn'}=${identifier})`, scope: userScope !== null && userScope !== void 0 ? userScope : 'one' }, (err, res) => {
|
|
102
98
|
if (err) {
|
|
103
99
|
reject(handleError(err));
|
|
104
100
|
return;
|
|
105
101
|
}
|
|
106
102
|
res.on('searchEntry', ({ object }) => {
|
|
107
|
-
|
|
108
|
-
resolve(`cn=${userCn},${userDn}`.toLowerCase());
|
|
103
|
+
resolve(object.dn.toLowerCase());
|
|
109
104
|
});
|
|
110
105
|
res.on('error', (err) => {
|
|
111
106
|
reject(handleError(err));
|
|
@@ -147,7 +142,7 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
147
142
|
});
|
|
148
143
|
}
|
|
149
144
|
async fetchUserGroups(userDn) {
|
|
150
|
-
const { groupDn, groupAttribute } = this.config;
|
|
145
|
+
const { groupDn, groupAttribute, groupScope } = this.config;
|
|
151
146
|
if (!groupDn) {
|
|
152
147
|
return Promise.resolve([]);
|
|
153
148
|
}
|
|
@@ -157,7 +152,7 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
157
152
|
this.bindClient.search(groupDn, {
|
|
158
153
|
attributes: ['cn'],
|
|
159
154
|
filter: `(${groupAttribute !== null && groupAttribute !== void 0 ? groupAttribute : 'member'}=${userDn})`,
|
|
160
|
-
scope: 'one',
|
|
155
|
+
scope: groupScope !== null && groupScope !== void 0 ? groupScope : 'one',
|
|
161
156
|
}, (err, res) => {
|
|
162
157
|
if (err) {
|
|
163
158
|
reject(handleError(err));
|
|
@@ -244,12 +239,13 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
244
239
|
reject(handleError(err));
|
|
245
240
|
});
|
|
246
241
|
client.bind(user.external_identifier, password, (err) => {
|
|
247
|
-
client.destroy();
|
|
248
242
|
if (err) {
|
|
249
243
|
reject(handleError(err));
|
|
250
|
-
return;
|
|
251
244
|
}
|
|
252
|
-
|
|
245
|
+
else {
|
|
246
|
+
resolve();
|
|
247
|
+
}
|
|
248
|
+
client.destroy();
|
|
253
249
|
});
|
|
254
250
|
});
|
|
255
251
|
}
|
|
@@ -13,7 +13,7 @@ export declare class OAuth2AuthDriver extends LocalAuthDriver {
|
|
|
13
13
|
generateAuthUrl(codeVerifier: string): string;
|
|
14
14
|
private fetchUserId;
|
|
15
15
|
getUserID(payload: Record<string, any>): Promise<string>;
|
|
16
|
-
login(user: User
|
|
16
|
+
login(user: User): Promise<SessionData>;
|
|
17
17
|
refresh(user: User, sessionData: SessionData): Promise<SessionData>;
|
|
18
18
|
}
|
|
19
19
|
export declare function createOAuth2AuthRouter(providerName: string): Router;
|
|
@@ -80,7 +80,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
80
80
|
tokenSet = await this.client.oauthCallback(this.redirectUrl, { code: payload.code, state: payload.state }, { code_verifier: payload.codeVerifier, state: openid_client_1.generators.codeChallenge(payload.codeVerifier) });
|
|
81
81
|
const issuer = this.client.issuer;
|
|
82
82
|
if (issuer.metadata.userinfo_endpoint) {
|
|
83
|
-
userInfo = await this.client.userinfo(tokenSet);
|
|
83
|
+
userInfo = await this.client.userinfo(tokenSet.access_token);
|
|
84
84
|
}
|
|
85
85
|
else if (tokenSet.id_token) {
|
|
86
86
|
userInfo = tokenSet.claims();
|
|
@@ -123,8 +123,8 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
123
123
|
});
|
|
124
124
|
return (await this.fetchUserId(identifier));
|
|
125
125
|
}
|
|
126
|
-
async login(user
|
|
127
|
-
return this.refresh(user,
|
|
126
|
+
async login(user) {
|
|
127
|
+
return this.refresh(user, null);
|
|
128
128
|
}
|
|
129
129
|
async refresh(user, sessionData) {
|
|
130
130
|
let authData = user.auth_data;
|
|
@@ -13,7 +13,7 @@ export declare class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
13
13
|
generateAuthUrl(codeVerifier: string): Promise<string>;
|
|
14
14
|
private fetchUserId;
|
|
15
15
|
getUserID(payload: Record<string, any>): Promise<string>;
|
|
16
|
-
login(user: User
|
|
16
|
+
login(user: User): Promise<SessionData>;
|
|
17
17
|
refresh(user: User, sessionData: SessionData): Promise<SessionData>;
|
|
18
18
|
}
|
|
19
19
|
export declare function createOpenIDAuthRouter(providerName: string): Router;
|
|
@@ -31,6 +31,12 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
31
31
|
this.client = new Promise((resolve, reject) => {
|
|
32
32
|
openid_client_1.Issuer.discover(issuerUrl)
|
|
33
33
|
.then((issuer) => {
|
|
34
|
+
const supportedTypes = issuer.metadata.response_types_supported;
|
|
35
|
+
if (!(supportedTypes === null || supportedTypes === void 0 ? void 0 : supportedTypes.includes('code'))) {
|
|
36
|
+
reject(new exceptions_1.InvalidConfigException('OpenID provider does not support required code flow', {
|
|
37
|
+
provider: additionalConfig.provider,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
34
40
|
resolve(new issuer.Client({
|
|
35
41
|
client_id: clientId,
|
|
36
42
|
client_secret: clientSecret,
|
|
@@ -82,7 +88,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
82
88
|
tokenSet = await client.callback(this.redirectUrl, { code: payload.code, state: payload.state }, { code_verifier: payload.codeVerifier, state: openid_client_1.generators.codeChallenge(payload.codeVerifier) });
|
|
83
89
|
const issuer = client.issuer;
|
|
84
90
|
if (issuer.metadata.userinfo_endpoint) {
|
|
85
|
-
userInfo = await client.userinfo(tokenSet);
|
|
91
|
+
userInfo = await client.userinfo(tokenSet.access_token);
|
|
86
92
|
}
|
|
87
93
|
else {
|
|
88
94
|
userInfo = tokenSet.claims();
|
|
@@ -125,8 +131,8 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
125
131
|
});
|
|
126
132
|
return (await this.fetchUserId(identifier));
|
|
127
133
|
}
|
|
128
|
-
async login(user
|
|
129
|
-
return this.refresh(user,
|
|
134
|
+
async login(user) {
|
|
135
|
+
return this.refresh(user, null);
|
|
130
136
|
}
|
|
131
137
|
async refresh(user, sessionData) {
|
|
132
138
|
let authData = user.auth_data;
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const exceptions_1 = require("../exceptions");
|
|
8
|
+
const respond_1 = require("../middleware/respond");
|
|
9
|
+
const use_collection_1 = __importDefault(require("../middleware/use-collection"));
|
|
10
|
+
const validate_batch_1 = require("../middleware/validate-batch");
|
|
11
|
+
const services_1 = require("../services");
|
|
12
|
+
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
13
|
+
const router = express_1.default.Router();
|
|
14
|
+
router.use((0, use_collection_1.default)('directus_notifications'));
|
|
15
|
+
router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
16
|
+
const service = new services_1.NotificationsService({
|
|
17
|
+
accountability: req.accountability,
|
|
18
|
+
schema: req.schema,
|
|
19
|
+
});
|
|
20
|
+
const savedKeys = [];
|
|
21
|
+
if (Array.isArray(req.body)) {
|
|
22
|
+
const keys = await service.createMany(req.body);
|
|
23
|
+
savedKeys.push(...keys);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const key = await service.createOne(req.body);
|
|
27
|
+
savedKeys.push(key);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
if (Array.isArray(req.body)) {
|
|
31
|
+
const records = await service.readMany(savedKeys, req.sanitizedQuery);
|
|
32
|
+
res.locals.payload = { data: records };
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const record = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
|
36
|
+
res.locals.payload = { data: record };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error instanceof exceptions_1.ForbiddenException) {
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
return next();
|
|
46
|
+
}), respond_1.respond);
|
|
47
|
+
const readHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
48
|
+
const service = new services_1.NotificationsService({
|
|
49
|
+
accountability: req.accountability,
|
|
50
|
+
schema: req.schema,
|
|
51
|
+
});
|
|
52
|
+
const metaService = new services_1.MetaService({
|
|
53
|
+
accountability: req.accountability,
|
|
54
|
+
schema: req.schema,
|
|
55
|
+
});
|
|
56
|
+
let result;
|
|
57
|
+
if (req.singleton) {
|
|
58
|
+
result = await service.readSingleton(req.sanitizedQuery);
|
|
59
|
+
}
|
|
60
|
+
else if (req.body.keys) {
|
|
61
|
+
result = await service.readMany(req.body.keys, req.sanitizedQuery);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
result = await service.readByQuery(req.sanitizedQuery);
|
|
65
|
+
}
|
|
66
|
+
const meta = await metaService.getMetaForQuery('directus_presets', req.sanitizedQuery);
|
|
67
|
+
res.locals.payload = { data: result, meta };
|
|
68
|
+
return next();
|
|
69
|
+
});
|
|
70
|
+
router.get('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
71
|
+
router.search('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
72
|
+
router.get('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
73
|
+
const service = new services_1.NotificationsService({
|
|
74
|
+
accountability: req.accountability,
|
|
75
|
+
schema: req.schema,
|
|
76
|
+
});
|
|
77
|
+
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
|
78
|
+
res.locals.payload = { data: record || null };
|
|
79
|
+
return next();
|
|
80
|
+
}), respond_1.respond);
|
|
81
|
+
router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handler_1.default)(async (req, res, next) => {
|
|
82
|
+
const service = new services_1.NotificationsService({
|
|
83
|
+
accountability: req.accountability,
|
|
84
|
+
schema: req.schema,
|
|
85
|
+
});
|
|
86
|
+
let keys = [];
|
|
87
|
+
if (req.body.keys) {
|
|
88
|
+
keys = await service.updateMany(req.body.keys, req.body.data);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
keys = await service.updateByQuery(req.body.query, req.body.data);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const result = await service.readMany(keys, req.sanitizedQuery);
|
|
95
|
+
res.locals.payload = { data: result };
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof exceptions_1.ForbiddenException) {
|
|
99
|
+
return next();
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
return next();
|
|
104
|
+
}), respond_1.respond);
|
|
105
|
+
router.patch('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
106
|
+
const service = new services_1.NotificationsService({
|
|
107
|
+
accountability: req.accountability,
|
|
108
|
+
schema: req.schema,
|
|
109
|
+
});
|
|
110
|
+
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
|
111
|
+
try {
|
|
112
|
+
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
113
|
+
res.locals.payload = { data: record };
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error instanceof exceptions_1.ForbiddenException) {
|
|
117
|
+
return next();
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
return next();
|
|
122
|
+
}), respond_1.respond);
|
|
123
|
+
router.delete('/', (0, validate_batch_1.validateBatch)('delete'), (0, async_handler_1.default)(async (req, res, next) => {
|
|
124
|
+
const service = new services_1.NotificationsService({
|
|
125
|
+
accountability: req.accountability,
|
|
126
|
+
schema: req.schema,
|
|
127
|
+
});
|
|
128
|
+
if (Array.isArray(req.body)) {
|
|
129
|
+
await service.deleteMany(req.body);
|
|
130
|
+
}
|
|
131
|
+
else if (req.body.keys) {
|
|
132
|
+
await service.deleteMany(req.body.keys);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
await service.deleteByQuery(req.body.query);
|
|
136
|
+
}
|
|
137
|
+
return next();
|
|
138
|
+
}), respond_1.respond);
|
|
139
|
+
router.delete('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
140
|
+
const service = new services_1.NotificationsService({
|
|
141
|
+
accountability: req.accountability,
|
|
142
|
+
schema: req.schema,
|
|
143
|
+
});
|
|
144
|
+
await service.deleteOne(req.params.pk);
|
|
145
|
+
return next();
|
|
146
|
+
}), respond_1.respond);
|
|
147
|
+
exports.default = router;
|
|
@@ -99,7 +99,7 @@ class KnexSpatial_PG extends KnexSpatial {
|
|
|
99
99
|
createColumn(table, field) {
|
|
100
100
|
var _a;
|
|
101
101
|
const type = (_a = field.type.split('.')[1]) !== null && _a !== void 0 ? _a : 'geometry';
|
|
102
|
-
return table.specificType(field.field, `geometry(${type})`);
|
|
102
|
+
return table.specificType(field.field, `geometry(${type}, 4326)`);
|
|
103
103
|
}
|
|
104
104
|
_intersects_bbox(key, geojson) {
|
|
105
105
|
const geometry = this.fromGeoJSON(geojson);
|
|
@@ -110,6 +110,9 @@ class KnexSpatial_MySQL extends KnexSpatial {
|
|
|
110
110
|
collect(table, column) {
|
|
111
111
|
return this.knex.raw(`concat('geometrycollection(', group_concat(? separator ', '), ')'`, this.asText(table, column));
|
|
112
112
|
}
|
|
113
|
+
fromText(text) {
|
|
114
|
+
return this.knex.raw('st_geomfromtext(?)', text);
|
|
115
|
+
}
|
|
113
116
|
}
|
|
114
117
|
class KnexSpatial_Redshift extends KnexSpatial {
|
|
115
118
|
createColumn(table, field) {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.down = exports.up = void 0;
|
|
4
4
|
async function up(knex) {
|
|
5
5
|
await knex('directus_fields')
|
|
6
|
-
.update({ special: knex.raw(`REPLACE(
|
|
6
|
+
.update({ special: knex.raw(`REPLACE(??, 'geometry,', 'geometry.')`, ['special']) })
|
|
7
7
|
.where('special', 'like', '%geometry,Point%')
|
|
8
8
|
.orWhere('special', 'like', '%geometry,LineString%')
|
|
9
9
|
.orWhere('special', 'like', '%geometry,Polygon%')
|
|
@@ -14,7 +14,7 @@ async function up(knex) {
|
|
|
14
14
|
exports.up = up;
|
|
15
15
|
async function down(knex) {
|
|
16
16
|
await knex('directus_fields')
|
|
17
|
-
.update({ special: knex.raw(`REPLACE(
|
|
17
|
+
.update({ special: knex.raw(`REPLACE(??, 'geometry.', 'geometry,')`, ['special']) })
|
|
18
18
|
.where('special', 'like', '%geometry.Point%')
|
|
19
19
|
.orWhere('special', 'like', '%geometry.LineString%')
|
|
20
20
|
.orWhere('special', 'like', '%geometry.Polygon%')
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.down = exports.up = void 0;
|
|
4
|
+
async function up(knex) {
|
|
5
|
+
await knex.schema.createTable('directus_notifications', (table) => {
|
|
6
|
+
table.increments();
|
|
7
|
+
table.timestamp('timestamp').notNullable();
|
|
8
|
+
table.string('status').defaultTo('inbox');
|
|
9
|
+
table.uuid('recipient').notNullable().references('id').inTable('directus_users').onDelete('CASCADE');
|
|
10
|
+
table.uuid('sender').notNullable().references('id').inTable('directus_users');
|
|
11
|
+
table.string('subject').notNullable();
|
|
12
|
+
table.text('message');
|
|
13
|
+
table.string('collection', 64);
|
|
14
|
+
table.string('item');
|
|
15
|
+
});
|
|
16
|
+
await knex.schema.alterTable('directus_users', (table) => {
|
|
17
|
+
table.boolean('email_notifications').defaultTo(true);
|
|
18
|
+
});
|
|
19
|
+
await knex('directus_users').update({ email_notifications: true });
|
|
20
|
+
}
|
|
21
|
+
exports.up = up;
|
|
22
|
+
async function down(knex) {
|
|
23
|
+
await knex.schema.dropTable('directus_notifications');
|
|
24
|
+
await knex.schema.alterTable('directus_users', (table) => {
|
|
25
|
+
table.dropColumn('email_notifications');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
exports.down = down;
|
|
@@ -72,6 +72,20 @@
|
|
|
72
72
|
- collection: directus_settings
|
|
73
73
|
action: read
|
|
74
74
|
|
|
75
|
+
- collection: directus_notifications
|
|
76
|
+
action: read
|
|
77
|
+
permissions:
|
|
78
|
+
recipient:
|
|
79
|
+
_eq: $CURRENT_USER
|
|
80
|
+
fields: '*'
|
|
81
|
+
|
|
82
|
+
- collection: directus_notifications
|
|
83
|
+
action: update
|
|
84
|
+
permissions:
|
|
85
|
+
recipient:
|
|
86
|
+
_eq: $CURRENT_USER
|
|
87
|
+
fields: 'status'
|
|
88
|
+
|
|
75
89
|
- collection: directus_users
|
|
76
90
|
action: read
|
|
77
91
|
permissions:
|
|
@@ -36,7 +36,7 @@ fields:
|
|
|
36
36
|
|
|
37
37
|
- field: project_color
|
|
38
38
|
interface: select-color
|
|
39
|
-
note: $t:field_options.directus_settings.
|
|
39
|
+
note: $t:field_options.directus_settings.project_color_note
|
|
40
40
|
translations:
|
|
41
41
|
language: en-US
|
|
42
42
|
translations: Brand Color
|
|
@@ -44,7 +44,7 @@ fields:
|
|
|
44
44
|
|
|
45
45
|
- field: project_logo
|
|
46
46
|
interface: file
|
|
47
|
-
note:
|
|
47
|
+
note: $t:field_options.directus_settings.project_logo_note
|
|
48
48
|
translations:
|
|
49
49
|
language: en-US
|
|
50
50
|
translations: Brand Logo
|
|
@@ -77,7 +77,7 @@ fields:
|
|
|
77
77
|
language: css
|
|
78
78
|
lineNumber: true
|
|
79
79
|
template: |
|
|
80
|
-
#app {
|
|
80
|
+
#app, #main-content {
|
|
81
81
|
--border-radius-outline: 0px !important;
|
|
82
82
|
--border-radius: 0px !important;
|
|
83
83
|
}
|
|
@@ -170,7 +170,7 @@ fields:
|
|
|
170
170
|
onlyOnCreate: false
|
|
171
171
|
width: full
|
|
172
172
|
- field: fit
|
|
173
|
-
name:
|
|
173
|
+
name: $t:field_options.directus_settings.storage_asset_presets.fit_label
|
|
174
174
|
type: string
|
|
175
175
|
schema:
|
|
176
176
|
is_nullable: false
|
|
@@ -217,7 +217,7 @@ fields:
|
|
|
217
217
|
step: 1
|
|
218
218
|
width: half
|
|
219
219
|
- field: withoutEnlargement
|
|
220
|
-
name:
|
|
220
|
+
name: $t:field_options.directus_settings.storage_asset_presets.upscaling
|
|
221
221
|
type: boolean
|
|
222
222
|
schema:
|
|
223
223
|
default_value: false
|
|
@@ -227,7 +227,7 @@ fields:
|
|
|
227
227
|
options:
|
|
228
228
|
label: $t:no_upscale
|
|
229
229
|
- field: format
|
|
230
|
-
name:
|
|
230
|
+
name: $t:format
|
|
231
231
|
type: string
|
|
232
232
|
schema:
|
|
233
233
|
is_nullable: false
|
|
@@ -307,7 +307,7 @@ fields:
|
|
|
307
307
|
meta:
|
|
308
308
|
interface: text-input
|
|
309
309
|
options:
|
|
310
|
-
placeholder:
|
|
310
|
+
placeholder: $t:field_options.directus_settings.basemaps_name_placeholder
|
|
311
311
|
- field: type
|
|
312
312
|
name: $t:type
|
|
313
313
|
meta:
|
|
@@ -40,19 +40,19 @@ fields:
|
|
|
40
40
|
display_options:
|
|
41
41
|
showAsDot: true
|
|
42
42
|
choices:
|
|
43
|
-
- text: $t:
|
|
43
|
+
- text: $t:field_options.directus_webhooks.status_options_active
|
|
44
44
|
value: active
|
|
45
45
|
foreground: 'var(--primary-10)'
|
|
46
46
|
background: 'var(--primary)'
|
|
47
|
-
- text: $t:
|
|
47
|
+
- text: $t:field_options.directus_webhooks.status_options_inactive
|
|
48
48
|
value: inactive
|
|
49
49
|
foreground: 'var(--foreground-normal)'
|
|
50
50
|
background: 'var(--background-normal-alt)'
|
|
51
51
|
options:
|
|
52
52
|
choices:
|
|
53
|
-
- text: $t:
|
|
53
|
+
- text: $t:field_options.directus_webhooks.status_options_active
|
|
54
54
|
value: active
|
|
55
|
-
- text: $t:
|
|
55
|
+
- text: $t:field_options.directus_webhooks.status_options_inactive
|
|
56
56
|
value: inactive
|
|
57
57
|
width: half
|
|
58
58
|
|
|
@@ -82,3 +82,9 @@ data:
|
|
|
82
82
|
- many_collection: directus_panels
|
|
83
83
|
many_field: user_created
|
|
84
84
|
one_collection: directus_users
|
|
85
|
+
- many_collection: directus_notifications
|
|
86
|
+
many_field: recipient
|
|
87
|
+
one_collection: directus_users
|
|
88
|
+
- many_collection: directus_notifications
|
|
89
|
+
many_field: sender
|
|
90
|
+
one_collection: directus_users
|
package/dist/mailer.js
CHANGED
|
@@ -11,14 +11,23 @@ let transporter;
|
|
|
11
11
|
function getMailer() {
|
|
12
12
|
if (transporter)
|
|
13
13
|
return transporter;
|
|
14
|
-
|
|
14
|
+
const transportName = env_1.default.EMAIL_TRANSPORT.toLowerCase();
|
|
15
|
+
if (transportName === 'sendmail') {
|
|
15
16
|
transporter = nodemailer_1.default.createTransport({
|
|
16
17
|
sendmail: true,
|
|
17
18
|
newline: env_1.default.EMAIL_SENDMAIL_NEW_LINE || 'unix',
|
|
18
19
|
path: env_1.default.EMAIL_SENDMAIL_PATH || '/usr/sbin/sendmail',
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
|
-
else if (
|
|
22
|
+
else if (transportName === 'ses') {
|
|
23
|
+
const aws = require('@aws-sdk/client-ses');
|
|
24
|
+
const sesOptions = (0, get_config_from_env_1.getConfigFromEnv)('EMAIL_SES_');
|
|
25
|
+
const ses = new aws.SES(sesOptions);
|
|
26
|
+
transporter = nodemailer_1.default.createTransport({
|
|
27
|
+
SES: { ses, aws },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else if (transportName === 'smtp') {
|
|
22
31
|
let auth = false;
|
|
23
32
|
if (env_1.default.EMAIL_SMTP_USER || env_1.default.EMAIL_SMTP_PASSWORD) {
|
|
24
33
|
auth = {
|
|
@@ -37,7 +46,7 @@ function getMailer() {
|
|
|
37
46
|
tls,
|
|
38
47
|
});
|
|
39
48
|
}
|
|
40
|
-
else if (
|
|
49
|
+
else if (transportName === 'mailgun') {
|
|
41
50
|
const mg = require('nodemailer-mailgun-transport');
|
|
42
51
|
transporter = nodemailer_1.default.createTransport(mg({
|
|
43
52
|
auth: {
|