Compare commits
93 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8e45a42a6b | ||
![]() |
9a7cdf42e1 | ||
![]() |
c36b652d5b | ||
![]() |
553070fc23 | ||
![]() |
5d69f7e24f | ||
![]() |
bc0e2bada0 | ||
![]() |
80b6cc1d94 | ||
![]() |
bce3273e64 | ||
![]() |
3abf61152a | ||
![]() |
14923d4cd6 | ||
![]() |
6fdc4bf900 | ||
![]() |
d21e1f75b5 | ||
![]() |
84a0b37fcc | ||
![]() |
f135a0f09e | ||
![]() |
0f24c99456 | ||
![]() |
9eae0ab947 | ||
![]() |
3bf1f79c79 | ||
![]() |
b21074c871 | ||
![]() |
d7893d9a32 | ||
![]() |
9cbdda330c | ||
![]() |
42a9bfd099 | ||
![]() |
eb15bd01ca | ||
![]() |
9e98aebeb3 | ||
![]() |
1361cbc826 | ||
![]() |
679d0808a9 | ||
![]() |
6fe9a548ad | ||
![]() |
2d6d2430d2 | ||
![]() |
a445538e81 | ||
![]() |
50d38ffbd8 | ||
![]() |
93bcdfd9c9 | ||
![]() |
5be3b101a5 | ||
![]() |
024c7476c7 | ||
![]() |
30a7ffe93d | ||
![]() |
e2d803ebf7 | ||
![]() |
be7e67c940 | ||
![]() |
ead4b13ba5 | ||
![]() |
e02c42ee18 | ||
![]() |
d39886fdf8 | ||
![]() |
11a425f1de | ||
![]() |
f0e194e584 | ||
![]() |
d4b9331cf2 | ||
![]() |
37e1acc5f1 | ||
![]() |
ffaf6a577d | ||
![]() |
afdaf6ba39 | ||
![]() |
4c49367910 | ||
![]() |
a506c4411d | ||
![]() |
1859c9854e | ||
![]() |
6ff29b9ae6 | ||
![]() |
3578f6b849 | ||
![]() |
0347864fde | ||
![]() |
5f9786a2c7 | ||
![]() |
75aeff1898 | ||
![]() |
0afcdce6d3 | ||
![]() |
a591d0ea87 | ||
![]() |
0e111a3532 | ||
![]() |
b599466ffa | ||
![]() |
69727e78df | ||
![]() |
02ae67b147 | ||
![]() |
a769f78801 | ||
![]() |
d583e42428 | ||
![]() |
da732becb6 | ||
![]() |
b89a4d58d9 | ||
![]() |
09854147d1 | ||
![]() |
3648c2bfe3 | ||
![]() |
3f3ee032f6 | ||
![]() |
68e5d54331 | ||
![]() |
824c434b0b | ||
![]() |
9f0e0ca656 | ||
![]() |
95f89ba03e | ||
![]() |
697f72ecf4 | ||
![]() |
4f03f2ab51 | ||
![]() |
c81531cb7a | ||
![]() |
7b6e4aa153 | ||
![]() |
f21039d19d | ||
![]() |
8c936a91be | ||
![]() |
24451892ff | ||
![]() |
6bba2c82fe | ||
![]() |
3320dc6bc4 | ||
![]() |
9d42fd9293 | ||
![]() |
e6b806616f | ||
![]() |
6ec5872391 | ||
![]() |
a26cf932a1 | ||
![]() |
38a3e3ab9f | ||
![]() |
32b17c1418 | ||
![]() |
44aa6a1579 | ||
![]() |
2369aacd2a | ||
![]() |
7dafc6364b | ||
![]() |
3d25fa0aeb | ||
![]() |
0297b0f296 | ||
![]() |
4c7d09c3d8 | ||
![]() |
48a74826e8 | ||
![]() |
ef34068ac4 | ||
![]() |
3987a8db77 |
@@ -8,7 +8,7 @@
|
|||||||
"version": "latest"
|
"version": "latest"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": 16
|
"version": 18
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/common-utils:1": {
|
"ghcr.io/devcontainers/features/common-utils:1": {
|
||||||
"username": "vscode",
|
"username": "vscode",
|
||||||
|
@@ -33,7 +33,6 @@
|
|||||||
"axios": "1.6.0",
|
"axios": "1.6.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bullmq": "^3.0.0",
|
"bullmq": "^3.0.0",
|
||||||
"copyfiles": "^2.4.1",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
@@ -45,7 +44,6 @@
|
|||||||
"graphql-middleware": "^6.1.15",
|
"graphql-middleware": "^6.1.15",
|
||||||
"graphql-shield": "^7.5.0",
|
"graphql-shield": "^7.5.0",
|
||||||
"graphql-tools": "^8.2.0",
|
"graphql-tools": "^8.2.0",
|
||||||
"graphql-type-json": "^0.3.2",
|
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"http-errors": "~1.6.3",
|
"http-errors": "~1.6.3",
|
||||||
"http-proxy-agent": "^7.0.0",
|
"http-proxy-agent": "^7.0.0",
|
||||||
@@ -68,7 +66,6 @@
|
|||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"raw-body": "^2.5.2",
|
"raw-body": "^2.5.2",
|
||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"stripe": "^11.13.0",
|
|
||||||
"winston": "^3.7.1",
|
"winston": "^3.7.1",
|
||||||
"xmlrpc": "^1.3.2"
|
"xmlrpc": "^1.3.2"
|
||||||
},
|
},
|
||||||
|
@@ -0,0 +1,120 @@
|
|||||||
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Change a scheduled event',
|
||||||
|
key: 'changeScheduledEvent',
|
||||||
|
description: 'Changes a scheduled event',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Scheduled Event',
|
||||||
|
key: 'scheduledEventId',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: false,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listScheduledEvents',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Status',
|
||||||
|
key: 'status',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'After the status has been changed to COMPLETED or CANCELED, it becomes immutable and cannot be modified further.',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'SCHEDULED', value: 1 },
|
||||||
|
{ label: 'ACTIVE', value: 2 },
|
||||||
|
{ label: 'COMPLETED', value: 3 },
|
||||||
|
{ label: 'CANCELED', value: 4 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Type',
|
||||||
|
key: 'entityType',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{ label: 'Stage channel', value: 1 },
|
||||||
|
{ label: 'Voice channel', value: 2 },
|
||||||
|
{ label: 'External', value: 3 },
|
||||||
|
],
|
||||||
|
additionalFields: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicFields',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listScheduledEventFieldsForChange',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parameters.entityType',
|
||||||
|
value: '{parameters.entityType}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Name',
|
||||||
|
key: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Description',
|
||||||
|
key: 'description',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Image',
|
||||||
|
key: 'image',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'Image as DataURI scheme [data:image/<jpeg/png/gif>;base64,BASE64_ENCODED_<JPEG/PNG/GIF>_IMAGE_DATA]',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const data = {
|
||||||
|
channel_id: $.step.parameters.channel_id,
|
||||||
|
name: $.step.parameters.name,
|
||||||
|
scheduled_start_time: $.step.parameters.scheduledStartTime,
|
||||||
|
scheduled_end_time: $.step.parameters.scheduledEndTime,
|
||||||
|
description: $.step.parameters.description,
|
||||||
|
entity_type: $.step.parameters.entityType,
|
||||||
|
image: $.step.parameters.image,
|
||||||
|
};
|
||||||
|
|
||||||
|
const isExternal = $.step.parameters.entityType === 3;
|
||||||
|
|
||||||
|
if (isExternal) {
|
||||||
|
data.entity_metadata = {
|
||||||
|
location: $.step.parameters.location,
|
||||||
|
};
|
||||||
|
|
||||||
|
data.channel_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await $.http?.patch(
|
||||||
|
`/guilds/${$.auth.data.guildId}/scheduled-events/${$.step.parameters.scheduledEventId}`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({ raw: response.data });
|
||||||
|
},
|
||||||
|
});
|
@@ -1,4 +1,9 @@
|
|||||||
|
import changeScheduledEvent from './change-scheduled-event/index.js';
|
||||||
import sendMessageToChannel from './send-message-to-channel/index.js';
|
import sendMessageToChannel from './send-message-to-channel/index.js';
|
||||||
import createScheduledEvent from './create-scheduled-event/index.js';
|
import createScheduledEvent from './create-scheduled-event/index.js';
|
||||||
|
|
||||||
export default [sendMessageToChannel, createScheduledEvent];
|
export default [
|
||||||
|
changeScheduledEvent,
|
||||||
|
sendMessageToChannel,
|
||||||
|
createScheduledEvent,
|
||||||
|
];
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import listChannels from './list-channels/index.js';
|
import listChannels from './list-channels/index.js';
|
||||||
|
import listScheduledEvents from './list-scheduled-events/index.js';
|
||||||
import listVoiceChannels from './list-voice-channels/index.js';
|
import listVoiceChannels from './list-voice-channels/index.js';
|
||||||
|
|
||||||
export default [listChannels, listVoiceChannels];
|
export default [listChannels, listScheduledEvents, listVoiceChannels];
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
name: 'List scheduled events',
|
||||||
|
key: 'listScheduledEvents',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const scheduledEvents = {
|
||||||
|
data: [],
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await $.http.get(
|
||||||
|
`/guilds/${$.auth.data.guildId}/scheduled-events`
|
||||||
|
);
|
||||||
|
|
||||||
|
scheduledEvents.data = response.data.map((scheduledEvent) => {
|
||||||
|
return {
|
||||||
|
value: scheduledEvent.id,
|
||||||
|
name: scheduledEvent.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return scheduledEvents;
|
||||||
|
},
|
||||||
|
};
|
@@ -1,3 +1,7 @@
|
|||||||
import listExternalScheduledEventFields from './list-external-scheduled-event-fields/index.js';
|
import listExternalScheduledEventFields from './list-external-scheduled-event-fields/index.js';
|
||||||
|
import listScheduledEventFieldsForChange from './list-scheduled-event-fields-for-change/index.js';
|
||||||
|
|
||||||
export default [listExternalScheduledEventFields];
|
export default [
|
||||||
|
listExternalScheduledEventFields,
|
||||||
|
listScheduledEventFieldsForChange,
|
||||||
|
];
|
||||||
|
@@ -0,0 +1,87 @@
|
|||||||
|
export default {
|
||||||
|
name: 'List scheduled event fields for change',
|
||||||
|
key: 'listScheduledEventFieldsForChange',
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const isExternal = $.step.parameters.entityType === 3;
|
||||||
|
|
||||||
|
if (isExternal) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Location',
|
||||||
|
key: 'location',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'The location of the event (1-100 characters). This will be omitted if type is NOT EXTERNAL',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Start-Time',
|
||||||
|
key: 'scheduledStartTime',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: 'The time the event will start [ISO8601]',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'End-Time',
|
||||||
|
key: 'scheduledEndTime',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'The time the event will end [ISO8601]. This will be omitted if type is NOT EXTERNAL',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Channel',
|
||||||
|
key: 'channel_id',
|
||||||
|
type: 'dropdown',
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'Pick a voice or stage channel to link the event to. This will be omitted if type is EXTERNAL',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listVoiceChannels',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Location',
|
||||||
|
key: 'location',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'The location of the event (1-100 characters). This will be omitted if type is NOT EXTERNAL',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Start-Time',
|
||||||
|
key: 'scheduledStartTime',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description: 'The time the event will start [ISO8601]',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'End-Time',
|
||||||
|
key: 'scheduledEndTime',
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'The time the event will end [ISO8601]. This will be omitted if type is NOT EXTERNAL',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
};
|
@@ -1,5 +1,6 @@
|
|||||||
import defineAction from '../../../../helpers/define-action.js';
|
import defineAction from '../../../../helpers/define-action.js';
|
||||||
|
|
||||||
|
import base64ToString from './transformers/base64-to-string.js';
|
||||||
import capitalize from './transformers/capitalize.js';
|
import capitalize from './transformers/capitalize.js';
|
||||||
import extractEmailAddress from './transformers/extract-email-address.js';
|
import extractEmailAddress from './transformers/extract-email-address.js';
|
||||||
import extractNumber from './transformers/extract-number.js';
|
import extractNumber from './transformers/extract-number.js';
|
||||||
@@ -8,10 +9,12 @@ import lowercase from './transformers/lowercase.js';
|
|||||||
import markdownToHtml from './transformers/markdown-to-html.js';
|
import markdownToHtml from './transformers/markdown-to-html.js';
|
||||||
import pluralize from './transformers/pluralize.js';
|
import pluralize from './transformers/pluralize.js';
|
||||||
import replace from './transformers/replace.js';
|
import replace from './transformers/replace.js';
|
||||||
|
import stringToBase64 from './transformers/string-to-base64.js';
|
||||||
import trimWhitespace from './transformers/trim-whitespace.js';
|
import trimWhitespace from './transformers/trim-whitespace.js';
|
||||||
import useDefaultValue from './transformers/use-default-value.js';
|
import useDefaultValue from './transformers/use-default-value.js';
|
||||||
|
|
||||||
const transformers = {
|
const transformers = {
|
||||||
|
base64ToString,
|
||||||
capitalize,
|
capitalize,
|
||||||
extractEmailAddress,
|
extractEmailAddress,
|
||||||
extractNumber,
|
extractNumber,
|
||||||
@@ -20,6 +23,7 @@ const transformers = {
|
|||||||
markdownToHtml,
|
markdownToHtml,
|
||||||
pluralize,
|
pluralize,
|
||||||
replace,
|
replace,
|
||||||
|
stringToBase64,
|
||||||
trimWhitespace,
|
trimWhitespace,
|
||||||
useDefaultValue,
|
useDefaultValue,
|
||||||
};
|
};
|
||||||
@@ -37,6 +41,7 @@ export default defineAction({
|
|||||||
required: true,
|
required: true,
|
||||||
variables: true,
|
variables: true,
|
||||||
options: [
|
options: [
|
||||||
|
{ label: 'Base64 to String', value: 'base64ToString' },
|
||||||
{ label: 'Capitalize', value: 'capitalize' },
|
{ label: 'Capitalize', value: 'capitalize' },
|
||||||
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
|
||||||
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
|
||||||
@@ -45,6 +50,7 @@ export default defineAction({
|
|||||||
{ label: 'Lowercase', value: 'lowercase' },
|
{ label: 'Lowercase', value: 'lowercase' },
|
||||||
{ label: 'Pluralize', value: 'pluralize' },
|
{ label: 'Pluralize', value: 'pluralize' },
|
||||||
{ label: 'Replace', value: 'replace' },
|
{ label: 'Replace', value: 'replace' },
|
||||||
|
{ label: 'String to Base64', value: 'stringToBase64' },
|
||||||
{ label: 'Trim Whitespace', value: 'trimWhitespace' },
|
{ label: 'Trim Whitespace', value: 'trimWhitespace' },
|
||||||
{ label: 'Use Default Value', value: 'useDefaultValue' },
|
{ label: 'Use Default Value', value: 'useDefaultValue' },
|
||||||
],
|
],
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
const base64ToString = ($) => {
|
||||||
|
const input = $.step.parameters.input;
|
||||||
|
const decodedString = Buffer.from(input, 'base64').toString('utf8');
|
||||||
|
|
||||||
|
return decodedString;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default base64ToString;
|
@@ -0,0 +1,8 @@
|
|||||||
|
const stringtoBase64 = ($) => {
|
||||||
|
const input = $.step.parameters.input;
|
||||||
|
const base64String = Buffer.from(input).toString('base64');
|
||||||
|
|
||||||
|
return base64String;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default stringtoBase64;
|
@@ -1,3 +1,4 @@
|
|||||||
|
import base64ToString from './text/base64-to-string.js';
|
||||||
import capitalize from './text/capitalize.js';
|
import capitalize from './text/capitalize.js';
|
||||||
import extractEmailAddress from './text/extract-email-address.js';
|
import extractEmailAddress from './text/extract-email-address.js';
|
||||||
import extractNumber from './text/extract-number.js';
|
import extractNumber from './text/extract-number.js';
|
||||||
@@ -6,6 +7,7 @@ import lowercase from './text/lowercase.js';
|
|||||||
import markdownToHtml from './text/markdown-to-html.js';
|
import markdownToHtml from './text/markdown-to-html.js';
|
||||||
import pluralize from './text/pluralize.js';
|
import pluralize from './text/pluralize.js';
|
||||||
import replace from './text/replace.js';
|
import replace from './text/replace.js';
|
||||||
|
import stringToBase64 from './text/string-to-base64.js';
|
||||||
import trimWhitespace from './text/trim-whitespace.js';
|
import trimWhitespace from './text/trim-whitespace.js';
|
||||||
import useDefaultValue from './text/use-default-value.js';
|
import useDefaultValue from './text/use-default-value.js';
|
||||||
import performMathOperation from './numbers/perform-math-operation.js';
|
import performMathOperation from './numbers/perform-math-operation.js';
|
||||||
@@ -15,6 +17,7 @@ import formatPhoneNumber from './numbers/format-phone-number.js';
|
|||||||
import formatDateTime from './date-time/format-date-time.js';
|
import formatDateTime from './date-time/format-date-time.js';
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
base64ToString,
|
||||||
capitalize,
|
capitalize,
|
||||||
extractEmailAddress,
|
extractEmailAddress,
|
||||||
extractNumber,
|
extractNumber,
|
||||||
@@ -23,6 +26,7 @@ const options = {
|
|||||||
markdownToHtml,
|
markdownToHtml,
|
||||||
pluralize,
|
pluralize,
|
||||||
replace,
|
replace,
|
||||||
|
stringToBase64,
|
||||||
trimWhitespace,
|
trimWhitespace,
|
||||||
useDefaultValue,
|
useDefaultValue,
|
||||||
performMathOperation,
|
performMathOperation,
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
const base64ToString = [
|
||||||
|
{
|
||||||
|
label: 'Input',
|
||||||
|
key: 'input',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'Text that will be converted from Base64 to string.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default base64ToString;
|
@@ -0,0 +1,12 @@
|
|||||||
|
const stringToBase64 = [
|
||||||
|
{
|
||||||
|
label: 'Input',
|
||||||
|
key: 'input',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'Text that will be converted to Base64.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default stringToBase64;
|
@@ -18,7 +18,9 @@ const port = process.env.PORT || '3000';
|
|||||||
const serveWebAppSeparately =
|
const serveWebAppSeparately =
|
||||||
process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false;
|
process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false;
|
||||||
|
|
||||||
let apiUrl = new URL(`${protocol}://${host}:${port}`).toString();
|
let apiUrl = new URL(
|
||||||
|
process.env.API_URL || `${protocol}://${host}:${port}`
|
||||||
|
).toString();
|
||||||
apiUrl = apiUrl.substring(0, apiUrl.length - 1);
|
apiUrl = apiUrl.substring(0, apiUrl.length - 1);
|
||||||
|
|
||||||
// use apiUrl by default, which has less priority over the following cases
|
// use apiUrl by default, which has less priority over the following cases
|
||||||
@@ -88,6 +90,10 @@ const appConfig = {
|
|||||||
licenseKey: process.env.LICENSE_KEY,
|
licenseKey: process.env.LICENSE_KEY,
|
||||||
sentryDsn: process.env.SENTRY_DSN,
|
sentryDsn: process.env.SENTRY_DSN,
|
||||||
CI: process.env.CI === 'true',
|
CI: process.env.CI === 'true',
|
||||||
|
disableNotificationsPage: process.env.DISABLE_NOTIFICATIONS_PAGE === 'true',
|
||||||
|
disableFavicon: process.env.DISABLE_FAVICON === 'true',
|
||||||
|
additionalDrawerLink: process.env.ADDITIONAL_DRAWER_LINK,
|
||||||
|
additionalDrawerLinkText: process.env.ADDITIONAL_DRAWER_LINK_TEXT,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!appConfig.encryptionKey) {
|
if (!appConfig.encryptionKey) {
|
||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
import { renderObject } from '../../../../../helpers/renderer.js';
|
||||||
|
import permissionCatalog from '../../../../../helpers/permission-catalog.ee.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
renderObject(response, permissionCatalog);
|
||||||
|
};
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
|
import getPermissionsCatalogMock from '../../../../../../test/mocks/rest/api/v1/admin/permissions/get-permissions-catalog.ee.js';
|
||||||
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/admin/permissions/catalog', () => {
|
||||||
|
let role, currentUser, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
role = await createRole({ key: 'admin' });
|
||||||
|
currentUser = await createUser({ roleId: role.id });
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return roles', async () => {
|
||||||
|
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/admin/permissions/catalog')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = await getPermissionsCatalogMock();
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,16 @@
|
|||||||
|
import { renderObject } from '../../../../../helpers/renderer.js';
|
||||||
|
import Role from '../../../../../models/role.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const role = await Role.query()
|
||||||
|
.leftJoinRelated({
|
||||||
|
permissions: true,
|
||||||
|
})
|
||||||
|
.withGraphFetched({
|
||||||
|
permissions: true,
|
||||||
|
})
|
||||||
|
.findById(request.params.roleId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
renderObject(response, role);
|
||||||
|
};
|
@@ -0,0 +1,38 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
|
import { createPermission } from '../../../../../../test/factories/permission.js';
|
||||||
|
import getRoleMock from '../../../../../../test/mocks/rest/api/v1/admin/roles/get-role.ee.js';
|
||||||
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/admin/roles/:roleId', () => {
|
||||||
|
let role, currentUser, token, permissionOne, permissionTwo;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
role = await createRole({ key: 'admin' });
|
||||||
|
permissionOne = await createPermission({ roleId: role.id });
|
||||||
|
permissionTwo = await createPermission({ roleId: role.id });
|
||||||
|
currentUser = await createUser({ roleId: role.id });
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return roles', async () => {
|
||||||
|
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get(`/api/v1/admin/roles/${role.id}`)
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = await getRoleMock(role, [
|
||||||
|
permissionOne,
|
||||||
|
permissionTwo,
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { renderObject } from '../../../../../helpers/renderer.js';
|
||||||
|
import Role from '../../../../../models/role.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const roles = await Role.query().orderBy('name');
|
||||||
|
|
||||||
|
renderObject(response, roles);
|
||||||
|
};
|
@@ -0,0 +1,33 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
|
import getRolesMock from '../../../../../../test/mocks/rest/api/v1/admin/roles/get-roles.ee.js';
|
||||||
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/admin/roles', () => {
|
||||||
|
let roleOne, roleTwo, currentUser, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
roleOne = await createRole({ key: 'admin' });
|
||||||
|
roleTwo = await createRole({ key: 'user' });
|
||||||
|
currentUser = await createUser({ roleId: roleOne.id });
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return roles', async () => {
|
||||||
|
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/admin/roles')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = await getRolesMock([roleOne, roleTwo]);
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,10 @@
|
|||||||
|
import { renderObject } from '../../../../../helpers/renderer.js';
|
||||||
|
import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const samlAuthProvider = await SamlAuthProvider.query()
|
||||||
|
.findById(request.params.samlAuthProviderId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
renderObject(response, samlAuthProvider);
|
||||||
|
};
|
@@ -0,0 +1,34 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
|
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
|
||||||
|
import getSamlAuthProviderMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.js';
|
||||||
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => {
|
||||||
|
let samlAuthProvider, currentUser, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const role = await createRole({ key: 'admin' });
|
||||||
|
currentUser = await createUser({ roleId: role.id });
|
||||||
|
samlAuthProvider = await createSamlAuthProvider();
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return saml auth provider with specified id', async () => {
|
||||||
|
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get(`/api/v1/admin/saml-auth-providers/${samlAuthProvider.id}`)
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = await getSamlAuthProviderMock(samlAuthProvider);
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,11 @@
|
|||||||
|
import { renderObject } from '../../../../../helpers/renderer.js';
|
||||||
|
import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const samlAuthProviders = await SamlAuthProvider.query().orderBy(
|
||||||
|
'created_at',
|
||||||
|
'desc'
|
||||||
|
);
|
||||||
|
|
||||||
|
renderObject(response, samlAuthProviders);
|
||||||
|
};
|
@@ -0,0 +1,39 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createRole } from '../../../../../../test/factories/role.js';
|
||||||
|
import { createUser } from '../../../../../../test/factories/user.js';
|
||||||
|
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
|
||||||
|
import getSamlAuthProvidersMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.js';
|
||||||
|
import * as license from '../../../../../helpers/license.ee.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/admin/saml-auth-providers', () => {
|
||||||
|
let samlAuthProviderOne, samlAuthProviderTwo, currentUser, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const role = await createRole({ key: 'admin' });
|
||||||
|
currentUser = await createUser({ roleId: role.id });
|
||||||
|
|
||||||
|
samlAuthProviderOne = await createSamlAuthProvider();
|
||||||
|
samlAuthProviderTwo = await createSamlAuthProvider();
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return saml auth providers', async () => {
|
||||||
|
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
|
||||||
|
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/admin/saml-auth-providers')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = await getSamlAuthProvidersMock([
|
||||||
|
samlAuthProviderTwo,
|
||||||
|
samlAuthProviderOne,
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import axios from '../../../../helpers/axios-with-proxy.js';
|
||||||
|
import logger from '../../../../helpers/logger.js';
|
||||||
|
|
||||||
|
const NOTIFICATIONS_URL =
|
||||||
|
'https://notifications.automatisch.io/notifications.json';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
let notifications = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(NOTIFICATIONS_URL);
|
||||||
|
notifications = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error fetching notifications API endpoint!', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderObject(response, notifications);
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { describe, it } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/automatisch/notifications', () => {
|
||||||
|
it('should return Automatisch notifications', async () => {
|
||||||
|
await request(app).get('/api/v1/automatisch/notifications').expect(200);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,6 @@
|
|||||||
|
import appConfig from '../../../../config/app.js';
|
||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
renderObject(response, { version: appConfig.version });
|
||||||
|
};
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/automatisch/version', () => {
|
||||||
|
it('should return Automatisch version', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/automatisch/version')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
data: {
|
||||||
|
version: '0.10.0',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import Billing from '../../../../helpers/billing/index.ee.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const paddleInfo = Billing.paddleInfo;
|
||||||
|
|
||||||
|
renderObject(response, paddleInfo);
|
||||||
|
};
|
@@ -0,0 +1,33 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createUser } from '../../../../../test/factories/user.js';
|
||||||
|
import getPaddleInfoMock from '../../../../../test/mocks/rest/api/v1/payment/get-paddle-info.js';
|
||||||
|
import appConfig from '../../../../config/app.js';
|
||||||
|
import billing from '../../../../helpers/billing/index.ee.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/payment/paddle-info', () => {
|
||||||
|
let user, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await createUser();
|
||||||
|
token = createAuthTokenByUserId(user.id);
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
vi.spyOn(billing.paddleInfo, 'vendorId', 'get').mockReturnValue(
|
||||||
|
'sampleVendorId'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return payment plans', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/payment/paddle-info')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = await getPaddleInfoMock();
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import Billing from '../../../../helpers/billing/index.ee.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const paymentPlans = Billing.paddlePlans;
|
||||||
|
|
||||||
|
renderObject(response, paymentPlans);
|
||||||
|
};
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createUser } from '../../../../../test/factories/user.js';
|
||||||
|
import getPaymentPlansMock from '../../../../../test/mocks/rest/api/v1/payment/get-plans.js';
|
||||||
|
import appConfig from '../../../../config/app.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/payment/plans', () => {
|
||||||
|
let user, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await createUser();
|
||||||
|
token = createAuthTokenByUserId(user.id);
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return payment plans', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/payment/plans')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = await getPaymentPlansMock();
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,5 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
renderObject(response, request.currentUser);
|
||||||
|
};
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||||
|
import { createUser } from '../../../../../test/factories/user';
|
||||||
|
import getCurrentUserMock from '../../../../../test/mocks/rest/api/v1/users/get-current-user';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users/me', () => {
|
||||||
|
let role, currentUser, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentUser = await createUser();
|
||||||
|
role = await currentUser.$relatedQuery('role');
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return current user info', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/users/me')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = getCurrentUserMock(currentUser, role);
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const inTrial = await request.currentUser.inTrial();
|
||||||
|
|
||||||
|
const trialInfo = {
|
||||||
|
inTrial,
|
||||||
|
expireAt: request.currentUser.trialExpiryDate,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderObject(response, trialInfo);
|
||||||
|
};
|
@@ -0,0 +1,38 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
|
||||||
|
import { createUser } from '../../../../../test/factories/user.js';
|
||||||
|
import getUserTrialMock from '../../../../../test/mocks/rest/api/v1/users/get-user-trial.js';
|
||||||
|
import appConfig from '../../../../config/app.js';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import User from '../../../../models/user.js';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users/:userId/trial', () => {
|
||||||
|
let user, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
|
||||||
|
user = await createUser({ trialExpiryDate });
|
||||||
|
token = createAuthTokenByUserId(user.id);
|
||||||
|
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should return in trial, active subscription and expire at info', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(false);
|
||||||
|
vi.spyOn(User.prototype, 'hasActiveSubscription').mockResolvedValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get(`/api/v1/users/${user.id}/trial`)
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = await getUserTrialMock(user);
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
16
packages/backend/src/controllers/api/v1/users/get-user.js
Normal file
16
packages/backend/src/controllers/api/v1/users/get-user.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import User from '../../../../models/user.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const user = await User.query()
|
||||||
|
.leftJoinRelated({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.withGraphFetched({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.findById(request.params.userId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
renderObject(response, user);
|
||||||
|
};
|
@@ -0,0 +1,36 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app.js';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||||
|
import { createUser } from '../../../../../test/factories/user';
|
||||||
|
import { createPermission } from '../../../../../test/factories/permission';
|
||||||
|
import getUserMock from '../../../../../test/mocks/rest/api/v1/users/get-user';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users/:userId', () => {
|
||||||
|
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentUser = await createUser();
|
||||||
|
anotherUser = await createUser();
|
||||||
|
currentUserRole = await currentUser.$relatedQuery('role');
|
||||||
|
anotherUserRole = await anotherUser.$relatedQuery('role');
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
roleId: currentUserRole.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
});
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return specified user info', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get(`/api/v1/users/${anotherUser.id}`)
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedPayload = getUserMock(anotherUser, anotherUserRole);
|
||||||
|
expect(response.body).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
18
packages/backend/src/controllers/api/v1/users/get-users.js
Normal file
18
packages/backend/src/controllers/api/v1/users/get-users.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { renderObject } from '../../../../helpers/renderer.js';
|
||||||
|
import User from '../../../../models/user.js';
|
||||||
|
import paginateRest from '../../../../helpers/pagination-rest.js';
|
||||||
|
|
||||||
|
export default async (request, response) => {
|
||||||
|
const usersQuery = User.query()
|
||||||
|
.leftJoinRelated({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.withGraphFetched({
|
||||||
|
role: true,
|
||||||
|
})
|
||||||
|
.orderBy('full_name', 'asc');
|
||||||
|
|
||||||
|
const users = await paginateRest(usersQuery, request.query.page);
|
||||||
|
|
||||||
|
renderObject(response, users);
|
||||||
|
};
|
@@ -0,0 +1,56 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../../../app';
|
||||||
|
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
|
||||||
|
import { createRole } from '../../../../../test/factories/role';
|
||||||
|
import { createPermission } from '../../../../../test/factories/permission';
|
||||||
|
import { createUser } from '../../../../../test/factories/user';
|
||||||
|
import getUsersMock from '../../../../../test/mocks/rest/api/v1/users/get-users';
|
||||||
|
|
||||||
|
describe('GET /api/v1/users', () => {
|
||||||
|
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
currentUserRole = await createRole({
|
||||||
|
key: 'currentUser',
|
||||||
|
name: 'Current user role',
|
||||||
|
});
|
||||||
|
|
||||||
|
await createPermission({
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
roleId: currentUserRole.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
currentUser = await createUser({
|
||||||
|
roleId: currentUserRole.id,
|
||||||
|
fullName: 'Current User',
|
||||||
|
});
|
||||||
|
|
||||||
|
anotherUserRole = await createRole({
|
||||||
|
key: 'anotherUser',
|
||||||
|
name: 'Another user role',
|
||||||
|
});
|
||||||
|
|
||||||
|
anotherUser = await createUser({
|
||||||
|
roleId: anotherUserRole.id,
|
||||||
|
fullName: 'Another User',
|
||||||
|
});
|
||||||
|
|
||||||
|
token = createAuthTokenByUserId(currentUser.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return users data', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.get('/api/v1/users')
|
||||||
|
.set('Authorization', token)
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = await getUsersMock(
|
||||||
|
[anotherUser, currentUser],
|
||||||
|
[anotherUserRole, currentUserRole]
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
3
packages/backend/src/controllers/healthcheck/index.js
Normal file
3
packages/backend/src/controllers/healthcheck/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default async (request, response) => {
|
||||||
|
response.status(200).end();
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { describe, it } from 'vitest';
|
||||||
|
import request from 'supertest';
|
||||||
|
import app from '../../app.js';
|
||||||
|
|
||||||
|
describe('GET /healthcheck', () => {
|
||||||
|
it('should return 200 response with version data', async () => {
|
||||||
|
await request(app).get('/healthcheck').expect(200);
|
||||||
|
});
|
||||||
|
});
|
@@ -1,7 +1,10 @@
|
|||||||
|
import appConfig from '../../config/app.js';
|
||||||
import User from '../../models/user.js';
|
import User from '../../models/user.js';
|
||||||
import Role from '../../models/role.js';
|
import Role from '../../models/role.js';
|
||||||
|
|
||||||
const registerUser = async (_parent, params) => {
|
const registerUser = async (_parent, params) => {
|
||||||
|
if (!appConfig.isCloud) return;
|
||||||
|
|
||||||
const { fullName, email, password } = params.input;
|
const { fullName, email, password } = params.input;
|
||||||
|
|
||||||
const existingUser = await User.query().findOne({
|
const existingUser = await User.query().findOne({
|
||||||
|
@@ -1,9 +1,17 @@
|
|||||||
|
import appConfig from '../../config/app.js';
|
||||||
import { hasValidLicense } from '../../helpers/license.ee.js';
|
import { hasValidLicense } from '../../helpers/license.ee.js';
|
||||||
import Config from '../../models/config.js';
|
import Config from '../../models/config.js';
|
||||||
|
|
||||||
const getConfig = async (_parent, params) => {
|
const getConfig = async (_parent, params) => {
|
||||||
if (!(await hasValidLicense())) return {};
|
if (!(await hasValidLicense())) return {};
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
disableNotificationsPage: appConfig.disableNotificationsPage,
|
||||||
|
disableFavicon: appConfig.disableFavicon,
|
||||||
|
additionalDrawerLink: appConfig.additionalDrawerLink,
|
||||||
|
additionalDrawerLinkText: appConfig.additionalDrawerLinkText,
|
||||||
|
};
|
||||||
|
|
||||||
const configQuery = Config.query();
|
const configQuery = Config.query();
|
||||||
|
|
||||||
if (Array.isArray(params.keys)) {
|
if (Array.isArray(params.keys)) {
|
||||||
@@ -18,7 +26,7 @@ const getConfig = async (_parent, params) => {
|
|||||||
computedConfig[key] = value?.data;
|
computedConfig[key] = value?.data;
|
||||||
|
|
||||||
return computedConfig;
|
return computedConfig;
|
||||||
}, {});
|
}, defaultConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getConfig;
|
export default getConfig;
|
||||||
|
@@ -2,6 +2,7 @@ import { vi, describe, it, expect, beforeEach } from 'vitest';
|
|||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import app from '../../app';
|
import app from '../../app';
|
||||||
import { createConfig } from '../../../test/factories/config';
|
import { createConfig } from '../../../test/factories/config';
|
||||||
|
import appConfig from '../../config/app';
|
||||||
import * as license from '../../helpers/license.ee';
|
import * as license from '../../helpers/license.ee';
|
||||||
|
|
||||||
describe('graphQL getConfig query', () => {
|
describe('graphQL getConfig query', () => {
|
||||||
@@ -56,6 +57,10 @@ describe('graphQL getConfig query', () => {
|
|||||||
[configOne.key]: configOne.value.data,
|
[configOne.key]: configOne.value.data,
|
||||||
[configTwo.key]: configTwo.value.data,
|
[configTwo.key]: configTwo.value.data,
|
||||||
[configThree.key]: configThree.value.data,
|
[configThree.key]: configThree.value.data,
|
||||||
|
disableNotificationsPage: false,
|
||||||
|
disableFavicon: false,
|
||||||
|
additionalDrawerLink: undefined,
|
||||||
|
additionalDrawerLinkText: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -82,6 +87,48 @@ describe('graphQL getConfig query', () => {
|
|||||||
getConfig: {
|
getConfig: {
|
||||||
[configOne.key]: configOne.value.data,
|
[configOne.key]: configOne.value.data,
|
||||||
[configTwo.key]: configTwo.value.data,
|
[configTwo.key]: configTwo.value.data,
|
||||||
|
disableNotificationsPage: false,
|
||||||
|
disableFavicon: false,
|
||||||
|
additionalDrawerLink: undefined,
|
||||||
|
additionalDrawerLinkText: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(response.body).toEqual(expectedResponsePayload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('and with different defaults', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
vi.spyOn(appConfig, 'disableNotificationsPage', 'get').mockReturnValue(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
vi.spyOn(appConfig, 'disableFavicon', 'get').mockReturnValue(true);
|
||||||
|
vi.spyOn(appConfig, 'additionalDrawerLink', 'get').mockReturnValue(
|
||||||
|
'https://automatisch.io'
|
||||||
|
);
|
||||||
|
vi.spyOn(appConfig, 'additionalDrawerLinkText', 'get').mockReturnValue(
|
||||||
|
'Automatisch'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return custom config', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.post('/graphql')
|
||||||
|
.send({ query })
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const expectedResponsePayload = {
|
||||||
|
data: {
|
||||||
|
getConfig: {
|
||||||
|
[configOne.key]: configOne.value.data,
|
||||||
|
[configTwo.key]: configTwo.value.data,
|
||||||
|
[configThree.key]: configThree.value.data,
|
||||||
|
disableNotificationsPage: true,
|
||||||
|
disableFavicon: true,
|
||||||
|
additionalDrawerLink: 'https://automatisch.io',
|
||||||
|
additionalDrawerLinkText: 'Automatisch',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -20,7 +20,8 @@ export const isAuthenticated = async (_parent, _args, req) => {
|
|||||||
.withGraphFetched({
|
.withGraphFetched({
|
||||||
role: true,
|
role: true,
|
||||||
permissions: true,
|
permissions: true,
|
||||||
});
|
})
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -28,6 +29,14 @@ export const isAuthenticated = async (_parent, _args, req) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const authenticateUser = async (request, response, next) => {
|
||||||
|
if (await isAuthenticated(null, null, request)) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return response.status(401).end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isAuthenticatedRule = rule()(isAuthenticated);
|
const isAuthenticatedRule = rule()(isAuthenticated);
|
||||||
|
|
||||||
export const authenticationRules = {
|
export const authenticationRules = {
|
||||||
|
@@ -1,11 +1,8 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { allow } from 'graphql-shield';
|
import { allow } from 'graphql-shield';
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import User from '../models/user.js';
|
|
||||||
import { isAuthenticated, authenticationRules } from './authentication.js';
|
import { isAuthenticated, authenticationRules } from './authentication.js';
|
||||||
|
import { createUser } from '../../test/factories/user.js';
|
||||||
vi.mock('jsonwebtoken');
|
import createAuthTokenByUserId from '../helpers/create-auth-token-by-user-id.js';
|
||||||
vi.mock('../models/user.js');
|
|
||||||
|
|
||||||
describe('isAuthenticated', () => {
|
describe('isAuthenticated', () => {
|
||||||
it('should return false if no token is provided', async () => {
|
it('should return false if no token is provided', async () => {
|
||||||
@@ -14,29 +11,26 @@ describe('isAuthenticated', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if token is invalid', async () => {
|
it('should return false if token is invalid', async () => {
|
||||||
jwt.verify.mockImplementation(() => {
|
|
||||||
throw new Error('invalid token');
|
|
||||||
});
|
|
||||||
|
|
||||||
const req = { headers: { authorization: 'invalidToken' } };
|
const req = { headers: { authorization: 'invalidToken' } };
|
||||||
expect(await isAuthenticated(null, null, req)).toBe(false);
|
expect(await isAuthenticated(null, null, req)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if token is valid', async () => {
|
it('should return true if token is valid and there is a user', async () => {
|
||||||
jwt.verify.mockReturnValue({ userId: '123' });
|
const user = await createUser();
|
||||||
|
const token = createAuthTokenByUserId(user.id);
|
||||||
|
|
||||||
User.query.mockReturnValue({
|
const req = { headers: { authorization: token } };
|
||||||
findById: vi.fn().mockReturnValue({
|
|
||||||
leftJoinRelated: vi.fn().mockReturnThis(),
|
|
||||||
withGraphFetched: vi
|
|
||||||
.fn()
|
|
||||||
.mockResolvedValue({ id: '123', role: {}, permissions: {} }),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const req = { headers: { authorization: 'validToken' } };
|
|
||||||
expect(await isAuthenticated(null, null, req)).toBe(true);
|
expect(await isAuthenticated(null, null, req)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return false if token is valid and but there is no user', async () => {
|
||||||
|
const user = await createUser();
|
||||||
|
const token = createAuthTokenByUserId(user.id);
|
||||||
|
await user.$query().delete();
|
||||||
|
|
||||||
|
const req = { headers: { authorization: token } };
|
||||||
|
expect(await isAuthenticated(null, null, req)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('authentication rules', () => {
|
describe('authentication rules', () => {
|
||||||
|
33
packages/backend/src/helpers/authorization.js
Normal file
33
packages/backend/src/helpers/authorization.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const authorizationList = {
|
||||||
|
'GET /api/v1/users/:userId': {
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
},
|
||||||
|
'GET /api/v1/users/': {
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const authorizeUser = async (request, response, next) => {
|
||||||
|
const currentRoute =
|
||||||
|
request.method + ' ' + request.baseUrl + request.route.path;
|
||||||
|
const currentRouteRule = authorizationList[currentRoute];
|
||||||
|
|
||||||
|
try {
|
||||||
|
request.currentUser.can(currentRouteRule.action, currentRouteRule.subject);
|
||||||
|
next();
|
||||||
|
} catch (error) {
|
||||||
|
return response.status(403).end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const authorizeAdmin = async (request, response, next) => {
|
||||||
|
const role = await request.currentUser.$relatedQuery('role');
|
||||||
|
|
||||||
|
if (role?.isAdmin) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return response.status(403).end();
|
||||||
|
}
|
||||||
|
};
|
11
packages/backend/src/helpers/check-is-cloud.js
Normal file
11
packages/backend/src/helpers/check-is-cloud.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import appConfig from '../config/app.js';
|
||||||
|
|
||||||
|
export const checkIsCloud = async (request, response, next) => {
|
||||||
|
if (appConfig.isCloud) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return response.status(404).end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default checkIsCloud;
|
9
packages/backend/src/helpers/check-is-enterprise.js
Normal file
9
packages/backend/src/helpers/check-is-enterprise.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { hasValidLicense } from './license.ee.js';
|
||||||
|
|
||||||
|
export const checkIsEnterprise = async (request, response, next) => {
|
||||||
|
if (await hasValidLicense()) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return response.status(404).end();
|
||||||
|
}
|
||||||
|
};
|
@@ -1,6 +1,9 @@
|
|||||||
import * as path from 'path';
|
import path from 'path';
|
||||||
import * as fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as handlebars from 'handlebars';
|
import handlebars from 'handlebars';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const compileEmail = (emailPath, replacements = {}) => {
|
const compileEmail = (emailPath, replacements = {}) => {
|
||||||
const filePath = path.join(__dirname, `../views/emails/${emailPath}.ee.hbs`);
|
const filePath = path.join(__dirname, `../views/emails/${emailPath}.ee.hbs`);
|
||||||
|
@@ -4,8 +4,8 @@ import appConfig from '../config/app.js';
|
|||||||
const levels = {
|
const levels = {
|
||||||
error: 0,
|
error: 0,
|
||||||
warn: 1,
|
warn: 1,
|
||||||
info: 2,
|
http: 2,
|
||||||
http: 3,
|
info: 3,
|
||||||
debug: 4,
|
debug: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
25
packages/backend/src/helpers/pagination-rest.js
Normal file
25
packages/backend/src/helpers/pagination-rest.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const paginateRest = async (query, page) => {
|
||||||
|
const pageSize = 10;
|
||||||
|
|
||||||
|
page = parseInt(page, 10);
|
||||||
|
|
||||||
|
if (isNaN(page) || page < 1) {
|
||||||
|
page = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [records, count] = await Promise.all([
|
||||||
|
query.limit(pageSize).offset((page - 1) * pageSize),
|
||||||
|
query.resultSize(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageInfo: {
|
||||||
|
currentPage: page,
|
||||||
|
totalPages: Math.ceil(count / pageSize),
|
||||||
|
},
|
||||||
|
totalCount: count,
|
||||||
|
records,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default paginateRest;
|
44
packages/backend/src/helpers/renderer.js
Normal file
44
packages/backend/src/helpers/renderer.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import serializers from '../serializers/index.js';
|
||||||
|
|
||||||
|
const isPaginated = (object) =>
|
||||||
|
object?.pageInfo &&
|
||||||
|
object?.totalCount !== undefined &&
|
||||||
|
Array.isArray(object?.records);
|
||||||
|
|
||||||
|
const isArray = (object) =>
|
||||||
|
Array.isArray(object) || Array.isArray(object?.records);
|
||||||
|
|
||||||
|
const totalCount = (object) =>
|
||||||
|
isPaginated(object) ? object.totalCount : isArray(object) ? object.length : 1;
|
||||||
|
|
||||||
|
const renderObject = (response, object) => {
|
||||||
|
let data = isPaginated(object) ? object.records : object;
|
||||||
|
const type = isPaginated(object)
|
||||||
|
? object.records[0].constructor.name
|
||||||
|
: Array.isArray(object)
|
||||||
|
? object[0].constructor.name
|
||||||
|
: object.constructor.name;
|
||||||
|
|
||||||
|
const serializer = serializers[type];
|
||||||
|
|
||||||
|
if (serializer) {
|
||||||
|
data = Array.isArray(data)
|
||||||
|
? data.map((item) => serializer(item))
|
||||||
|
: serializer(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const computedPayload = {
|
||||||
|
data,
|
||||||
|
meta: {
|
||||||
|
type,
|
||||||
|
count: totalCount(object),
|
||||||
|
isArray: isArray(object),
|
||||||
|
currentPage: isPaginated(object) ? object.pageInfo.currentPage : null,
|
||||||
|
totalPages: isPaginated(object) ? object.pageInfo.totalPages : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return response.json(computedPayload);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { renderObject };
|
@@ -15,7 +15,7 @@ const webUIHandler = async (app) => {
|
|||||||
app.use(express.static(webBuildPath));
|
app.use(express.static(webBuildPath));
|
||||||
|
|
||||||
app.get('*', (_req, res) => {
|
app.get('*', (_req, res) => {
|
||||||
res.set('Content-Security-Policy', 'frame-ancestors: none;');
|
res.set('Content-Security-Policy', 'frame-ancestors \'none\';');
|
||||||
res.set('X-Frame-Options', 'DENY');
|
res.set('X-Frame-Options', 'DENY');
|
||||||
|
|
||||||
res.sendFile(indexHtml);
|
res.sendFile(indexHtml);
|
||||||
|
17
packages/backend/src/routes/api/v1/admin/permissions.ee.js
Normal file
17
packages/backend/src/routes/api/v1/admin/permissions.ee.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../../helpers/authentication.js';
|
||||||
|
import { authorizeAdmin } from '../../../../helpers/authorization.js';
|
||||||
|
import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js';
|
||||||
|
import getPermissionsCatalogAction from '../../../../controllers/api/v1/admin/permissions/get-permissions-catalog.ee.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/catalog',
|
||||||
|
authenticateUser,
|
||||||
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
|
getPermissionsCatalogAction
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
26
packages/backend/src/routes/api/v1/admin/roles.ee.js
Normal file
26
packages/backend/src/routes/api/v1/admin/roles.ee.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../../helpers/authentication.js';
|
||||||
|
import { authorizeAdmin } from '../../../../helpers/authorization.js';
|
||||||
|
import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js';
|
||||||
|
import getRolesAction from '../../../../controllers/api/v1/admin/roles/get-roles.ee.js';
|
||||||
|
import getRoleAction from '../../../../controllers/api/v1/admin/roles/get-role.ee.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/',
|
||||||
|
authenticateUser,
|
||||||
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
|
getRolesAction
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/:roleId',
|
||||||
|
authenticateUser,
|
||||||
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
|
getRoleAction
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
@@ -0,0 +1,26 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../../helpers/authentication.js';
|
||||||
|
import { authorizeAdmin } from '../../../../helpers/authorization.js';
|
||||||
|
import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js';
|
||||||
|
import getSamlAuthProvidersAction from '../../../../controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.js';
|
||||||
|
import getSamlAuthProviderAction from '../../../../controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/',
|
||||||
|
authenticateUser,
|
||||||
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
|
getSamlAuthProvidersAction
|
||||||
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/:samlAuthProviderId',
|
||||||
|
authenticateUser,
|
||||||
|
authorizeAdmin,
|
||||||
|
checkIsEnterprise,
|
||||||
|
getSamlAuthProviderAction
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
10
packages/backend/src/routes/api/v1/automatisch.js
Normal file
10
packages/backend/src/routes/api/v1/automatisch.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import versionAction from '../../../controllers/api/v1/automatisch/version.js';
|
||||||
|
import notificationsAction from '../../../controllers/api/v1/automatisch/notifications.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/version', versionAction);
|
||||||
|
router.get('/notifications', notificationsAction);
|
||||||
|
|
||||||
|
export default router;
|
12
packages/backend/src/routes/api/v1/payment.ee.js
Normal file
12
packages/backend/src/routes/api/v1/payment.ee.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../helpers/authentication.js';
|
||||||
|
import checkIsCloud from '../../../helpers/check-is-cloud.js';
|
||||||
|
import getPlansAction from '../../../controllers/api/v1/payment/get-plans.ee.js';
|
||||||
|
import getPaddleInfoAction from '../../../controllers/api/v1/payment/get-paddle-info.ee.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/plans', authenticateUser, checkIsCloud, getPlansAction);
|
||||||
|
router.get('/paddle-info', authenticateUser, checkIsCloud, getPaddleInfoAction);
|
||||||
|
|
||||||
|
export default router;
|
22
packages/backend/src/routes/api/v1/users.js
Normal file
22
packages/backend/src/routes/api/v1/users.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { authenticateUser } from '../../../helpers/authentication.js';
|
||||||
|
import { authorizeUser } from '../../../helpers/authorization.js';
|
||||||
|
import checkIsCloud from '../../../helpers/check-is-cloud.js';
|
||||||
|
import getCurrentUserAction from '../../../controllers/api/v1/users/get-current-user.js';
|
||||||
|
import getUserAction from '../../../controllers/api/v1/users/get-user.js';
|
||||||
|
import getUsersAction from '../../../controllers/api/v1/users/get-users.js';
|
||||||
|
import getUserTrialAction from '../../../controllers/api/v1/users/get-user-trial.ee.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/', authenticateUser, authorizeUser, getUsersAction);
|
||||||
|
router.get('/me', authenticateUser, getCurrentUserAction);
|
||||||
|
router.get('/:userId', authenticateUser, authorizeUser, getUserAction);
|
||||||
|
router.get(
|
||||||
|
'/:userId/trial',
|
||||||
|
authenticateUser,
|
||||||
|
checkIsCloud,
|
||||||
|
getUserTrialAction
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
8
packages/backend/src/routes/healthcheck.js
Normal file
8
packages/backend/src/routes/healthcheck.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import indexAction from '../controllers/healthcheck/index.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get('/', indexAction);
|
||||||
|
|
||||||
|
export default router;
|
@@ -2,11 +2,25 @@ import { Router } from 'express';
|
|||||||
import graphQLInstance from '../helpers/graphql-instance.js';
|
import graphQLInstance from '../helpers/graphql-instance.js';
|
||||||
import webhooksRouter from './webhooks.js';
|
import webhooksRouter from './webhooks.js';
|
||||||
import paddleRouter from './paddle.ee.js';
|
import paddleRouter from './paddle.ee.js';
|
||||||
|
import healthcheckRouter from './healthcheck.js';
|
||||||
|
import automatischRouter from './api/v1/automatisch.js';
|
||||||
|
import usersRouter from './api/v1/users.js';
|
||||||
|
import paymentRouter from './api/v1/payment.ee.js';
|
||||||
|
import samlAuthProvidersRouter from './api/v1/admin/saml-auth-providers.ee.js';
|
||||||
|
import rolesRouter from './api/v1/admin/roles.ee.js';
|
||||||
|
import permissionsRouter from './api/v1/admin/permissions.ee.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.use('/graphql', graphQLInstance);
|
router.use('/graphql', graphQLInstance);
|
||||||
router.use('/webhooks', webhooksRouter);
|
router.use('/webhooks', webhooksRouter);
|
||||||
router.use('/paddle', paddleRouter);
|
router.use('/paddle', paddleRouter);
|
||||||
|
router.use('/healthcheck', healthcheckRouter);
|
||||||
|
router.use('/api/v1/automatisch', automatischRouter);
|
||||||
|
router.use('/api/v1/users', usersRouter);
|
||||||
|
router.use('/api/v1/payment', paymentRouter);
|
||||||
|
router.use('/api/v1/admin/saml-auth-providers', samlAuthProvidersRouter);
|
||||||
|
router.use('/api/v1/admin/roles', rolesRouter);
|
||||||
|
router.use('/api/v1/admin/permissions', permissionsRouter);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
13
packages/backend/src/serializers/index.js
Normal file
13
packages/backend/src/serializers/index.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import userSerializer from './user.js';
|
||||||
|
import roleSerializer from './role.js';
|
||||||
|
import permissionSerializer from './permission.js';
|
||||||
|
import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
|
||||||
|
|
||||||
|
const serializers = {
|
||||||
|
User: userSerializer,
|
||||||
|
Role: roleSerializer,
|
||||||
|
Permission: permissionSerializer,
|
||||||
|
SamlAuthProvider: samlAuthProviderSerializer,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default serializers;
|
13
packages/backend/src/serializers/permission.js
Normal file
13
packages/backend/src/serializers/permission.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const permissionSerializer = (permission) => {
|
||||||
|
return {
|
||||||
|
id: permission.id,
|
||||||
|
roleId: permission.roleId,
|
||||||
|
action: permission.action,
|
||||||
|
subject: permission.subject,
|
||||||
|
conditions: permission.conditions,
|
||||||
|
createdAt: permission.createdAt,
|
||||||
|
updatedAt: permission.updatedAt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default permissionSerializer;
|
25
packages/backend/src/serializers/permission.test.js
Normal file
25
packages/backend/src/serializers/permission.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { createPermission } from '../../test/factories/permission';
|
||||||
|
import permissionSerializer from './permission';
|
||||||
|
|
||||||
|
describe('permissionSerializer', () => {
|
||||||
|
let permission;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
permission = await createPermission();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return permission data', async () => {
|
||||||
|
const expectedPayload = {
|
||||||
|
id: permission.id,
|
||||||
|
roleId: permission.roleId,
|
||||||
|
action: permission.action,
|
||||||
|
subject: permission.subject,
|
||||||
|
conditions: permission.conditions,
|
||||||
|
createdAt: permission.createdAt,
|
||||||
|
updatedAt: permission.updatedAt,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(permissionSerializer(permission)).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
23
packages/backend/src/serializers/role.js
Normal file
23
packages/backend/src/serializers/role.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import permissionSerializer from './permission.js';
|
||||||
|
|
||||||
|
const roleSerializer = (role) => {
|
||||||
|
let roleData = {
|
||||||
|
id: role.id,
|
||||||
|
name: role.name,
|
||||||
|
key: role.key,
|
||||||
|
description: role.description,
|
||||||
|
createdAt: role.createdAt,
|
||||||
|
updatedAt: role.updatedAt,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (role.permissions) {
|
||||||
|
roleData.permissions = role.permissions.map((permission) =>
|
||||||
|
permissionSerializer(permission)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return roleData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default roleSerializer;
|
48
packages/backend/src/serializers/role.test.js
Normal file
48
packages/backend/src/serializers/role.test.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { createRole } from '../../test/factories/role';
|
||||||
|
import roleSerializer from './role';
|
||||||
|
import { createPermission } from '../../test/factories/permission';
|
||||||
|
|
||||||
|
describe('roleSerializer', () => {
|
||||||
|
let role, permissionOne, permissionTwo;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
role = await createRole();
|
||||||
|
|
||||||
|
permissionOne = await createPermission({
|
||||||
|
roleId: role.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionTwo = await createPermission({
|
||||||
|
roleId: role.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'Role',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return role data', async () => {
|
||||||
|
const expectedPayload = {
|
||||||
|
id: role.id,
|
||||||
|
name: role.name,
|
||||||
|
key: role.key,
|
||||||
|
description: role.description,
|
||||||
|
createdAt: role.createdAt,
|
||||||
|
updatedAt: role.updatedAt,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(roleSerializer(role)).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return role data with the permissions', async () => {
|
||||||
|
role.permissions = [permissionOne, permissionTwo];
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
permissions: [permissionOne, permissionTwo],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(roleSerializer(role)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
18
packages/backend/src/serializers/saml-auth-provider.ee.js
Normal file
18
packages/backend/src/serializers/saml-auth-provider.ee.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const samlAuthProviderSerializer = (samlAuthProvider) => {
|
||||||
|
return {
|
||||||
|
id: samlAuthProvider.id,
|
||||||
|
name: samlAuthProvider.name,
|
||||||
|
certificate: samlAuthProvider.certificate,
|
||||||
|
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
|
||||||
|
issuer: samlAuthProvider.issuer,
|
||||||
|
entryPoint: samlAuthProvider.entryPoint,
|
||||||
|
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
|
||||||
|
surnameAttributeName: samlAuthProvider.surnameAttributeName,
|
||||||
|
emailAttributeName: samlAuthProvider.emailAttributeName,
|
||||||
|
roleAttributeName: samlAuthProvider.roleAttributeName,
|
||||||
|
active: samlAuthProvider.active,
|
||||||
|
defaultRoleId: samlAuthProvider.defaultRoleId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default samlAuthProviderSerializer;
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { createSamlAuthProvider } from '../../test/factories/saml-auth-provider.ee.js';
|
||||||
|
import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
|
||||||
|
|
||||||
|
describe('samlAuthProviderSerializer', () => {
|
||||||
|
let samlAuthProvider;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
samlAuthProvider = await createSamlAuthProvider();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return saml auth provider data', async () => {
|
||||||
|
const expectedPayload = {
|
||||||
|
id: samlAuthProvider.id,
|
||||||
|
name: samlAuthProvider.name,
|
||||||
|
certificate: samlAuthProvider.certificate,
|
||||||
|
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
|
||||||
|
issuer: samlAuthProvider.issuer,
|
||||||
|
entryPoint: samlAuthProvider.entryPoint,
|
||||||
|
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
|
||||||
|
surnameAttributeName: samlAuthProvider.surnameAttributeName,
|
||||||
|
emailAttributeName: samlAuthProvider.emailAttributeName,
|
||||||
|
roleAttributeName: samlAuthProvider.roleAttributeName,
|
||||||
|
active: samlAuthProvider.active,
|
||||||
|
defaultRoleId: samlAuthProvider.defaultRoleId,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(samlAuthProviderSerializer(samlAuthProvider)).toEqual(
|
||||||
|
expectedPayload
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
31
packages/backend/src/serializers/user.js
Normal file
31
packages/backend/src/serializers/user.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import roleSerializer from './role.js';
|
||||||
|
import permissionSerializer from './permission.js';
|
||||||
|
import appConfig from '../config/app.js';
|
||||||
|
|
||||||
|
const userSerializer = (user) => {
|
||||||
|
let userData = {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
updatedAt: user.updatedAt,
|
||||||
|
fullName: user.fullName,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (user.role) {
|
||||||
|
userData.role = roleSerializer(user.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.permissions) {
|
||||||
|
userData.permissions = user.permissions.map((permission) =>
|
||||||
|
permissionSerializer(permission)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appConfig.isCloud && user.trialExpiryDate) {
|
||||||
|
userData.trialExpiryDate = user.trialExpiryDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default userSerializer;
|
75
packages/backend/src/serializers/user.test.js
Normal file
75
packages/backend/src/serializers/user.test.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import appConfig from '../config/app';
|
||||||
|
import { createUser } from '../../test/factories/user';
|
||||||
|
import { createPermission } from '../../test/factories/permission';
|
||||||
|
import userSerializer from './user';
|
||||||
|
|
||||||
|
describe('userSerializer', () => {
|
||||||
|
let user, role, permissionOne, permissionTwo;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
user = await createUser();
|
||||||
|
role = await user.$relatedQuery('role');
|
||||||
|
|
||||||
|
permissionOne = await createPermission({
|
||||||
|
roleId: role.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'User',
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionTwo = await createPermission({
|
||||||
|
roleId: role.id,
|
||||||
|
action: 'read',
|
||||||
|
subject: 'Role',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
email: user.email,
|
||||||
|
fullName: user.fullName,
|
||||||
|
id: user.id,
|
||||||
|
updatedAt: user.updatedAt,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toEqual(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data with the role', async () => {
|
||||||
|
user.role = role;
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
role,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data with the permissions', async () => {
|
||||||
|
user.permissions = [permissionOne, permissionTwo];
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
permissions: [permissionOne, permissionTwo],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return user data with trial expiry date', async () => {
|
||||||
|
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
|
||||||
|
|
||||||
|
await user.$query().patch({
|
||||||
|
trialExpiryDate: DateTime.now().plus({ days: 30 }).toISODate(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectedPayload = {
|
||||||
|
trialExpiryDate: user.trialExpiryDate,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(userSerializer(user)).toMatchObject(expectedPayload);
|
||||||
|
});
|
||||||
|
});
|
@@ -1,4 +1,5 @@
|
|||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import Config from '../../src/models/config';
|
||||||
|
|
||||||
export const createConfig = async (params = {}) => {
|
export const createConfig = async (params = {}) => {
|
||||||
const configData = {
|
const configData = {
|
||||||
@@ -6,10 +7,7 @@ export const createConfig = async (params = {}) => {
|
|||||||
value: params?.value || { data: 'sampleConfig' },
|
value: params?.value || { data: 'sampleConfig' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const [config] = await global.knex
|
const config = await Config.query().insert(configData).returning('*');
|
||||||
.table('config')
|
|
||||||
.insert(configData)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import appConfig from '../../src/config/app';
|
import appConfig from '../../src/config/app';
|
||||||
import { AES } from 'crypto-js';
|
import { AES } from 'crypto-js';
|
||||||
|
import Connection from '../../src/models/connection';
|
||||||
|
|
||||||
export const createConnection = async (params = {}) => {
|
export const createConnection = async (params = {}) => {
|
||||||
params.key = params?.key || 'deepl';
|
params.key = params?.key || 'deepl';
|
||||||
@@ -16,10 +17,7 @@ export const createConnection = async (params = {}) => {
|
|||||||
appConfig.encryptionKey
|
appConfig.encryptionKey
|
||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
const [connection] = await global.knex
|
const connection = await Connection.query().insert(params).returning('*');
|
||||||
.table('connections')
|
|
||||||
.insert(params)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import ExecutionStep from '../../src/models/execution-step';
|
||||||
import { createExecution } from './execution';
|
import { createExecution } from './execution';
|
||||||
import { createStep } from './step';
|
import { createStep } from './step';
|
||||||
|
|
||||||
@@ -8,8 +9,7 @@ export const createExecutionStep = async (params = {}) => {
|
|||||||
params.dataIn = params?.dataIn || { dataIn: 'dataIn' };
|
params.dataIn = params?.dataIn || { dataIn: 'dataIn' };
|
||||||
params.dataOut = params?.dataOut || { dataOut: 'dataOut' };
|
params.dataOut = params?.dataOut || { dataOut: 'dataOut' };
|
||||||
|
|
||||||
const [executionStep] = await global.knex
|
const executionStep = await ExecutionStep.query()
|
||||||
.table('executionSteps')
|
|
||||||
.insert(params)
|
.insert(params)
|
||||||
.returning('*');
|
.returning('*');
|
||||||
|
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Execution from '../../src/models/execution';
|
||||||
import { createFlow } from './flow';
|
import { createFlow } from './flow';
|
||||||
|
|
||||||
export const createExecution = async (params = {}) => {
|
export const createExecution = async (params = {}) => {
|
||||||
@@ -6,10 +7,7 @@ export const createExecution = async (params = {}) => {
|
|||||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||||
|
|
||||||
const [execution] = await global.knex
|
const execution = await Execution.query().insert(params).returning('*');
|
||||||
.table('executions')
|
|
||||||
.insert(params)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return execution;
|
return execution;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Flow from '../../src/models/flow';
|
||||||
import { createUser } from './user';
|
import { createUser } from './user';
|
||||||
|
|
||||||
export const createFlow = async (params = {}) => {
|
export const createFlow = async (params = {}) => {
|
||||||
@@ -6,7 +7,7 @@ export const createFlow = async (params = {}) => {
|
|||||||
params.createdAt = params?.createdAt || new Date().toISOString();
|
params.createdAt = params?.createdAt || new Date().toISOString();
|
||||||
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
params.updatedAt = params?.updatedAt || new Date().toISOString();
|
||||||
|
|
||||||
const [flow] = await global.knex.table('flows').insert(params).returning('*');
|
const flow = await Flow.query().insert(params).returning('*');
|
||||||
|
|
||||||
return flow;
|
return flow;
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Permission from '../../src/models/permission';
|
||||||
import { createRole } from './role';
|
import { createRole } from './role';
|
||||||
|
|
||||||
export const createPermission = async (params = {}) => {
|
export const createPermission = async (params = {}) => {
|
||||||
@@ -6,10 +7,7 @@ export const createPermission = async (params = {}) => {
|
|||||||
params.subject = params?.subject || 'User';
|
params.subject = params?.subject || 'User';
|
||||||
params.conditions = params?.conditions || ['isCreator'];
|
params.conditions = params?.conditions || ['isCreator'];
|
||||||
|
|
||||||
const [permission] = await global.knex
|
const permission = await Permission.query().insert(params).returning('*');
|
||||||
.table('permissions')
|
|
||||||
.insert(params)
|
|
||||||
.returning('*');
|
|
||||||
|
|
||||||
return permission;
|
return permission;
|
||||||
};
|
};
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
|
import Role from '../../src/models/role';
|
||||||
|
|
||||||
export const createRole = async (params = {}) => {
|
export const createRole = async (params = {}) => {
|
||||||
params.name = params?.name || 'Viewer';
|
params.name = params?.name || 'Viewer';
|
||||||
params.key = params?.key || 'viewer';
|
params.key = params?.key || 'viewer';
|
||||||
|
|
||||||
const [role] = await global.knex.table('roles').insert(params).returning('*');
|
const role = await Role.query().insert(params).returning('*');
|
||||||
|
|
||||||
return role;
|
return role;
|
||||||
};
|
};
|
||||||
|
33
packages/backend/test/factories/saml-auth-provider.ee.js
Normal file
33
packages/backend/test/factories/saml-auth-provider.ee.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createRole } from './role';
|
||||||
|
import SamlAuthProvider from '../../src/models/saml-auth-provider.ee.js';
|
||||||
|
|
||||||
|
export const createSamlAuthProvider = async (params = {}) => {
|
||||||
|
params.name = params?.name || 'Keycloak SAML';
|
||||||
|
params.certificate = params?.certificate || 'certificate';
|
||||||
|
params.signatureAlgorithm = params?.signatureAlgorithm || 'sha512';
|
||||||
|
|
||||||
|
params.entryPoint =
|
||||||
|
params?.entryPoint ||
|
||||||
|
'https://example.com/auth/realms/automatisch/protocol/saml';
|
||||||
|
|
||||||
|
params.issuer = params?.issuer || 'automatisch-client';
|
||||||
|
|
||||||
|
params.firstnameAttributeName =
|
||||||
|
params?.firstnameAttributeName || 'urn:oid:2.1.1.42';
|
||||||
|
|
||||||
|
params.surnameAttributeName =
|
||||||
|
params?.surnameAttributeName || 'urn:oid:2.1.1.4';
|
||||||
|
|
||||||
|
params.emailAttributeName =
|
||||||
|
params?.emailAttributeName || 'urn:oid:1.1.2342.19200300.100.1.1';
|
||||||
|
|
||||||
|
params.roleAttributeName = params?.roleAttributeName || 'Role';
|
||||||
|
params.defaultRoleId = params?.defaultRoleId || (await createRole()).id;
|
||||||
|
params.active = params?.active || true;
|
||||||
|
|
||||||
|
const samlAuthProvider = await SamlAuthProvider.query()
|
||||||
|
.insert(params)
|
||||||
|
.returning('*');
|
||||||
|
|
||||||
|
return samlAuthProvider;
|
||||||
|
};
|
@@ -1,3 +1,4 @@
|
|||||||
|
import Step from '../../src/models/step';
|
||||||
import { createFlow } from './flow';
|
import { createFlow } from './flow';
|
||||||
|
|
||||||
export const createStep = async (params = {}) => {
|
export const createStep = async (params = {}) => {
|
||||||
@@ -16,7 +17,7 @@ export const createStep = async (params = {}) => {
|
|||||||
params.appKey =
|
params.appKey =
|
||||||
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
|
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
|
||||||
|
|
||||||
const [step] = await global.knex.table('steps').insert(params).returning('*');
|
const step = await Step.query().insert(params).returning('*');
|
||||||
|
|
||||||
return step;
|
return step;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { createRole } from './role';
|
import { createRole } from './role';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
|
import User from '../../src/models/user';
|
||||||
|
|
||||||
export const createUser = async (params = {}) => {
|
export const createUser = async (params = {}) => {
|
||||||
params.roleId = params?.roleId || (await createRole()).id;
|
params.roleId = params?.roleId || (await createRole()).id;
|
||||||
@@ -7,7 +8,7 @@ export const createUser = async (params = {}) => {
|
|||||||
params.email = params?.email || faker.internet.email();
|
params.email = params?.email || faker.internet.email();
|
||||||
params.password = params?.password || faker.internet.password();
|
params.password = params?.password || faker.internet.password();
|
||||||
|
|
||||||
const [user] = await global.knex.table('users').insert(params).returning('*');
|
const user = await User.query().insert(params).returning('*');
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,64 @@
|
|||||||
|
const getPermissionsCatalogMock = async () => {
|
||||||
|
const data = {
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
key: 'create',
|
||||||
|
label: 'Create',
|
||||||
|
subjects: ['Connection', 'Flow'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'read',
|
||||||
|
label: 'Read',
|
||||||
|
subjects: ['Connection', 'Execution', 'Flow'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
label: 'Update',
|
||||||
|
subjects: ['Connection', 'Flow'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: 'Delete',
|
||||||
|
subjects: ['Connection', 'Flow'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'publish',
|
||||||
|
label: 'Publish',
|
||||||
|
subjects: ['Flow'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
key: 'isCreator',
|
||||||
|
label: 'Is creator',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
subjects: [
|
||||||
|
{
|
||||||
|
key: 'Connection',
|
||||||
|
label: 'Connection',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Flow',
|
||||||
|
label: 'Flow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Execution',
|
||||||
|
label: 'Execution',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getPermissionsCatalogMock;
|
@@ -0,0 +1,33 @@
|
|||||||
|
const getRoleMock = async (role, permissions) => {
|
||||||
|
const data = {
|
||||||
|
id: role.id,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
description: role.description,
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
permissions: permissions.map((permission) => ({
|
||||||
|
id: permission.id,
|
||||||
|
action: permission.action,
|
||||||
|
conditions: permission.conditions,
|
||||||
|
roleId: permission.roleId,
|
||||||
|
subject: permission.subject,
|
||||||
|
createdAt: permission.createdAt.toISOString(),
|
||||||
|
updatedAt: permission.updatedAt.toISOString(),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Role',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getRoleMock;
|
@@ -0,0 +1,26 @@
|
|||||||
|
const getRolesMock = async (roles) => {
|
||||||
|
const data = roles.map((role) => {
|
||||||
|
return {
|
||||||
|
id: role.id,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
description: role.description,
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: data.length,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Role',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getRolesMock;
|
@@ -0,0 +1,29 @@
|
|||||||
|
const getSamlAuthProviderMock = async (samlAuthProvider) => {
|
||||||
|
const data = {
|
||||||
|
active: samlAuthProvider.active,
|
||||||
|
certificate: samlAuthProvider.certificate,
|
||||||
|
defaultRoleId: samlAuthProvider.defaultRoleId,
|
||||||
|
emailAttributeName: samlAuthProvider.emailAttributeName,
|
||||||
|
entryPoint: samlAuthProvider.entryPoint,
|
||||||
|
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
|
||||||
|
id: samlAuthProvider.id,
|
||||||
|
issuer: samlAuthProvider.issuer,
|
||||||
|
name: samlAuthProvider.name,
|
||||||
|
roleAttributeName: samlAuthProvider.roleAttributeName,
|
||||||
|
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
|
||||||
|
surnameAttributeName: samlAuthProvider.surnameAttributeName,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'SamlAuthProvider',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getSamlAuthProviderMock;
|
@@ -0,0 +1,31 @@
|
|||||||
|
const getSamlAuthProvidersMock = async (samlAuthProviders) => {
|
||||||
|
const data = samlAuthProviders.map((samlAuthProvider) => {
|
||||||
|
return {
|
||||||
|
active: samlAuthProvider.active,
|
||||||
|
certificate: samlAuthProvider.certificate,
|
||||||
|
defaultRoleId: samlAuthProvider.defaultRoleId,
|
||||||
|
emailAttributeName: samlAuthProvider.emailAttributeName,
|
||||||
|
entryPoint: samlAuthProvider.entryPoint,
|
||||||
|
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
|
||||||
|
id: samlAuthProvider.id,
|
||||||
|
issuer: samlAuthProvider.issuer,
|
||||||
|
name: samlAuthProvider.name,
|
||||||
|
roleAttributeName: samlAuthProvider.roleAttributeName,
|
||||||
|
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
|
||||||
|
surnameAttributeName: samlAuthProvider.surnameAttributeName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: data.length,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'SamlAuthProvider',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getSamlAuthProvidersMock;
|
@@ -0,0 +1,17 @@
|
|||||||
|
const getPaddleInfoMock = async () => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
sandbox: true,
|
||||||
|
vendorId: 'sampleVendorId',
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getPaddleInfoMock;
|
22
packages/backend/test/mocks/rest/api/v1/payment/get-plans.js
Normal file
22
packages/backend/test/mocks/rest/api/v1/payment/get-plans.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const getPaymentPlansMock = async () => {
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
limit: '10,000',
|
||||||
|
name: '10k - monthly',
|
||||||
|
price: '€20',
|
||||||
|
productId: '47384',
|
||||||
|
quota: 10000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getPaymentPlansMock;
|
@@ -0,0 +1,31 @@
|
|||||||
|
const getCurrentUserMock = (currentUser, role) => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
createdAt: currentUser.createdAt.toISOString(),
|
||||||
|
email: currentUser.email,
|
||||||
|
fullName: currentUser.fullName,
|
||||||
|
id: currentUser.id,
|
||||||
|
permissions: [],
|
||||||
|
role: {
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
description: null,
|
||||||
|
id: role.id,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
|
||||||
|
updatedAt: currentUser.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentUserMock;
|
@@ -0,0 +1,17 @@
|
|||||||
|
const getUserTrialMock = async (currentUser) => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
inTrial: await currentUser.inTrial(),
|
||||||
|
expireAt: currentUser.trialExpiryDate.toISOString(),
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'Object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUserTrialMock;
|
30
packages/backend/test/mocks/rest/api/v1/users/get-user.js
Normal file
30
packages/backend/test/mocks/rest/api/v1/users/get-user.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
const getUserMock = (currentUser, role) => {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
createdAt: currentUser.createdAt.toISOString(),
|
||||||
|
email: currentUser.email,
|
||||||
|
fullName: currentUser.fullName,
|
||||||
|
id: currentUser.id,
|
||||||
|
role: {
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
description: null,
|
||||||
|
id: role.id,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
|
||||||
|
updatedAt: currentUser.updatedAt.toISOString(),
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
count: 1,
|
||||||
|
currentPage: null,
|
||||||
|
isArray: false,
|
||||||
|
totalPages: null,
|
||||||
|
type: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUserMock;
|
38
packages/backend/test/mocks/rest/api/v1/users/get-users.js
Normal file
38
packages/backend/test/mocks/rest/api/v1/users/get-users.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const getUsersMock = async (users, roles) => {
|
||||||
|
const data = users.map((user) => {
|
||||||
|
const role = roles.find((r) => r.id === user.roleId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
createdAt: user.createdAt.toISOString(),
|
||||||
|
email: user.email,
|
||||||
|
fullName: user.fullName,
|
||||||
|
id: user.id,
|
||||||
|
role: role
|
||||||
|
? {
|
||||||
|
createdAt: role.createdAt.toISOString(),
|
||||||
|
description: role.description,
|
||||||
|
id: role.id,
|
||||||
|
isAdmin: role.isAdmin,
|
||||||
|
key: role.key,
|
||||||
|
name: role.name,
|
||||||
|
updatedAt: role.updatedAt.toISOString(),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
trialExpiryDate: user.trialExpiryDate.toISOString(),
|
||||||
|
updatedAt: user.updatedAt.toISOString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: data.length,
|
||||||
|
currentPage: 1,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: 1,
|
||||||
|
type: 'User',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getUsersMock;
|
@@ -14,31 +14,33 @@ The default values for some environment variables might be different in our deve
|
|||||||
Please be careful with the `ENCRYPTION_KEY` and `WEBHOOK_SECRET_KEY` environment variables. They are used to encrypt your credentials from third-party services and verify webhook requests. If you change them, your existing connections and flows will not continue to work.
|
Please be careful with the `ENCRYPTION_KEY` and `WEBHOOK_SECRET_KEY` environment variables. They are used to encrypt your credentials from third-party services and verify webhook requests. If you change them, your existing connections and flows will not continue to work.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
| Variable Name | Type | Default Value | Description |
|
| Variable Name | Type | Default Value | Description |
|
||||||
| --------------------------- | ------- | ------------------ | ---------------------------------------------------------------------------------------------------- |
|
| ---------------------------- | ------- | ------------------ | ----------------------------------------------------------------------------------- |
|
||||||
| `HOST` | string | `localhost` | HTTP Host |
|
| `HOST` | string | `localhost` | HTTP Host |
|
||||||
| `PROTOCOL` | string | `http` | HTTP Protocol |
|
| `PROTOCOL` | string | `http` | HTTP Protocol |
|
||||||
| `PORT` | string | `3000` | HTTP Port |
|
| `PORT` | string | `3000` | HTTP Port |
|
||||||
| `APP_ENV` | string | `production` | Automatisch Environment |
|
| `APP_ENV` | string | `production` | Automatisch Environment |
|
||||||
| `WEB_APP_URL` | string | | Can be used to override connection URLs and CORS URL |
|
| `WEB_APP_URL` | string | | Can be used to override connection URLs and CORS URL |
|
||||||
| `WEBHOOK_URL` | string | | Can be used to override webhook URL |
|
| `WEBHOOK_URL` | string | | Can be used to override webhook URL |
|
||||||
| `LOG_LEVEL` | string | `info` | Can be used to configure log level such as `error`, `warn`, `info`, `http`, `debug` |
|
| `LOG_LEVEL` | string | `info` | Can be used to configure log level such as `error`, `warn`, `info`, `http`, `debug` |
|
||||||
| `POSTGRES_DATABASE` | string | `automatisch` | Database Name |
|
| `POSTGRES_DATABASE` | string | `automatisch` | Database Name |
|
||||||
| `POSTGRES_SCHEMA` | string | `public` | Database Schema |
|
| `POSTGRES_SCHEMA` | string | `public` | Database Schema |
|
||||||
| `POSTGRES_PORT` | number | `5432` | Database Port |
|
| `POSTGRES_PORT` | number | `5432` | Database Port |
|
||||||
| `POSTGRES_ENABLE_SSL` | boolean | `false` | Enable/Disable SSL for the database |
|
| `POSTGRES_ENABLE_SSL` | boolean | `false` | Enable/Disable SSL for the database |
|
||||||
| `POSTGRES_HOST` | string | `postgres` | Database Host |
|
| `POSTGRES_HOST` | string | `postgres` | Database Host |
|
||||||
| `POSTGRES_USERNAME` | string | `automatisch_user` | Database User |
|
| `POSTGRES_USERNAME` | string | `automatisch_user` | Database User |
|
||||||
| `POSTGRES_PASSWORD` | string | | Password of Database User |
|
| `POSTGRES_PASSWORD` | string | | Password of Database User |
|
||||||
| `ENCRYPTION_KEY` | string | | Encryption Key to store credentials |
|
| `ENCRYPTION_KEY` | string | | Encryption Key to store credentials |
|
||||||
| `WEBHOOK_SECRET_KEY` | string | | Webhook Secret Key to verify webhook requests |
|
| `WEBHOOK_SECRET_KEY` | string | | Webhook Secret Key to verify webhook requests |
|
||||||
| `APP_SECRET_KEY` | string | | Secret Key to authenticate the user |
|
| `APP_SECRET_KEY` | string | | Secret Key to authenticate the user |
|
||||||
| `REDIS_HOST` | string | `redis` | Redis Host |
|
| `REDIS_HOST` | string | `redis` | Redis Host |
|
||||||
| `REDIS_PORT` | number | `6379` | Redis Port |
|
| `REDIS_PORT` | number | `6379` | Redis Port |
|
||||||
| `REDIS_USERNAME` | string | | Redis Username |
|
| `REDIS_USERNAME` | string | | Redis Username |
|
||||||
| `REDIS_PASSWORD` | string | | Redis Password |
|
| `REDIS_PASSWORD` | string | | Redis Password |
|
||||||
| `REDIS_TLS` | boolean | `false` | Redis TLS |
|
| `REDIS_TLS` | boolean | `false` | Redis TLS |
|
||||||
| `TELEMETRY_ENABLED` | boolean | `true` | Enable/Disable Telemetry |
|
| `TELEMETRY_ENABLED` | boolean | `true` | Enable/Disable Telemetry |
|
||||||
| `ENABLE_BULLMQ_DASHBOARD` | boolean | `false` | Enable BullMQ Dashboard |
|
| `ENABLE_BULLMQ_DASHBOARD` | boolean | `false` | Enable BullMQ Dashboard |
|
||||||
| `BULLMQ_DASHBOARD_USERNAME` | string | | Username to login BullMQ Dashboard |
|
| `BULLMQ_DASHBOARD_USERNAME` | string | | Username to login BullMQ Dashboard |
|
||||||
| `BULLMQ_DASHBOARD_PASSWORD` | string | | Password to login BullMQ Dashboard |
|
| `BULLMQ_DASHBOARD_PASSWORD` | string | | Password to login BullMQ Dashboard |
|
||||||
|
| `DISABLE_NOTIFICATIONS_PAGE` | boolean | `false` | Enable/Disable notifications page |
|
||||||
|
| `DISABLE_FAVICON` | boolean | `false` | Enable/Disable favicon |
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
favicon: /favicons/discord.svg
|
favicon: /favicons/discord.svg
|
||||||
items:
|
items:
|
||||||
|
- name: Change a scheduled event
|
||||||
|
desc: Changes a scheduled event.
|
||||||
- name: Send a message to channel
|
- name: Send a message to channel
|
||||||
desc: Sends a message to a specific channel you specify.
|
desc: Sends a message to a specific channel you specify.
|
||||||
- name: Create a scheduled event
|
- name: Create a scheduled event
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -2,7 +2,6 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#0059F7" />
|
<meta name="theme-color" content="#0059F7" />
|
||||||
<meta
|
<meta
|
||||||
|
@@ -2,13 +2,6 @@
|
|||||||
"short_name": "automatisch",
|
"short_name": "automatisch",
|
||||||
"name": "automatisch",
|
"name": "automatisch",
|
||||||
"description": "Build workflow automation without spending time and money. No code is required.",
|
"description": "Build workflow automation without spending time and money. No code is required.",
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"theme_color": "#000000",
|
"theme_color": "#000000",
|
||||||
|
@@ -15,6 +15,7 @@ import { SvgIconComponent } from '@mui/icons-material';
|
|||||||
import AppBar from 'components/AppBar';
|
import AppBar from 'components/AppBar';
|
||||||
import Drawer from 'components/Drawer';
|
import Drawer from 'components/Drawer';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useCurrentUserAbility from 'hooks/useCurrentUserAbility';
|
import useCurrentUserAbility from 'hooks/useCurrentUserAbility';
|
||||||
|
|
||||||
type SettingsLayoutProps = {
|
type SettingsLayoutProps = {
|
||||||
@@ -86,19 +87,11 @@ function createDrawerLinks({
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawerBottomLinks = [
|
|
||||||
{
|
|
||||||
Icon: ArrowBackIosNewIcon,
|
|
||||||
primary: 'adminSettingsDrawer.goBack',
|
|
||||||
to: '/',
|
|
||||||
dataTest: 'go-back-drawer-link',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function SettingsLayout({
|
export default function SettingsLayout({
|
||||||
children,
|
children,
|
||||||
}: SettingsLayoutProps): React.ReactElement {
|
}: SettingsLayoutProps): React.ReactElement {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
const currentUserAbility = useCurrentUserAbility();
|
const currentUserAbility = useCurrentUserAbility();
|
||||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'));
|
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg'));
|
||||||
const [isDrawerOpen, setDrawerOpen] = React.useState(!matchSmallScreens);
|
const [isDrawerOpen, setDrawerOpen] = React.useState(!matchSmallScreens);
|
||||||
@@ -116,6 +109,15 @@ export default function SettingsLayout({
|
|||||||
canUpdateApp: currentUserAbility.can('update', 'App'),
|
canUpdateApp: currentUserAbility.can('update', 'App'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const drawerBottomLinks = [
|
||||||
|
{
|
||||||
|
Icon: ArrowBackIosNewIcon,
|
||||||
|
primary: formatMessage('adminSettingsDrawer.goBack'),
|
||||||
|
to: '/',
|
||||||
|
dataTest: 'go-back-drawer-link',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppBar
|
<AppBar
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user