Compare commits
21 Commits
add-loadin
...
v0.8.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
067ec2eb9c | ||
![]() |
2d332b32d9 | ||
![]() |
1d9ad2ba86 | ||
![]() |
a28e2177f7 | ||
![]() |
18fe0df691 | ||
![]() |
8e21a06d99 | ||
![]() |
2daf5473bb | ||
![]() |
928ff53adf | ||
![]() |
a71e95e6e5 | ||
![]() |
cb4a54b5cc | ||
![]() |
37e4524156 | ||
![]() |
9ac24ee051 | ||
![]() |
f28ccd559a | ||
![]() |
8e84a93d8e | ||
![]() |
d871dec1b7 | ||
![]() |
b133e1a197 | ||
![]() |
9346a037b9 | ||
![]() |
89facbcddd | ||
![]() |
53fef35638 | ||
![]() |
bfe496a09b | ||
![]() |
0dd444d50b |
@@ -33,32 +33,7 @@ services:
|
|||||||
- '6379:6379'
|
- '6379:6379'
|
||||||
expose:
|
expose:
|
||||||
- 6379
|
- 6379
|
||||||
keycloak:
|
|
||||||
image: quay.io/keycloak/keycloak:21.1
|
|
||||||
restart: always
|
|
||||||
container_name: keycloak
|
|
||||||
environment:
|
|
||||||
- KEYCLOAK_ADMIN=admin
|
|
||||||
- KEYCLOAK_ADMIN_PASSWORD=admin
|
|
||||||
- KC_DB=postgres
|
|
||||||
- KC_DB_URL_HOST=postgres
|
|
||||||
- KC_DB_URL_DATABASE=keycloak
|
|
||||||
- KC_DB_USERNAME=automatisch_user
|
|
||||||
- KC_DB_PASSWORD=automatisch_password
|
|
||||||
- KC_HEALTH_ENABLED=true
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
command: start-dev
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
healthcheck:
|
|
||||||
test: "curl -f http://localhost:8080/health/ready || exit 1"
|
|
||||||
volumes:
|
|
||||||
- keycloak:/opt/keycloak/data/
|
|
||||||
expose:
|
|
||||||
- 8080
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
keycloak:
|
|
||||||
|
@@ -4,7 +4,7 @@ WORKDIR /automatisch
|
|||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apk --no-cache add --virtual build-dependencies python3 build-base && \
|
apk --no-cache add --virtual build-dependencies python3 build-base && \
|
||||||
yarn global add @automatisch/cli@0.7.1 --network-timeout 1000000 && \
|
yarn global add @automatisch/cli@0.8.0 --network-timeout 1000000 && \
|
||||||
rm -rf /usr/local/share/.cache/ && \
|
rm -rf /usr/local/share/.cache/ && \
|
||||||
apk del build-dependencies
|
apk del build-dependencies
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM automatischio/automatisch:0.7.1
|
FROM automatischio/automatisch:0.8.0
|
||||||
WORKDIR /automatisch
|
WORKDIR /automatisch
|
||||||
|
|
||||||
RUN apk add --no-cache openssl dos2unix
|
RUN apk add --no-cache openssl dos2unix
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "0.7.1",
|
"version": "0.8.0",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"command": {
|
"command": {
|
||||||
|
@@ -2,55 +2,18 @@ import appConfig from '../../src/config/app';
|
|||||||
import logger from '../../src/helpers/logger';
|
import logger from '../../src/helpers/logger';
|
||||||
import client from './client';
|
import client from './client';
|
||||||
import User from '../../src/models/user';
|
import User from '../../src/models/user';
|
||||||
import Role from '../../src/models/role';
|
|
||||||
import Permission from '../../src/models/permission';
|
|
||||||
import '../../src/config/orm';
|
import '../../src/config/orm';
|
||||||
|
|
||||||
async function seedPermissionsIfNeeded() {
|
|
||||||
const existingPermissions = await Permission.query().limit(1).first();
|
|
||||||
|
|
||||||
if (!existingPermissions) return;
|
|
||||||
|
|
||||||
const getPermission = (subject: string, actions: string[]) => actions.map(action => ({ subject, action }));
|
|
||||||
|
|
||||||
await Permission.query().insert([
|
|
||||||
...getPermission('Connection', ['create', 'read', 'delete', 'update']),
|
|
||||||
...getPermission('Execution', ['read']),
|
|
||||||
...getPermission('Flow', ['create', 'delete', 'publish', 'read', 'update']),
|
|
||||||
...getPermission('Role', ['create', 'delete', 'read', 'update']),
|
|
||||||
...getPermission('User', ['create', 'delete', 'read', 'update']),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createOrFetchRole() {
|
|
||||||
const role = await Role.query().limit(1).first();
|
|
||||||
|
|
||||||
if (!role) {
|
|
||||||
const createdRole = await Role.query().insertAndFetch({
|
|
||||||
name: 'Admin',
|
|
||||||
key: 'admin',
|
|
||||||
});
|
|
||||||
|
|
||||||
return createdRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createUser(
|
export async function createUser(
|
||||||
email = 'user@automatisch.io',
|
email = 'user@automatisch.io',
|
||||||
password = 'sample'
|
password = 'sample'
|
||||||
) {
|
) {
|
||||||
const UNIQUE_VIOLATION_CODE = '23505';
|
const UNIQUE_VIOLATION_CODE = '23505';
|
||||||
|
|
||||||
await seedPermissionsIfNeeded();
|
|
||||||
|
|
||||||
const role = await createOrFetchRole();
|
|
||||||
const userParams = {
|
const userParams = {
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
fullName: 'Initial admin',
|
fullName: 'Initial admin',
|
||||||
roleId: role.id,
|
role: 'admin',
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@@ -12,7 +12,6 @@ const knexConfig = {
|
|||||||
database: appConfig.postgresDatabase,
|
database: appConfig.postgresDatabase,
|
||||||
ssl: appConfig.postgresEnableSsl,
|
ssl: appConfig.postgresEnableSsl,
|
||||||
},
|
},
|
||||||
asyncStackTraces: appConfig.isDev,
|
|
||||||
searchPath: [appConfig.postgresSchema],
|
searchPath: [appConfig.postgresSchema],
|
||||||
pool: { min: 0, max: 20 },
|
pool: { min: 0, max: 20 },
|
||||||
migrations: {
|
migrations: {
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@automatisch/backend",
|
"name": "@automatisch/backend",
|
||||||
"version": "0.7.1",
|
"version": "0.8.0",
|
||||||
"license": "See LICENSE file",
|
"license": "See LICENSE file",
|
||||||
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "ts-node-dev --watch 'src/graphql/schema.graphql' --exit-child src/server.ts",
|
"dev": "ts-node-dev --exit-child src/server.ts",
|
||||||
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
|
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
|
||||||
"build": "tsc && yarn copy-statics",
|
"build": "tsc && yarn copy-statics",
|
||||||
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
||||||
@@ -22,17 +22,14 @@
|
|||||||
"prebuild": "rm -rf ./dist"
|
"prebuild": "rm -rf ./dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@automatisch/web": "^0.7.1",
|
"@automatisch/web": "^0.8.0",
|
||||||
"@bull-board/express": "^3.10.1",
|
"@bull-board/express": "^3.10.1",
|
||||||
"@casl/ability": "^6.5.0",
|
|
||||||
"@graphql-tools/graphql-file-loader": "^7.3.4",
|
"@graphql-tools/graphql-file-loader": "^7.3.4",
|
||||||
"@graphql-tools/load": "^7.5.2",
|
"@graphql-tools/load": "^7.5.2",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
|
||||||
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
"@rudderstack/rudder-sdk-node": "^1.1.2",
|
||||||
"@sentry/node": "^7.42.0",
|
"@sentry/node": "^7.42.0",
|
||||||
"@sentry/tracing": "^7.42.0",
|
"@sentry/tracing": "^7.42.0",
|
||||||
"@types/luxon": "^2.3.1",
|
"@types/luxon": "^2.3.1",
|
||||||
"@types/passport": "^1.0.12",
|
|
||||||
"@types/xmlrpc": "^1.3.7",
|
"@types/xmlrpc": "^1.3.7",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
@@ -53,6 +50,8 @@
|
|||||||
"graphql-type-json": "^0.3.2",
|
"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",
|
||||||
|
"https-proxy-agent": "^7.0.1",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"knex": "^2.4.0",
|
"knex": "^2.4.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
@@ -63,7 +62,6 @@
|
|||||||
"nodemailer": "6.7.0",
|
"nodemailer": "6.7.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"objection": "^3.0.0",
|
"objection": "^3.0.0",
|
||||||
"passport": "^0.6.0",
|
|
||||||
"pg": "^8.7.1",
|
"pg": "^8.7.1",
|
||||||
"php-serialize": "^4.0.2",
|
"php-serialize": "^4.0.2",
|
||||||
"stripe": "^11.13.0",
|
"stripe": "^11.13.0",
|
||||||
@@ -106,7 +104,7 @@
|
|||||||
"url": "https://github.com/automatisch/automatisch/issues"
|
"url": "https://github.com/automatisch/automatisch/issues"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@automatisch/types": "^0.7.1",
|
"@automatisch/types": "^0.8.0",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
"@types/bull": "^3.15.8",
|
"@types/bull": "^3.15.8",
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
|
@@ -17,7 +17,6 @@ import {
|
|||||||
} from './helpers/create-bull-board-handler';
|
} from './helpers/create-bull-board-handler';
|
||||||
import injectBullBoardHandler from './helpers/inject-bull-board-handler';
|
import injectBullBoardHandler from './helpers/inject-bull-board-handler';
|
||||||
import router from './routes';
|
import router from './routes';
|
||||||
import configurePassport from './helpers/passport';
|
|
||||||
|
|
||||||
createBullBoardHandler(serverAdapter);
|
createBullBoardHandler(serverAdapter);
|
||||||
|
|
||||||
@@ -51,9 +50,6 @@ app.use(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
app.use(cors(corsOptions));
|
app.use(cors(corsOptions));
|
||||||
|
|
||||||
configurePassport(app);
|
|
||||||
|
|
||||||
app.use('/', router);
|
app.use('/', router);
|
||||||
|
|
||||||
webUIHandler(app);
|
webUIHandler(app);
|
||||||
|
@@ -6,7 +6,7 @@ import actions from './actions';
|
|||||||
import dynamicData from './dynamic-data';
|
import dynamicData from './dynamic-data';
|
||||||
|
|
||||||
export default defineApp({
|
export default defineApp({
|
||||||
name: 'Github',
|
name: 'GitHub',
|
||||||
key: 'github',
|
key: 'github',
|
||||||
baseUrl: 'https://github.com',
|
baseUrl: 'https://github.com',
|
||||||
apiBaseUrl: 'https://api.github.com',
|
apiBaseUrl: 'https://api.github.com',
|
||||||
|
@@ -9,11 +9,11 @@ export default {
|
|||||||
// ref:
|
// ref:
|
||||||
// - https://docs.gitlab.com/ee/api/projects.html#list-all-projects
|
// - https://docs.gitlab.com/ee/api/projects.html#list-all-projects
|
||||||
// - https://docs.gitlab.com/ee/api/rest/index.html#keyset-based-pagination
|
// - https://docs.gitlab.com/ee/api/rest/index.html#keyset-based-pagination
|
||||||
|
|
||||||
const firstPageRequest = $.http.get('/api/v4/projects', {
|
const firstPageRequest = $.http.get('/api/v4/projects', {
|
||||||
params: {
|
params: {
|
||||||
simple: true,
|
simple: true,
|
||||||
pagination: 'keyset',
|
pagination: 'keyset',
|
||||||
|
membership: true,
|
||||||
order_by: 'id',
|
order_by: 'id',
|
||||||
sort: 'asc',
|
sort: 'asc',
|
||||||
},
|
},
|
||||||
|
@@ -6,7 +6,7 @@ import triggers from './triggers';
|
|||||||
import dynamicData from './dynamic-data';
|
import dynamicData from './dynamic-data';
|
||||||
|
|
||||||
export default defineApp({
|
export default defineApp({
|
||||||
name: 'Gitlab',
|
name: 'GitLab',
|
||||||
key: 'gitlab',
|
key: 'gitlab',
|
||||||
baseUrl: 'https://gitlab.com',
|
baseUrl: 'https://gitlab.com',
|
||||||
apiBaseUrl: 'https://gitlab.com',
|
apiBaseUrl: 'https://gitlab.com',
|
||||||
|
@@ -0,0 +1,191 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
type THeaders = {
|
||||||
|
__id: string;
|
||||||
|
header: string;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
type TSheetsResponse = {
|
||||||
|
sheets: {
|
||||||
|
properties: {
|
||||||
|
sheetId: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type TBody = {
|
||||||
|
requests: IJSONObject[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create worksheet',
|
||||||
|
key: 'createWorksheet',
|
||||||
|
description:
|
||||||
|
'Create a blank worksheet with a title. Optionally, provide headers.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Drive',
|
||||||
|
key: 'driveId',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.',
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listDrives',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Spreadsheet',
|
||||||
|
key: 'spreadsheetId',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
dependsOn: ['parameters.driveId'],
|
||||||
|
variables: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listSpreadsheets',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parameters.driveId',
|
||||||
|
value: '{parameters.driveId}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Title',
|
||||||
|
key: 'title',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
description: '',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Headers',
|
||||||
|
key: 'headers',
|
||||||
|
type: 'dynamic' as const,
|
||||||
|
required: false,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: 'Header',
|
||||||
|
key: 'header',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Overwrite',
|
||||||
|
key: 'overwrite',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: false,
|
||||||
|
value: false,
|
||||||
|
description:
|
||||||
|
'If a worksheet with the specified title exists, its content would be lost. Please, use with caution.',
|
||||||
|
variables: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Yes',
|
||||||
|
value: 'true',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'No',
|
||||||
|
value: 'false',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const {
|
||||||
|
data: { sheets },
|
||||||
|
} = await $.http.get<TSheetsResponse>(
|
||||||
|
`/v4/spreadsheets/${$.step.parameters.spreadsheetId}`
|
||||||
|
);
|
||||||
|
const selectedSheet = sheets.find(
|
||||||
|
(sheet) => sheet.properties.title === $.step.parameters.title
|
||||||
|
);
|
||||||
|
const headers = $.step.parameters.headers as THeaders;
|
||||||
|
const values = headers.map((entry) => entry.header);
|
||||||
|
|
||||||
|
const body: TBody = {
|
||||||
|
requests: [
|
||||||
|
{
|
||||||
|
addSheet: {
|
||||||
|
properties: {
|
||||||
|
title: $.step.parameters.title,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($.step.parameters.overwrite === 'true' && selectedSheet) {
|
||||||
|
body.requests.unshift({
|
||||||
|
deleteSheet: {
|
||||||
|
sheetId: selectedSheet.properties.sheetId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await $.http.post(
|
||||||
|
`https://sheets.googleapis.com/v4/spreadsheets/${$.step.parameters.spreadsheetId}:batchUpdate`,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
|
||||||
|
if (values.length) {
|
||||||
|
const body = {
|
||||||
|
requests: [
|
||||||
|
{
|
||||||
|
updateCells: {
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
values: values.map((header) => ({
|
||||||
|
userEnteredValue: { stringValue: header },
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: '*',
|
||||||
|
start: {
|
||||||
|
sheetId:
|
||||||
|
data.replies[data.replies.length - 1].addSheet.properties
|
||||||
|
.sheetId,
|
||||||
|
rowIndex: 0,
|
||||||
|
columnIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data: response } = await $.http.post(
|
||||||
|
`https://sheets.googleapis.com/v4/spreadsheets/${$.step.parameters.spreadsheetId}:batchUpdate`,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: response,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -1,4 +1,5 @@
|
|||||||
import createSpreadsheet from './create-spreadsheet';
|
import createSpreadsheet from './create-spreadsheet';
|
||||||
import createSpreadsheetRow from './create-spreadsheet-row';
|
import createSpreadsheetRow from './create-spreadsheet-row';
|
||||||
|
import createWorksheet from './create-worksheet';
|
||||||
|
|
||||||
export default [createSpreadsheet, createSpreadsheetRow];
|
export default [createSpreadsheet, createSpreadsheetRow, createWorksheet];
|
||||||
|
@@ -0,0 +1,100 @@
|
|||||||
|
import { IJSONArray, IJSONObject } from '@automatisch/types';
|
||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
type TBody = {
|
||||||
|
parent: IJSONObject;
|
||||||
|
properties: IJSONObject;
|
||||||
|
children: IJSONArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create database item',
|
||||||
|
key: 'createDatabaseItem',
|
||||||
|
description: 'Creates an item in a database.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Database',
|
||||||
|
key: 'databaseId',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listDatabases',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Name',
|
||||||
|
key: 'name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'This field has a 2000 character limit. Any characters beyond 2000 will not be included.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Content',
|
||||||
|
key: 'content',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'The text to add to the page body. The max length for this field is 2000 characters. Any characters beyond 2000 will not be included.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const name = $.step.parameters.name as string;
|
||||||
|
const truncatedName = name.slice(0, 2000);
|
||||||
|
const content = $.step.parameters.content as string;
|
||||||
|
const truncatedContent = content.slice(0, 2000);
|
||||||
|
|
||||||
|
const body: TBody = {
|
||||||
|
parent: {
|
||||||
|
database_id: $.step.parameters.databaseId,
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
body.properties.Name = {
|
||||||
|
title: [
|
||||||
|
{
|
||||||
|
text: {
|
||||||
|
content: truncatedName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
body.children = [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
paragraph: {
|
||||||
|
rich_text: [
|
||||||
|
{
|
||||||
|
text: {
|
||||||
|
content: truncatedContent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await $.http.post('/v1/pages', body);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
104
packages/backend/src/apps/notion/actions/create-page/index.ts
Normal file
104
packages/backend/src/apps/notion/actions/create-page/index.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { IJSONArray, IJSONObject } from '@automatisch/types';
|
||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
type TBody = {
|
||||||
|
parent: IJSONObject;
|
||||||
|
properties: IJSONObject;
|
||||||
|
children: IJSONArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Create page',
|
||||||
|
key: 'createPage',
|
||||||
|
description: 'Creates a page inside a parent page',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Parent page',
|
||||||
|
key: 'parentPageId',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listParentPages',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Title',
|
||||||
|
key: 'title',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'This field has a 2000 character limit. Any characters beyond 2000 will not be included.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Content',
|
||||||
|
key: 'content',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'The text to add to the page body. The max length for this field is 2000 characters. Any characters beyond 2000 will not be included.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const parentPageId = $.step.parameters.parentPageId as string;
|
||||||
|
const title = $.step.parameters.title as string;
|
||||||
|
const truncatedTitle = title.slice(0, 2000);
|
||||||
|
const content = $.step.parameters.content as string;
|
||||||
|
const truncatedContent = content.slice(0, 2000);
|
||||||
|
|
||||||
|
const body: TBody = {
|
||||||
|
parent: {
|
||||||
|
page_id: parentPageId,
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
body.properties.title = {
|
||||||
|
type: 'title',
|
||||||
|
title: [
|
||||||
|
{
|
||||||
|
text: {
|
||||||
|
content: truncatedTitle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
body.children = [
|
||||||
|
{
|
||||||
|
object: 'block',
|
||||||
|
type: 'paragraph',
|
||||||
|
paragraph: {
|
||||||
|
rich_text: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
content: truncatedContent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await $.http.post('/v1/pages', body);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@@ -0,0 +1,70 @@
|
|||||||
|
import { IJSONArray, IJSONObject } from '@automatisch/types';
|
||||||
|
import defineAction from '../../../../helpers/define-action';
|
||||||
|
|
||||||
|
type TBody = {
|
||||||
|
filter: IJSONObject;
|
||||||
|
sorts: IJSONArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineAction({
|
||||||
|
name: 'Find database item',
|
||||||
|
key: 'findDatabaseItem',
|
||||||
|
description: 'Searches for an item in a database by property.',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
label: 'Database',
|
||||||
|
key: 'databaseId',
|
||||||
|
type: 'dropdown' as const,
|
||||||
|
required: true,
|
||||||
|
source: {
|
||||||
|
type: 'query',
|
||||||
|
name: 'getDynamicData',
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: 'key',
|
||||||
|
value: 'listDatabases',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Name',
|
||||||
|
key: 'name',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: false,
|
||||||
|
description:
|
||||||
|
'This field has a 2000 character limit. Any characters beyond 2000 will not be included.',
|
||||||
|
variables: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
async run($) {
|
||||||
|
const databaseId = $.step.parameters.databaseId as string;
|
||||||
|
const name = $.step.parameters.name as string;
|
||||||
|
const truncatedName = name.slice(0, 2000);
|
||||||
|
|
||||||
|
const body: TBody = {
|
||||||
|
filter: {
|
||||||
|
property: 'Name',
|
||||||
|
rich_text: {
|
||||||
|
equals: truncatedName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sorts: [
|
||||||
|
{
|
||||||
|
timestamp: 'last_edited_time',
|
||||||
|
direction: 'descending',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await $.http.post(
|
||||||
|
`/v1/databases/${databaseId}/query`,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
|
||||||
|
$.setActionItem({
|
||||||
|
raw: data.results[0],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
5
packages/backend/src/apps/notion/actions/index.ts
Normal file
5
packages/backend/src/apps/notion/actions/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import createDatabaseItem from './create-database-item';
|
||||||
|
import createPage from './create-page';
|
||||||
|
import findDatabaseItem from './find-database-item';
|
||||||
|
|
||||||
|
export default [createDatabaseItem, createPage, findDatabaseItem];
|
@@ -1,3 +1,4 @@
|
|||||||
import listDatabases from './list-databases';
|
import listDatabases from './list-databases';
|
||||||
|
import listParentPages from './list-parent-pages';
|
||||||
|
|
||||||
export default [listDatabases];
|
export default [listDatabases, listParentPages];
|
||||||
|
@@ -0,0 +1,70 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
type Page = {
|
||||||
|
id: string;
|
||||||
|
properties: {
|
||||||
|
title: {
|
||||||
|
title: [
|
||||||
|
{
|
||||||
|
plain_text: string;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
parent: {
|
||||||
|
workspace: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type ResponseData = {
|
||||||
|
results: Page[];
|
||||||
|
next_cursor?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Payload = {
|
||||||
|
filter: {
|
||||||
|
value: 'page';
|
||||||
|
property: 'object';
|
||||||
|
};
|
||||||
|
start_cursor?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'List parent pages',
|
||||||
|
key: 'listParentPages',
|
||||||
|
|
||||||
|
async run($: IGlobalVariable) {
|
||||||
|
const parentPages: {
|
||||||
|
data: IJSONObject[];
|
||||||
|
error: IJSONObject | null;
|
||||||
|
} = {
|
||||||
|
data: [],
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
const payload: Payload = {
|
||||||
|
filter: {
|
||||||
|
value: 'page',
|
||||||
|
property: 'object',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
do {
|
||||||
|
const response = await $.http.post<ResponseData>('/v1/search', payload);
|
||||||
|
|
||||||
|
payload.start_cursor = response.data.next_cursor;
|
||||||
|
|
||||||
|
const topLevelPages = response.data.results.filter(
|
||||||
|
(page) => page.parent.workspace
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const pages of topLevelPages) {
|
||||||
|
parentPages.data.push({
|
||||||
|
value: pages.id as string,
|
||||||
|
name: pages.properties.title.title[0].plain_text as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} while (payload.start_cursor);
|
||||||
|
|
||||||
|
return parentPages;
|
||||||
|
},
|
||||||
|
};
|
@@ -3,6 +3,7 @@ import addAuthHeader from './common/add-auth-header';
|
|||||||
import addNotionVersionHeader from './common/add-notion-version-header';
|
import addNotionVersionHeader from './common/add-notion-version-header';
|
||||||
import auth from './auth';
|
import auth from './auth';
|
||||||
import triggers from './triggers';
|
import triggers from './triggers';
|
||||||
|
import actions from './actions';
|
||||||
import dynamicData from './dynamic-data';
|
import dynamicData from './dynamic-data';
|
||||||
|
|
||||||
export default defineApp({
|
export default defineApp({
|
||||||
@@ -14,11 +15,9 @@ export default defineApp({
|
|||||||
authDocUrl: 'https://automatisch.io/docs/apps/notion/connection',
|
authDocUrl: 'https://automatisch.io/docs/apps/notion/connection',
|
||||||
primaryColor: '000000',
|
primaryColor: '000000',
|
||||||
supportsConnections: true,
|
supportsConnections: true,
|
||||||
beforeRequest: [
|
beforeRequest: [addAuthHeader, addNotionVersionHeader],
|
||||||
addAuthHeader,
|
|
||||||
addNotionVersionHeader,
|
|
||||||
],
|
|
||||||
auth,
|
auth,
|
||||||
triggers,
|
triggers,
|
||||||
|
actions,
|
||||||
dynamicData,
|
dynamicData,
|
||||||
});
|
});
|
||||||
|
@@ -1,32 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
import capitalize from 'lodash/capitalize';
|
|
||||||
import lowerCase from 'lodash/lowerCase';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.createTable('roles', (table) => {
|
|
||||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
|
||||||
table.string('name').notNullable();
|
|
||||||
table.string('key').notNullable();
|
|
||||||
table.string('description');
|
|
||||||
|
|
||||||
table.timestamps(true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
const uniqueUserRoles = await knex('users')
|
|
||||||
.select('role')
|
|
||||||
.groupBy('role');
|
|
||||||
|
|
||||||
for (const { role } of uniqueUserRoles) {
|
|
||||||
// skip empty roles
|
|
||||||
if (!role) continue;
|
|
||||||
|
|
||||||
await knex('roles').insert({
|
|
||||||
name: capitalize(role),
|
|
||||||
key: lowerCase(role),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.dropTable('roles');
|
|
||||||
}
|
|
@@ -1,25 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
const getPermission = (subject: string, actions: string[]) => actions.map(action => ({ subject, action }));
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.createTable('permissions', (table) => {
|
|
||||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
|
||||||
table.string('action').notNullable();
|
|
||||||
table.string('subject').notNullable();
|
|
||||||
|
|
||||||
table.timestamps(true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
await knex('permissions').insert([
|
|
||||||
...getPermission('Connection', ['create', 'read', 'delete', 'update']),
|
|
||||||
...getPermission('Execution', ['read']),
|
|
||||||
...getPermission('Flow', ['create', 'delete', 'publish', 'read', 'update']),
|
|
||||||
...getPermission('Role', ['create', 'delete', 'read', 'update']),
|
|
||||||
...getPermission('User', ['create', 'delete', 'read', 'update']),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.dropTable('permissions');
|
|
||||||
}
|
|
@@ -1,25 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.createTable('roles_permissions', (table) => {
|
|
||||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
|
||||||
table.uuid('role_id').references('id').inTable('roles');
|
|
||||||
table.uuid('permission_id').references('id').inTable('permissions');
|
|
||||||
});
|
|
||||||
|
|
||||||
const roles = await knex('roles').select('id');
|
|
||||||
const permissions = await knex('permissions').select('id');
|
|
||||||
|
|
||||||
for (const role of roles) {
|
|
||||||
for (const permission of permissions) {
|
|
||||||
await knex('roles_permissions').insert({
|
|
||||||
role_id: role.id,
|
|
||||||
permission_id: permission.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.dropTable('roles_permissions');
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.table('users', async (table) => {
|
|
||||||
table.uuid('role_id').references('id').inTable('roles');
|
|
||||||
});
|
|
||||||
|
|
||||||
const theRole = await knex('roles').select('id').limit(1).first();
|
|
||||||
const roles = await knex('roles').select('id', 'key');
|
|
||||||
|
|
||||||
for (const role of roles) {
|
|
||||||
await knex('users')
|
|
||||||
.where({
|
|
||||||
role: role.key
|
|
||||||
})
|
|
||||||
.update({
|
|
||||||
role_id: role.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// backfill not-migratables
|
|
||||||
await knex('users').whereNull('role_id').update({ role_id: theRole.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return await knex.schema.table('users', (table) => {
|
|
||||||
table.dropColumn('role_id');
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.table('users', async (table) => {
|
|
||||||
table.dropColumn('role');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return await knex.schema.table('users', (table) => {
|
|
||||||
table.string('role').defaultTo('user');
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.createTable('saml_auth_providers', (table) => {
|
|
||||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
|
||||||
table.string('name').notNullable();
|
|
||||||
table.text('certificate').notNullable();
|
|
||||||
table.string('signature_algorithm').notNullable();
|
|
||||||
table.string('issuer').notNullable();
|
|
||||||
table.text('entry_point').notNullable();
|
|
||||||
table.text('firstname_attribute_name').notNullable();
|
|
||||||
table.text('surname_attribute_name').notNullable();
|
|
||||||
table.text('email_attribute_name').notNullable();
|
|
||||||
table.text('role_attribute_name').notNullable();
|
|
||||||
table.uuid('default_role_id').references('id').inTable('roles');
|
|
||||||
|
|
||||||
table.timestamps(true, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.dropTable('saml_auth_providers');
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.createTable('identities', (table) => {
|
|
||||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
|
||||||
table.uuid('user_id').references('id').inTable('users');
|
|
||||||
table.string('remote_id').notNullable();
|
|
||||||
table.string('provider_id').notNullable();
|
|
||||||
table.string('provider_type').notNullable();
|
|
||||||
|
|
||||||
table.timestamps(true, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.dropTable('identities');
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
return await knex.schema.alterTable('users', (table) => {
|
|
||||||
table.string('password').nullable().alter();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(): Promise<void> {
|
|
||||||
// void
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
import { Knex } from 'knex';
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.alterTable('permissions', (table) => {
|
|
||||||
table.jsonb('conditions').notNullable().defaultTo([]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
return knex.schema.alterTable('permissions', (table) => {
|
|
||||||
table.dropColumn('conditions');
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,59 +1,47 @@
|
|||||||
import createConnection from './mutations/create-connection';
|
import createConnection from './mutations/create-connection';
|
||||||
import createFlow from './mutations/create-flow';
|
|
||||||
import createRole from './mutations/create-role.ee';
|
|
||||||
import createStep from './mutations/create-step';
|
|
||||||
import createUser from './mutations/create-user.ee';
|
|
||||||
import deleteConnection from './mutations/delete-connection';
|
|
||||||
import deleteCurrentUser from './mutations/delete-current-user.ee';
|
|
||||||
import deleteFlow from './mutations/delete-flow';
|
|
||||||
import deleteRole from './mutations/delete-role.ee';
|
|
||||||
import deleteStep from './mutations/delete-step';
|
|
||||||
import deleteUser from './mutations/delete-user.ee';
|
|
||||||
import duplicateFlow from './mutations/duplicate-flow';
|
|
||||||
import executeFlow from './mutations/execute-flow';
|
|
||||||
import forgotPassword from './mutations/forgot-password.ee';
|
|
||||||
import generateAuthUrl from './mutations/generate-auth-url';
|
import generateAuthUrl from './mutations/generate-auth-url';
|
||||||
import login from './mutations/login';
|
|
||||||
import registerUser from './mutations/register-user.ee';
|
|
||||||
import resetConnection from './mutations/reset-connection';
|
|
||||||
import resetPassword from './mutations/reset-password.ee';
|
|
||||||
import updateConnection from './mutations/update-connection';
|
import updateConnection from './mutations/update-connection';
|
||||||
import updateCurrentUser from './mutations/update-current-user';
|
import resetConnection from './mutations/reset-connection';
|
||||||
|
import verifyConnection from './mutations/verify-connection';
|
||||||
|
import deleteConnection from './mutations/delete-connection';
|
||||||
|
import createFlow from './mutations/create-flow';
|
||||||
import updateFlow from './mutations/update-flow';
|
import updateFlow from './mutations/update-flow';
|
||||||
import updateFlowStatus from './mutations/update-flow-status';
|
import updateFlowStatus from './mutations/update-flow-status';
|
||||||
import updateRole from './mutations/update-role.ee';
|
import executeFlow from './mutations/execute-flow';
|
||||||
|
import deleteFlow from './mutations/delete-flow';
|
||||||
|
import duplicateFlow from './mutations/duplicate-flow';
|
||||||
|
import createStep from './mutations/create-step';
|
||||||
import updateStep from './mutations/update-step';
|
import updateStep from './mutations/update-step';
|
||||||
import updateUser from './mutations/update-user.ee';
|
import deleteStep from './mutations/delete-step';
|
||||||
import verifyConnection from './mutations/verify-connection';
|
import createUser from './mutations/create-user.ee';
|
||||||
|
import deleteUser from './mutations/delete-user.ee';
|
||||||
|
import updateUser from './mutations/update-user';
|
||||||
|
import forgotPassword from './mutations/forgot-password.ee';
|
||||||
|
import resetPassword from './mutations/reset-password.ee';
|
||||||
|
import login from './mutations/login';
|
||||||
|
|
||||||
const mutationResolvers = {
|
const mutationResolvers = {
|
||||||
createConnection,
|
createConnection,
|
||||||
createFlow,
|
|
||||||
createRole,
|
|
||||||
createStep,
|
|
||||||
createUser,
|
|
||||||
deleteConnection,
|
|
||||||
deleteCurrentUser,
|
|
||||||
deleteFlow,
|
|
||||||
deleteRole,
|
|
||||||
deleteStep,
|
|
||||||
deleteUser,
|
|
||||||
duplicateFlow,
|
|
||||||
executeFlow,
|
|
||||||
forgotPassword,
|
|
||||||
generateAuthUrl,
|
generateAuthUrl,
|
||||||
login,
|
|
||||||
registerUser,
|
|
||||||
resetConnection,
|
|
||||||
resetPassword,
|
|
||||||
updateConnection,
|
updateConnection,
|
||||||
updateCurrentUser,
|
resetConnection,
|
||||||
updateUser,
|
verifyConnection,
|
||||||
|
deleteConnection,
|
||||||
|
createFlow,
|
||||||
updateFlow,
|
updateFlow,
|
||||||
updateFlowStatus,
|
updateFlowStatus,
|
||||||
updateRole,
|
executeFlow,
|
||||||
|
deleteFlow,
|
||||||
|
duplicateFlow,
|
||||||
|
createStep,
|
||||||
updateStep,
|
updateStep,
|
||||||
verifyConnection,
|
deleteStep,
|
||||||
|
createUser,
|
||||||
|
deleteUser,
|
||||||
|
updateUser,
|
||||||
|
forgotPassword,
|
||||||
|
resetPassword,
|
||||||
|
login,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mutationResolvers;
|
export default mutationResolvers;
|
||||||
|
@@ -13,8 +13,6 @@ const createConnection = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Connection');
|
|
||||||
|
|
||||||
await App.findOneByKey(params.input.key);
|
await App.findOneByKey(params.input.key);
|
||||||
|
|
||||||
return await context.currentUser.$relatedQuery('connections').insert({
|
return await context.currentUser.$relatedQuery('connections').insert({
|
||||||
|
@@ -14,8 +14,6 @@ const createFlow = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Flow');
|
|
||||||
|
|
||||||
const connectionId = params?.input?.connectionId;
|
const connectionId = params?.input?.connectionId;
|
||||||
const appKey = params?.input?.triggerAppKey;
|
const appKey = params?.input?.triggerAppKey;
|
||||||
|
|
||||||
|
@@ -1,32 +0,0 @@
|
|||||||
import kebabCase from 'lodash/kebabCase';
|
|
||||||
import Role from '../../models/role';
|
|
||||||
import Permission from '../../models/permission';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
input: {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
permissions: Permission[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const createRole = async (_parent: unknown, params: Params) => {
|
|
||||||
const { name, description, permissions } = params.input;
|
|
||||||
const key = kebabCase(name);
|
|
||||||
|
|
||||||
const existingRole = await Role.query().findOne({ key });
|
|
||||||
|
|
||||||
if (existingRole) {
|
|
||||||
throw new Error('Role already exists!');
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Role.query().insertGraph({
|
|
||||||
key,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
permissions,
|
|
||||||
}, { relate: ['permissions'] }).returning('*');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createRole;
|
|
@@ -22,8 +22,6 @@ const createStep = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const { input } = params;
|
const { input } = params;
|
||||||
|
|
||||||
if (input.appKey && input.key) {
|
if (input.appKey && input.key) {
|
||||||
|
@@ -5,13 +5,11 @@ type Params = {
|
|||||||
fullName: string;
|
fullName: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
roleId: string;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const createUser = async (_parent: unknown, params: Params) => {
|
const createUser = async (_parent: unknown, params: Params) => {
|
||||||
const { fullName, email, password, roleId } = params.input;
|
const { fullName, email, password } = params.input;
|
||||||
|
|
||||||
const existingUser = await User.query().findOne({ email });
|
const existingUser = await User.query().findOne({ email });
|
||||||
|
|
||||||
@@ -23,7 +21,7 @@ const createUser = async (_parent: unknown, params: Params) => {
|
|||||||
fullName,
|
fullName,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
roleId,
|
role: 'user',
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
@@ -11,8 +11,6 @@ const deleteConnection = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('delete', 'Connection');
|
|
||||||
|
|
||||||
await context.currentUser
|
await context.currentUser
|
||||||
.$relatedQuery('connections')
|
.$relatedQuery('connections')
|
||||||
.delete()
|
.delete()
|
||||||
|
@@ -1,23 +0,0 @@
|
|||||||
import { Duration } from 'luxon';
|
|
||||||
import Context from '../../types/express/context';
|
|
||||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const deleteUser = async (_parent: unknown, params: never, context: Context) => {
|
|
||||||
const id = context.currentUser.id;
|
|
||||||
|
|
||||||
await context.currentUser.$query().delete();
|
|
||||||
|
|
||||||
const jobName = `Delete user - ${id}`;
|
|
||||||
const jobPayload = { id };
|
|
||||||
const millisecondsFor30Days = Duration.fromObject({ days: 30 }).toMillis();
|
|
||||||
const jobOptions = {
|
|
||||||
delay: millisecondsFor30Days
|
|
||||||
};
|
|
||||||
|
|
||||||
await deleteUserQueue.add(jobName, jobPayload, jobOptions);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default deleteUser;
|
|
@@ -13,8 +13,6 @@ const deleteFlow = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('delete', 'Flow');
|
|
||||||
|
|
||||||
const flow = await context.currentUser
|
const flow = await context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
import Role from '../../models/role';
|
|
||||||
import Context from '../../types/express/context';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
input: {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const deleteRole = async (
|
|
||||||
_parent: unknown,
|
|
||||||
params: Params,
|
|
||||||
context: Context
|
|
||||||
) => {
|
|
||||||
const role = await Role.query().findById(params.input.id).throwIfNotFound();
|
|
||||||
|
|
||||||
if (role.isAdmin) {
|
|
||||||
throw new Error('Admin role cannot be deleted!');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: consider migrations for users that still have the role or
|
|
||||||
* do not let the role get deleted if there are still associated users
|
|
||||||
*/
|
|
||||||
await role.$query().delete();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default deleteRole;
|
|
@@ -11,8 +11,6 @@ const deleteStep = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const step = await context.currentUser
|
const step = await context.currentUser
|
||||||
.$relatedQuery('steps')
|
.$relatedQuery('steps')
|
||||||
.withGraphFetched('flow')
|
.withGraphFetched('flow')
|
||||||
|
@@ -1,23 +1,11 @@
|
|||||||
import { Duration } from 'luxon';
|
|
||||||
import Context from '../../types/express/context';
|
import Context from '../../types/express/context';
|
||||||
import User from '../../models/user';
|
|
||||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
import deleteUserQueue from '../../queues/delete-user.ee';
|
||||||
|
import { Duration } from 'luxon';
|
||||||
|
|
||||||
type Params = {
|
const deleteUser = async (_parent: unknown, params: never, context: Context) => {
|
||||||
input: {
|
const id = context.currentUser.id;
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
await context.currentUser.$query().delete();
|
||||||
const deleteUser = async (
|
|
||||||
_parent: unknown,
|
|
||||||
params: Params,
|
|
||||||
context: Context
|
|
||||||
) => {
|
|
||||||
const id = params.input.id;
|
|
||||||
|
|
||||||
await User.query().deleteById(id);
|
|
||||||
|
|
||||||
const jobName = `Delete user - ${id}`;
|
const jobName = `Delete user - ${id}`;
|
||||||
const jobPayload = { id };
|
const jobPayload = { id };
|
||||||
|
@@ -53,8 +53,6 @@ const duplicateFlow = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Flow');
|
|
||||||
|
|
||||||
const flow = await context.currentUser
|
const flow = await context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.withGraphJoined('[steps]')
|
.withGraphJoined('[steps]')
|
||||||
|
@@ -12,8 +12,6 @@ const executeFlow = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const { stepId } = params.input;
|
const { stepId } = params.input;
|
||||||
|
|
||||||
const untilStep = await context.currentUser
|
const untilStep = await context.currentUser
|
||||||
|
@@ -13,8 +13,6 @@ const generateAuthUrl = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Connection');
|
|
||||||
|
|
||||||
const connection = await context.currentUser
|
const connection = await context.currentUser
|
||||||
.$relatedQuery('connections')
|
.$relatedQuery('connections')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -13,7 +13,7 @@ const TOKEN_EXPIRES_IN = '14d';
|
|||||||
|
|
||||||
const login = async (_parent: unknown, params: Params) => {
|
const login = async (_parent: unknown, params: Params) => {
|
||||||
const user = await User.query().findOne({
|
const user = await User.query().findOne({
|
||||||
email: params.input.email,
|
email: params.input.email.toLowerCase(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user && (await user.login(params.input.password))) {
|
if (user && (await user.login(params.input.password))) {
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
import User from '../../models/user';
|
|
||||||
import Role from '../../models/role';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
input: {
|
|
||||||
fullName: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const registerUser = async (_parent: unknown, params: Params) => {
|
|
||||||
const { fullName, email, password } = params.input;
|
|
||||||
|
|
||||||
const existingUser = await User.query().findOne({ email });
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
throw new Error('User already exists!');
|
|
||||||
}
|
|
||||||
|
|
||||||
const role = await Role.query().findOne({ key: 'user' });
|
|
||||||
|
|
||||||
const user = await User.query().insert({
|
|
||||||
fullName,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
roleId: role.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return user;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default registerUser;
|
|
@@ -11,8 +11,6 @@ const resetConnection = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Connection');
|
|
||||||
|
|
||||||
let connection = await context.currentUser
|
let connection = await context.currentUser
|
||||||
.$relatedQuery('connections')
|
.$relatedQuery('connections')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -13,8 +13,6 @@ const updateConnection = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Connection');
|
|
||||||
|
|
||||||
let connection = await context.currentUser
|
let connection = await context.currentUser
|
||||||
.$relatedQuery('connections')
|
.$relatedQuery('connections')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -18,8 +18,6 @@ const updateFlowStatus = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('publish', 'Flow');
|
|
||||||
|
|
||||||
let flow = await context.currentUser
|
let flow = await context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.findOne({
|
.findOne({
|
||||||
@@ -57,7 +55,7 @@ const updateFlowStatus = async (
|
|||||||
} else {
|
} else {
|
||||||
if (newActiveValue) {
|
if (newActiveValue) {
|
||||||
flow = await flow.$query().patchAndFetch({
|
flow = await flow.$query().patchAndFetch({
|
||||||
publishedAt: new Date().toISOString(),
|
published_at: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const jobName = `${JOB_NAME}-${flow.id}`;
|
const jobName = `${JOB_NAME}-${flow.id}`;
|
||||||
@@ -80,12 +78,9 @@ const updateFlowStatus = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flow = await flow
|
flow = await flow.$query().withGraphFetched('steps').patchAndFetch({
|
||||||
.$query()
|
active: newActiveValue,
|
||||||
.withGraphFetched('steps')
|
});
|
||||||
.patchAndFetch({
|
|
||||||
active: newActiveValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
return flow;
|
return flow;
|
||||||
};
|
};
|
||||||
|
@@ -12,8 +12,6 @@ const updateFlow = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
let flow = await context.currentUser
|
let flow = await context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
import Context from '../../types/express/context';
|
|
||||||
import Role from '../../models/role';
|
|
||||||
import Permission from '../../models/permission';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
input: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
permissions: Permission[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const updateUser = async (
|
|
||||||
_parent: unknown,
|
|
||||||
params: Params,
|
|
||||||
context: Context
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
permissions,
|
|
||||||
} = params.input;
|
|
||||||
|
|
||||||
const role = await Role.query().findById(id).throwIfNotFound();
|
|
||||||
|
|
||||||
// TODO: delete the unrelated items!
|
|
||||||
await role.$relatedQuery('permissions').unrelate();
|
|
||||||
|
|
||||||
// TODO: possibly assert that given permissions do actually exist in catalog
|
|
||||||
// TODO: possibly optimize it with patching the different permissions compared to current ones
|
|
||||||
return await role.$query()
|
|
||||||
.patchAndFetch(
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
permissions,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateUser;
|
|
@@ -23,8 +23,6 @@ const updateStep = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const { input } = params;
|
const { input } = params;
|
||||||
|
|
||||||
let step = await context.currentUser
|
let step = await context.currentUser
|
||||||
|
@@ -1,34 +0,0 @@
|
|||||||
import Context from '../../types/express/context';
|
|
||||||
import User from '../../models/user';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
input: {
|
|
||||||
id: string;
|
|
||||||
email: string;
|
|
||||||
fullName: string;
|
|
||||||
role: {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const updateUser = async (
|
|
||||||
_parent: unknown,
|
|
||||||
params: Params,
|
|
||||||
context: Context
|
|
||||||
) => {
|
|
||||||
const user = await User.query()
|
|
||||||
.patchAndFetchById(
|
|
||||||
params.input.id,
|
|
||||||
{
|
|
||||||
email: params.input.email,
|
|
||||||
fullName: params.input.fullName,
|
|
||||||
roleId: params.input.role.id,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return user;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default updateUser;
|
|
@@ -8,7 +8,7 @@ type Params = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateCurrentUser = async (
|
const updateUser = async (
|
||||||
_parent: unknown,
|
_parent: unknown,
|
||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
@@ -22,4 +22,4 @@ const updateCurrentUser = async (
|
|||||||
return user;
|
return user;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default updateCurrentUser;
|
export default updateUser;
|
@@ -13,8 +13,6 @@ const verifyConnection = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('create', 'Connection');
|
|
||||||
|
|
||||||
let connection = await context.currentUser
|
let connection = await context.currentUser
|
||||||
.$relatedQuery('connections')
|
.$relatedQuery('connections')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -6,8 +6,6 @@ type Params = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getApp = async (_parent: unknown, params: Params, context: Context) => {
|
const getApp = async (_parent: unknown, params: Params, context: Context) => {
|
||||||
context.currentUser.can('read', 'Connection');
|
|
||||||
|
|
||||||
const app = await App.findOneByKey(params.key);
|
const app = await App.findOneByKey(params.key);
|
||||||
|
|
||||||
if (context.currentUser) {
|
if (context.currentUser) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { IApp } from '@automatisch/types';
|
|
||||||
import App from '../../models/app';
|
import App from '../../models/app';
|
||||||
|
import { IApp } from '@automatisch/types';
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { IConnection } from '@automatisch/types';
|
|
||||||
import App from '../../models/app';
|
import App from '../../models/app';
|
||||||
import Context from '../../types/express/context';
|
import Context from '../../types/express/context';
|
||||||
|
import { IApp, IConnection } from '@automatisch/types';
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -11,8 +11,6 @@ const getConnectedApps = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('read', 'Connection');
|
|
||||||
|
|
||||||
let apps = await App.findAll(params.name);
|
let apps = await App.findAll(params.name);
|
||||||
|
|
||||||
const connections = await context.currentUser
|
const connections = await context.currentUser
|
||||||
|
@@ -16,8 +16,6 @@ const getDynamicData = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const step = await context.currentUser
|
const step = await context.currentUser
|
||||||
.$relatedQuery('steps')
|
.$relatedQuery('steps')
|
||||||
.withGraphFetched({
|
.withGraphFetched({
|
||||||
|
@@ -14,8 +14,6 @@ const getDynamicFields = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const step = await context.currentUser
|
const step = await context.currentUser
|
||||||
.$relatedQuery('steps')
|
.$relatedQuery('steps')
|
||||||
.withGraphFetched({
|
.withGraphFetched({
|
||||||
|
@@ -12,8 +12,6 @@ const getExecutionSteps = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('read', 'Execution');
|
|
||||||
|
|
||||||
const execution = await context.currentUser
|
const execution = await context.currentUser
|
||||||
.$relatedQuery('executions')
|
.$relatedQuery('executions')
|
||||||
.withSoftDeleted()
|
.withSoftDeleted()
|
||||||
|
@@ -9,8 +9,6 @@ const getExecution = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('read', 'Execution');
|
|
||||||
|
|
||||||
const execution = await context.currentUser
|
const execution = await context.currentUser
|
||||||
.$relatedQuery('executions')
|
.$relatedQuery('executions')
|
||||||
.withGraphFetched({
|
.withGraphFetched({
|
||||||
|
@@ -12,8 +12,6 @@ const getExecutions = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('read', 'Execution');
|
|
||||||
|
|
||||||
const selectStatusStatement = `
|
const selectStatusStatement = `
|
||||||
case
|
case
|
||||||
when count(*) filter (where execution_steps.status = 'failure') > 0
|
when count(*) filter (where execution_steps.status = 'failure') > 0
|
||||||
|
@@ -5,8 +5,6 @@ type Params = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getFlow = async (_parent: unknown, params: Params, context: Context) => {
|
const getFlow = async (_parent: unknown, params: Params, context: Context) => {
|
||||||
context.currentUser.can('read', 'Flow');
|
|
||||||
|
|
||||||
const flow = await context.currentUser
|
const flow = await context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.withGraphJoined('[steps.[connection]]')
|
.withGraphJoined('[steps.[connection]]')
|
||||||
|
@@ -10,8 +10,6 @@ type Params = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getFlows = async (_parent: unknown, params: Params, context: Context) => {
|
const getFlows = async (_parent: unknown, params: Params, context: Context) => {
|
||||||
context.currentUser.can('read', 'Flow');
|
|
||||||
|
|
||||||
const flowsQuery = context.currentUser
|
const flowsQuery = context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.joinRelated({
|
.joinRelated({
|
||||||
|
@@ -1,76 +0,0 @@
|
|||||||
const getPermissions = async () => {
|
|
||||||
const Connection = {
|
|
||||||
label: 'Connection',
|
|
||||||
key: 'Connection',
|
|
||||||
};
|
|
||||||
|
|
||||||
const Flow = {
|
|
||||||
label: 'Flow',
|
|
||||||
key: 'Flow',
|
|
||||||
};
|
|
||||||
|
|
||||||
const Execution = {
|
|
||||||
label: 'Execution',
|
|
||||||
key: 'Execution',
|
|
||||||
};
|
|
||||||
|
|
||||||
const permissions = {
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
key: 'isCreator',
|
|
||||||
label: 'Is creator'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: 'Create',
|
|
||||||
action: 'create',
|
|
||||||
subjects: [
|
|
||||||
Connection.key,
|
|
||||||
Flow.key,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Read',
|
|
||||||
action: 'read',
|
|
||||||
subjects: [
|
|
||||||
Connection.key,
|
|
||||||
Execution.key,
|
|
||||||
Flow.key,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Update',
|
|
||||||
action: 'update',
|
|
||||||
subjects: [
|
|
||||||
Connection.key,
|
|
||||||
Flow.key,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Delete',
|
|
||||||
action: 'delete',
|
|
||||||
subjects: [
|
|
||||||
Connection.key,
|
|
||||||
Flow.key,
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Publish',
|
|
||||||
action: 'publish',
|
|
||||||
subjects: [
|
|
||||||
Flow.key,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
subjects: [
|
|
||||||
Connection,
|
|
||||||
Flow,
|
|
||||||
Execution
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return permissions;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getPermissions;
|
|
@@ -1,13 +0,0 @@
|
|||||||
import Context from '../../types/express/context';
|
|
||||||
import Role from '../../models/role';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
id: string
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const getRole = async (_parent: unknown, params: Params, context: Context) => {
|
|
||||||
return await Role.query().findById(params.id).throwIfNotFound();
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getRole;
|
|
@@ -1,9 +0,0 @@
|
|||||||
import Context from '../../types/express/context';
|
|
||||||
import Role from '../../models/role';
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const getRoles = async (_parent: unknown, params: unknown, context: Context) => {
|
|
||||||
return await Role.query();
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getRoles;
|
|
@@ -1,9 +0,0 @@
|
|||||||
import SamlAuthProvider from '../../models/saml-auth-provider.ee';
|
|
||||||
|
|
||||||
const getSamlAuthProviders = async () => {
|
|
||||||
const providers = await SamlAuthProvider.query();
|
|
||||||
|
|
||||||
return providers;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getSamlAuthProviders;
|
|
@@ -11,8 +11,6 @@ const getStepWithTestExecutions = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Flow');
|
|
||||||
|
|
||||||
const step = await context.currentUser
|
const step = await context.currentUser
|
||||||
.$relatedQuery('steps')
|
.$relatedQuery('steps')
|
||||||
.findOne({ 'steps.id': params.stepId })
|
.findOne({ 'steps.id': params.stepId })
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
import Context from '../../types/express/context';
|
|
||||||
import User from '../../models/user';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
id: string
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const getUser = async (_parent: unknown, params: Params, context: Context) => {
|
|
||||||
return await User
|
|
||||||
.query()
|
|
||||||
.leftJoinRelated({
|
|
||||||
role: true
|
|
||||||
})
|
|
||||||
.withGraphFetched({
|
|
||||||
role: true
|
|
||||||
})
|
|
||||||
.findById(params.id)
|
|
||||||
.throwIfNotFound();
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getUser;
|
|
@@ -1,25 +0,0 @@
|
|||||||
import Context from '../../types/express/context';
|
|
||||||
import paginate from '../../helpers/pagination';
|
|
||||||
import User from '../../models/user';
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
limit: number;
|
|
||||||
offset: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: access
|
|
||||||
const getUsers = async (_parent: unknown, params: Params, context: Context) => {
|
|
||||||
const usersQuery = User
|
|
||||||
.query()
|
|
||||||
.leftJoinRelated({
|
|
||||||
role: true
|
|
||||||
})
|
|
||||||
.withGraphFetched({
|
|
||||||
role: true
|
|
||||||
})
|
|
||||||
.orderBy('full_name', 'desc');
|
|
||||||
|
|
||||||
return paginate(usersQuery, params.limit, params.offset);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getUsers;
|
|
@@ -12,8 +12,6 @@ const testConnection = async (
|
|||||||
params: Params,
|
params: Params,
|
||||||
context: Context
|
context: Context
|
||||||
) => {
|
) => {
|
||||||
context.currentUser.can('update', 'Connection');
|
|
||||||
|
|
||||||
let connection = await context.currentUser
|
let connection = await context.currentUser
|
||||||
.$relatedQuery('connections')
|
.$relatedQuery('connections')
|
||||||
.findOne({
|
.findOne({
|
||||||
|
@@ -1,59 +1,47 @@
|
|||||||
import getApp from './queries/get-app';
|
|
||||||
import getApps from './queries/get-apps';
|
import getApps from './queries/get-apps';
|
||||||
import getAutomatischInfo from './queries/get-automatisch-info';
|
import getApp from './queries/get-app';
|
||||||
import getBillingAndUsage from './queries/get-billing-and-usage.ee';
|
|
||||||
import getConnectedApps from './queries/get-connected-apps';
|
import getConnectedApps from './queries/get-connected-apps';
|
||||||
import getCurrentUser from './queries/get-current-user';
|
import testConnection from './queries/test-connection';
|
||||||
import getDynamicData from './queries/get-dynamic-data';
|
|
||||||
import getDynamicFields from './queries/get-dynamic-fields';
|
|
||||||
import getExecution from './queries/get-execution';
|
|
||||||
import getExecutionSteps from './queries/get-execution-steps';
|
|
||||||
import getExecutions from './queries/get-executions';
|
|
||||||
import getFlow from './queries/get-flow';
|
import getFlow from './queries/get-flow';
|
||||||
import getFlows from './queries/get-flows';
|
import getFlows from './queries/get-flows';
|
||||||
import getUser from './queries/get-user';
|
|
||||||
import getUsers from './queries/get-users';
|
|
||||||
import getInvoices from './queries/get-invoices.ee';
|
|
||||||
import getPaddleInfo from './queries/get-paddle-info.ee';
|
|
||||||
import getPaymentPlans from './queries/get-payment-plans.ee';
|
|
||||||
import getPermissions from './queries/get-permissions.ee';
|
|
||||||
import getRole from './queries/get-role.ee';
|
|
||||||
import getRoles from './queries/get-roles.ee';
|
|
||||||
import getSamlAuthProviders from './queries/get-saml-auth-providers.ee';
|
|
||||||
import getStepWithTestExecutions from './queries/get-step-with-test-executions';
|
import getStepWithTestExecutions from './queries/get-step-with-test-executions';
|
||||||
import getSubscriptionStatus from './queries/get-subscription-status.ee';
|
import getExecution from './queries/get-execution';
|
||||||
|
import getExecutions from './queries/get-executions';
|
||||||
|
import getExecutionSteps from './queries/get-execution-steps';
|
||||||
|
import getDynamicData from './queries/get-dynamic-data';
|
||||||
|
import getDynamicFields from './queries/get-dynamic-fields';
|
||||||
|
import getCurrentUser from './queries/get-current-user';
|
||||||
|
import getPaymentPlans from './queries/get-payment-plans.ee';
|
||||||
|
import getPaddleInfo from './queries/get-paddle-info.ee';
|
||||||
|
import getBillingAndUsage from './queries/get-billing-and-usage.ee';
|
||||||
|
import getInvoices from './queries/get-invoices.ee';
|
||||||
|
import getAutomatischInfo from './queries/get-automatisch-info';
|
||||||
import getTrialStatus from './queries/get-trial-status.ee';
|
import getTrialStatus from './queries/get-trial-status.ee';
|
||||||
|
import getSubscriptionStatus from './queries/get-subscription-status.ee';
|
||||||
import healthcheck from './queries/healthcheck';
|
import healthcheck from './queries/healthcheck';
|
||||||
import testConnection from './queries/test-connection';
|
|
||||||
|
|
||||||
const queryResolvers = {
|
const queryResolvers = {
|
||||||
getApp,
|
|
||||||
getApps,
|
getApps,
|
||||||
getAutomatischInfo,
|
getApp,
|
||||||
getBillingAndUsage,
|
|
||||||
getConnectedApps,
|
getConnectedApps,
|
||||||
getCurrentUser,
|
testConnection,
|
||||||
getDynamicData,
|
getFlow,
|
||||||
getDynamicFields,
|
getFlows,
|
||||||
|
getStepWithTestExecutions,
|
||||||
getExecution,
|
getExecution,
|
||||||
getExecutions,
|
getExecutions,
|
||||||
getExecutionSteps,
|
getExecutionSteps,
|
||||||
getFlow,
|
getDynamicData,
|
||||||
getFlows,
|
getDynamicFields,
|
||||||
getInvoices,
|
getCurrentUser,
|
||||||
getPaddleInfo,
|
|
||||||
getPaymentPlans,
|
getPaymentPlans,
|
||||||
getPermissions,
|
getPaddleInfo,
|
||||||
getRole,
|
getBillingAndUsage,
|
||||||
getRoles,
|
getInvoices,
|
||||||
getSamlAuthProviders,
|
getAutomatischInfo,
|
||||||
getStepWithTestExecutions,
|
|
||||||
getSubscriptionStatus,
|
|
||||||
getTrialStatus,
|
getTrialStatus,
|
||||||
getUser,
|
getSubscriptionStatus,
|
||||||
getUsers,
|
|
||||||
healthcheck,
|
healthcheck,
|
||||||
testConnection,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default queryResolvers;
|
export default queryResolvers;
|
||||||
|
@@ -41,46 +41,31 @@ type Query {
|
|||||||
getAutomatischInfo: GetAutomatischInfo
|
getAutomatischInfo: GetAutomatischInfo
|
||||||
getTrialStatus: GetTrialStatus
|
getTrialStatus: GetTrialStatus
|
||||||
getSubscriptionStatus: GetSubscriptionStatus
|
getSubscriptionStatus: GetSubscriptionStatus
|
||||||
getSamlAuthProviders: [GetSamlAuthProviders]
|
|
||||||
getUsers(
|
|
||||||
limit: Int!
|
|
||||||
offset: Int!
|
|
||||||
): UserConnection
|
|
||||||
getUser(id: String!): User
|
|
||||||
getRoles: [Role]
|
|
||||||
getRole(id: String!): Role
|
|
||||||
getPermissions: Permissions
|
|
||||||
healthcheck: AppHealth
|
healthcheck: AppHealth
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createConnection(input: CreateConnectionInput): Connection
|
createConnection(input: CreateConnectionInput): Connection
|
||||||
createFlow(input: CreateFlowInput): Flow
|
|
||||||
createRole(input: CreateRoleInput): Role
|
|
||||||
createStep(input: CreateStepInput): Step
|
|
||||||
createUser(input: CreateUserInput): User
|
|
||||||
deleteConnection(input: DeleteConnectionInput): Boolean
|
|
||||||
deleteCurrentUser: Boolean
|
|
||||||
deleteFlow(input: DeleteFlowInput): Boolean
|
|
||||||
deleteRole(input: DeleteRoleInput): Boolean
|
|
||||||
deleteStep(input: DeleteStepInput): Step
|
|
||||||
deleteUser(input: DeleteUserInput): Boolean
|
|
||||||
duplicateFlow(input: DuplicateFlowInput): Flow
|
|
||||||
executeFlow(input: ExecuteFlowInput): executeFlowType
|
|
||||||
forgotPassword(input: ForgotPasswordInput): Boolean
|
|
||||||
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
|
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
|
||||||
login(input: LoginInput): Auth
|
|
||||||
registerUser(input: RegisterUserInput): User
|
|
||||||
resetConnection(input: ResetConnectionInput): Connection
|
|
||||||
resetPassword(input: ResetPasswordInput): Boolean
|
|
||||||
updateConnection(input: UpdateConnectionInput): Connection
|
updateConnection(input: UpdateConnectionInput): Connection
|
||||||
updateCurrentUser(input: UpdateCurrentUserInput): User
|
resetConnection(input: ResetConnectionInput): Connection
|
||||||
|
verifyConnection(input: VerifyConnectionInput): Connection
|
||||||
|
deleteConnection(input: DeleteConnectionInput): Boolean
|
||||||
|
createFlow(input: CreateFlowInput): Flow
|
||||||
updateFlow(input: UpdateFlowInput): Flow
|
updateFlow(input: UpdateFlowInput): Flow
|
||||||
updateFlowStatus(input: UpdateFlowStatusInput): Flow
|
updateFlowStatus(input: UpdateFlowStatusInput): Flow
|
||||||
updateRole(input: UpdateRoleInput): Role
|
executeFlow(input: ExecuteFlowInput): executeFlowType
|
||||||
|
deleteFlow(input: DeleteFlowInput): Boolean
|
||||||
|
duplicateFlow(input: DuplicateFlowInput): Flow
|
||||||
|
createStep(input: CreateStepInput): Step
|
||||||
updateStep(input: UpdateStepInput): Step
|
updateStep(input: UpdateStepInput): Step
|
||||||
|
deleteStep(input: DeleteStepInput): Step
|
||||||
|
createUser(input: CreateUserInput): User
|
||||||
|
deleteUser: Boolean
|
||||||
updateUser(input: UpdateUserInput): User
|
updateUser(input: UpdateUserInput): User
|
||||||
verifyConnection(input: VerifyConnectionInput): Connection
|
forgotPassword(input: ForgotPasswordInput): Boolean
|
||||||
|
resetPassword(input: ResetPasswordInput): Boolean
|
||||||
|
login(input: LoginInput): Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -292,15 +277,6 @@ type Execution {
|
|||||||
flow: Flow
|
flow: Flow
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserConnection {
|
|
||||||
edges: [UserEdge]
|
|
||||||
pageInfo: PageInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserEdge {
|
|
||||||
node: User
|
|
||||||
}
|
|
||||||
|
|
||||||
input CreateConnectionInput {
|
input CreateConnectionInput {
|
||||||
key: String!
|
key: String!
|
||||||
formattedData: JSONObject!
|
formattedData: JSONObject!
|
||||||
@@ -384,31 +360,9 @@ input CreateUserInput {
|
|||||||
fullName: String!
|
fullName: String!
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
role: UserRoleInput!
|
|
||||||
}
|
|
||||||
|
|
||||||
input UserRoleInput {
|
|
||||||
id: String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateUserInput {
|
input UpdateUserInput {
|
||||||
id: String!
|
|
||||||
fullName: String
|
|
||||||
email: String
|
|
||||||
role: UserRoleInput
|
|
||||||
}
|
|
||||||
|
|
||||||
input DeleteUserInput {
|
|
||||||
id: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input RegisterUserInput {
|
|
||||||
fullName: String!
|
|
||||||
email: String!
|
|
||||||
password: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input UpdateCurrentUserInput {
|
|
||||||
email: String
|
email: String
|
||||||
password: String
|
password: String
|
||||||
fullName: String
|
fullName: String
|
||||||
@@ -428,29 +382,6 @@ input LoginInput {
|
|||||||
password: String!
|
password: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input PermissionInput {
|
|
||||||
action: String!
|
|
||||||
subject: String!
|
|
||||||
conditions: [String]
|
|
||||||
}
|
|
||||||
|
|
||||||
input CreateRoleInput {
|
|
||||||
name: String!
|
|
||||||
description: String
|
|
||||||
permissions: [PermissionInput]
|
|
||||||
}
|
|
||||||
|
|
||||||
input UpdateRoleInput {
|
|
||||||
id: String!
|
|
||||||
name: String!
|
|
||||||
description: String
|
|
||||||
permissions: [PermissionInput]
|
|
||||||
}
|
|
||||||
|
|
||||||
input DeleteRoleInput {
|
|
||||||
id: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
|
The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
|
||||||
"""
|
"""
|
||||||
@@ -522,21 +453,11 @@ type User {
|
|||||||
id: String
|
id: String
|
||||||
fullName: String
|
fullName: String
|
||||||
email: String
|
email: String
|
||||||
role: Role
|
role: String
|
||||||
permissions: [Permission]
|
|
||||||
createdAt: String
|
createdAt: String
|
||||||
updatedAt: String
|
updatedAt: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type Role {
|
|
||||||
id: String
|
|
||||||
name: String
|
|
||||||
key: String
|
|
||||||
description: String
|
|
||||||
isAdmin: Boolean
|
|
||||||
permissions: [Permission]
|
|
||||||
}
|
|
||||||
|
|
||||||
type PageInfo {
|
type PageInfo {
|
||||||
currentPage: Int!
|
currentPage: Int!
|
||||||
totalPages: Int!
|
totalPages: Int!
|
||||||
@@ -633,41 +554,6 @@ type PaymentPlan {
|
|||||||
productId: String
|
productId: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetSamlAuthProviders {
|
|
||||||
id: String
|
|
||||||
name: String
|
|
||||||
issuer: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Permission {
|
|
||||||
action: String
|
|
||||||
subject: String
|
|
||||||
conditions: [String]
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: emphasize it's a catalog item
|
|
||||||
type Permissions {
|
|
||||||
actions: [Action]
|
|
||||||
subjects: [Subject]
|
|
||||||
conditions: [Condition]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Action {
|
|
||||||
label: String
|
|
||||||
action: String
|
|
||||||
subjects: [String]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Condition {
|
|
||||||
key: String
|
|
||||||
label: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Subject {
|
|
||||||
label: String
|
|
||||||
key: String
|
|
||||||
}
|
|
||||||
|
|
||||||
schema {
|
schema {
|
||||||
query: Query
|
query: Query
|
||||||
mutation: Mutation
|
mutation: Mutation
|
||||||
|
@@ -12,17 +12,7 @@ const isAuthenticated = rule()(async (_parent, _args, req) => {
|
|||||||
const { userId } = jwt.verify(token, appConfig.appSecretKey) as {
|
const { userId } = jwt.verify(token, appConfig.appSecretKey) as {
|
||||||
userId: string;
|
userId: string;
|
||||||
};
|
};
|
||||||
req.currentUser = await User
|
req.currentUser = await User.query().findById(userId).throwIfNotFound();
|
||||||
.query()
|
|
||||||
.findById(userId)
|
|
||||||
.leftJoinRelated({
|
|
||||||
role: true,
|
|
||||||
permissions: true,
|
|
||||||
})
|
|
||||||
.withGraphFetched({
|
|
||||||
role: true,
|
|
||||||
permissions: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -35,14 +25,13 @@ const authentication = shield(
|
|||||||
Query: {
|
Query: {
|
||||||
'*': isAuthenticated,
|
'*': isAuthenticated,
|
||||||
getAutomatischInfo: allow,
|
getAutomatischInfo: allow,
|
||||||
getSamlAuthProviders: allow,
|
|
||||||
healthcheck: allow,
|
healthcheck: allow,
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
'*': isAuthenticated,
|
'*': isAuthenticated,
|
||||||
registerUser: allow,
|
|
||||||
forgotPassword: allow,
|
|
||||||
login: allow,
|
login: allow,
|
||||||
|
createUser: allow,
|
||||||
|
forgotPassword: allow,
|
||||||
resetPassword: allow,
|
resetPassword: allow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
24
packages/backend/src/helpers/axios-with-proxy.ts
Normal file
24
packages/backend/src/helpers/axios-with-proxy.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||||
|
import { HttpProxyAgent } from 'http-proxy-agent';
|
||||||
|
|
||||||
|
const config: AxiosRequestConfig = {};
|
||||||
|
const httpProxyUrl = process.env.http_proxy;
|
||||||
|
const httpsProxyUrl = process.env.https_proxy;
|
||||||
|
const supportsProxy = httpProxyUrl || httpsProxyUrl;
|
||||||
|
|
||||||
|
if (supportsProxy) {
|
||||||
|
if (httpProxyUrl) {
|
||||||
|
config.httpAgent = new HttpProxyAgent(process.env.http_proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpsProxyUrl) {
|
||||||
|
config.httpsAgent = new HttpsProxyAgent(process.env.https_proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.proxy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const axiosWithProxyInstance = axios.create(config);
|
||||||
|
|
||||||
|
export default axiosWithProxyInstance;
|
@@ -1,3 +1,4 @@
|
|||||||
|
// TODO: replace with axios-with-proxy when needed
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import appConfig from '../../config/app';
|
import appConfig from '../../config/app';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
// TODO: replace with axios-with-proxy
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import appConfig from '../config/app';
|
import appConfig from '../config/app';
|
||||||
import memoryCache from 'memory-cache';
|
import memoryCache from 'memory-cache';
|
||||||
|
@@ -3,7 +3,7 @@ import ExecutionStep from '../models/execution-step';
|
|||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
|
|
||||||
// INFO: don't remove space in allowed character group!
|
// INFO: don't remove space in allowed character group!
|
||||||
const variableRegExp = /({{step\.[\da-zA-Z-]+(?:\.[\da-zA-Z-_ ]+)+}})/g;
|
const variableRegExp = /({{step\.[\da-zA-Z-]+(?:\.[\da-zA-Z-_ :]+)+}})/g;
|
||||||
|
|
||||||
export default function computeParameters(
|
export default function computeParameters(
|
||||||
parameters: Step['parameters'],
|
parameters: Step['parameters'],
|
||||||
@@ -42,7 +42,7 @@ export default function computeParameters(
|
|||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
[key]: value.map(item => computeParameters(item, executionSteps)),
|
[key]: value.map((item) => computeParameters(item, executionSteps)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,14 +0,0 @@
|
|||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import appConfig from '../config/app';
|
|
||||||
|
|
||||||
const TOKEN_EXPIRES_IN = '14d';
|
|
||||||
|
|
||||||
const createAuthTokenByUserId = (userId: string) => {
|
|
||||||
const token = jwt.sign({ userId }, appConfig.appSecretKey, {
|
|
||||||
expiresIn: TOKEN_EXPIRES_IN,
|
|
||||||
});
|
|
||||||
|
|
||||||
return token;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createAuthTokenByUserId;
|
|
@@ -1,48 +0,0 @@
|
|||||||
import SamlAuthProvider from '../models/saml-auth-provider.ee';
|
|
||||||
import User from '../models/user';
|
|
||||||
import Identity from '../models/identity.ee';
|
|
||||||
|
|
||||||
const getUser = (user: Record<string, unknown>, providerConfig: SamlAuthProvider) => ({
|
|
||||||
name: user[providerConfig.firstnameAttributeName],
|
|
||||||
surname: user[providerConfig.surnameAttributeName],
|
|
||||||
id: user.nameID,
|
|
||||||
email: user[providerConfig.emailAttributeName],
|
|
||||||
role: user[providerConfig.roleAttributeName],
|
|
||||||
})
|
|
||||||
|
|
||||||
const findOrCreateUserBySamlIdentity = async (userIdentity: Record<string, unknown>, samlAuthProvider: SamlAuthProvider) => {
|
|
||||||
const mappedUser = getUser(userIdentity, samlAuthProvider);
|
|
||||||
const identity = await Identity.query().findOne({
|
|
||||||
remote_id: mappedUser.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (identity) {
|
|
||||||
const user = await identity.$relatedQuery('user');
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createdUser = await User.query().insertGraph({
|
|
||||||
fullName: [
|
|
||||||
mappedUser.name,
|
|
||||||
mappedUser.surname
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(' '),
|
|
||||||
email: mappedUser.email as string,
|
|
||||||
roleId: samlAuthProvider.defaultRoleId,
|
|
||||||
identities: [
|
|
||||||
{
|
|
||||||
remoteId: mappedUser.id as string,
|
|
||||||
providerId: samlAuthProvider.id,
|
|
||||||
providerType: 'saml'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
relate: ['identities']
|
|
||||||
}).returning('*');
|
|
||||||
|
|
||||||
return createdUser;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default findOrCreateUserBySamlIdentity;
|
|
@@ -1,8 +1,10 @@
|
|||||||
import axios, { AxiosRequestConfig } from 'axios';
|
|
||||||
export { AxiosInstance as IHttpClient } from 'axios';
|
|
||||||
import { IHttpClientParams } from '@automatisch/types';
|
import { IHttpClientParams } from '@automatisch/types';
|
||||||
import { URL } from 'url';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
|
import { URL } from 'node:url';
|
||||||
|
export { AxiosInstance as IHttpClient } from 'axios';
|
||||||
|
|
||||||
import HttpError from '../../errors/http';
|
import HttpError from '../../errors/http';
|
||||||
|
import axios from '../axios-with-proxy';
|
||||||
|
|
||||||
const removeBaseUrlForAbsoluteUrls = (
|
const removeBaseUrlForAbsoluteUrls = (
|
||||||
requestConfig: AxiosRequestConfig
|
requestConfig: AxiosRequestConfig
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import ExtendedQueryBuilder from '../models/query-builder';
|
import ExtendedQueryBuilder from '../models/query-builder';
|
||||||
import type Base from '../models/base';
|
|
||||||
|
|
||||||
const paginate = async (
|
const paginate = async (
|
||||||
query: ExtendedQueryBuilder<Model, Model[]>,
|
query: ExtendedQueryBuilder<Model, Model[]>,
|
||||||
limit: number,
|
limit: number,
|
||||||
offset: number,
|
offset: number
|
||||||
) => {
|
) => {
|
||||||
if (limit < 1 || limit > 100) {
|
if (limit < 1 || limit > 100) {
|
||||||
throw new Error('Limit must be between 1 and 100');
|
throw new Error('Limit must be between 1 and 100');
|
||||||
@@ -21,9 +20,11 @@ const paginate = async (
|
|||||||
currentPage: Math.ceil(offset / limit + 1),
|
currentPage: Math.ceil(offset / limit + 1),
|
||||||
totalPages: Math.ceil(count / limit),
|
totalPages: Math.ceil(count / limit),
|
||||||
},
|
},
|
||||||
edges: records.map((record: Base) => ({
|
edges: records.map((record: Model) => {
|
||||||
node: record,
|
return {
|
||||||
})),
|
node: record,
|
||||||
|
};
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,84 +0,0 @@
|
|||||||
import { URL } from 'node:url';
|
|
||||||
import { IRequest } from '@automatisch/types';
|
|
||||||
import { MultiSamlStrategy } from '@node-saml/passport-saml';
|
|
||||||
import { Express } from 'express';
|
|
||||||
import passport from 'passport';
|
|
||||||
|
|
||||||
import appConfig from '../config/app';
|
|
||||||
import createAuthTokenByUserId from '../helpers/create-auth-token-by-user-id';
|
|
||||||
import SamlAuthProvider from '../models/saml-auth-provider.ee';
|
|
||||||
import findOrCreateUserBySamlIdentity from './find-or-create-user-by-saml-identity.ee'
|
|
||||||
|
|
||||||
export default function configurePassport(app: Express) {
|
|
||||||
app.use(passport.initialize({
|
|
||||||
userProperty: 'currentUser',
|
|
||||||
}));
|
|
||||||
|
|
||||||
passport.use(new MultiSamlStrategy(
|
|
||||||
{
|
|
||||||
passReqToCallback: true,
|
|
||||||
getSamlOptions: async function (request, done) {
|
|
||||||
const { issuer } = request.params;
|
|
||||||
const notFoundIssuer = new Error('Issuer cannot be found!');
|
|
||||||
|
|
||||||
if (!issuer) return done(notFoundIssuer);
|
|
||||||
|
|
||||||
const authProvider = await SamlAuthProvider.query().findOne({
|
|
||||||
issuer: request.params.issuer as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!authProvider) {
|
|
||||||
return done(notFoundIssuer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return done(null, authProvider.config);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async function (request, user: Record<string, unknown>, done) {
|
|
||||||
const { issuer } = request.params;
|
|
||||||
const notFoundIssuer = new Error('Issuer cannot be found!');
|
|
||||||
|
|
||||||
if (!issuer) return done(notFoundIssuer);
|
|
||||||
|
|
||||||
const authProvider = await SamlAuthProvider.query().findOne({
|
|
||||||
issuer: request.params.issuer as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!authProvider) {
|
|
||||||
return done(notFoundIssuer);
|
|
||||||
}
|
|
||||||
|
|
||||||
const foundUserWithIdentity = await findOrCreateUserBySamlIdentity(user, authProvider);
|
|
||||||
return done(null, foundUserWithIdentity as unknown as Record<string, unknown>);
|
|
||||||
},
|
|
||||||
function (request, user: Record<string, unknown>, done: (error: any, user: Record<string, unknown>) => void) {
|
|
||||||
return done(null, null);
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
app.get('/login/saml/:issuer',
|
|
||||||
passport.authenticate('saml',
|
|
||||||
{
|
|
||||||
session: false,
|
|
||||||
successRedirect: '/',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
app.post(
|
|
||||||
'/login/saml/:issuer/callback',
|
|
||||||
passport.authenticate('saml', {
|
|
||||||
session: false,
|
|
||||||
failureRedirect: '/',
|
|
||||||
failureFlash: true,
|
|
||||||
}),
|
|
||||||
(req: IRequest, res) => {
|
|
||||||
const token = createAuthTokenByUserId(req.currentUser.id);
|
|
||||||
|
|
||||||
const redirectUrl = new URL(
|
|
||||||
`/login/callback?token=${token}`,
|
|
||||||
appConfig.webAppUrl,
|
|
||||||
).toString();
|
|
||||||
res.redirect(redirectUrl);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
@@ -40,9 +40,6 @@ class Connection extends Base {
|
|||||||
userId: { type: 'string', format: 'uuid' },
|
userId: { type: 'string', format: 'uuid' },
|
||||||
verified: { type: 'boolean', default: false },
|
verified: { type: 'boolean', default: false },
|
||||||
draft: { type: 'boolean' },
|
draft: { type: 'boolean' },
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -31,9 +31,6 @@ class ExecutionStep extends Base {
|
|||||||
dataOut: { type: ['object', 'null'] },
|
dataOut: { type: ['object', 'null'] },
|
||||||
status: { type: 'string', enum: ['success', 'failure'] },
|
status: { type: 'string', enum: ['success', 'failure'] },
|
||||||
errorDetails: { type: ['object', 'null'] },
|
errorDetails: { type: ['object', 'null'] },
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -22,9 +22,6 @@ class Execution extends Base {
|
|||||||
flowId: { type: 'string', format: 'uuid' },
|
flowId: { type: 'string', format: 'uuid' },
|
||||||
testRun: { type: 'boolean', default: false },
|
testRun: { type: 'boolean', default: false },
|
||||||
internalId: { type: 'string' },
|
internalId: { type: 'string' },
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ class Flow extends Base {
|
|||||||
status: 'paused' | 'published' | 'draft';
|
status: 'paused' | 'published' | 'draft';
|
||||||
steps: Step[];
|
steps: Step[];
|
||||||
triggerStep: Step;
|
triggerStep: Step;
|
||||||
publishedAt: string;
|
published_at: string;
|
||||||
remoteWebhookId: string;
|
remoteWebhookId: string;
|
||||||
executions?: Execution[];
|
executions?: Execution[];
|
||||||
lastExecution?: Execution;
|
lastExecution?: Execution;
|
||||||
@@ -37,10 +37,6 @@ class Flow extends Base {
|
|||||||
userId: { type: 'string', format: 'uuid' },
|
userId: { type: 'string', format: 'uuid' },
|
||||||
remoteWebhookId: { type: 'string' },
|
remoteWebhookId: { type: 'string' },
|
||||||
active: { type: 'boolean' },
|
active: { type: 'boolean' },
|
||||||
publishedAt: { type: 'string' },
|
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
import Base from './base';
|
|
||||||
import SamlAuthProvider from './saml-auth-provider.ee';
|
|
||||||
import User from './user';
|
|
||||||
|
|
||||||
class Identity extends Base {
|
|
||||||
id!: string;
|
|
||||||
remoteId!: string;
|
|
||||||
userId!: string;
|
|
||||||
providerId!: string;
|
|
||||||
providerType!: 'saml';
|
|
||||||
|
|
||||||
static tableName = 'identities';
|
|
||||||
|
|
||||||
static jsonSchema = {
|
|
||||||
type: 'object',
|
|
||||||
required: [
|
|
||||||
'providerId',
|
|
||||||
'remoteId',
|
|
||||||
'userId',
|
|
||||||
'providerType',
|
|
||||||
],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
id: { type: 'string', format: 'uuid' },
|
|
||||||
userId: { type: 'string', format: 'uuid' },
|
|
||||||
remoteId: { type: 'string', minLength: 1 },
|
|
||||||
providerId: { type: 'string', format: 'uuid' },
|
|
||||||
providerType: { type: 'string', enum: ['saml'] },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static relationMappings = () => ({
|
|
||||||
user: {
|
|
||||||
relation: Base.BelongsToOneRelation,
|
|
||||||
modelClass: User,
|
|
||||||
join: {
|
|
||||||
from: 'users.id',
|
|
||||||
to: 'identities.user_id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
samlAuthProvider: {
|
|
||||||
relation: Base.BelongsToOneRelation,
|
|
||||||
modelClass: SamlAuthProvider,
|
|
||||||
join: {
|
|
||||||
from: 'saml_auth_providers.id',
|
|
||||||
to: 'identities.provider_id'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Identity;
|
|
@@ -1,26 +0,0 @@
|
|||||||
import Base from './base';
|
|
||||||
|
|
||||||
class Permission extends Base {
|
|
||||||
id: string;
|
|
||||||
action: string;
|
|
||||||
subject: string;
|
|
||||||
conditions: string[];
|
|
||||||
|
|
||||||
static tableName = 'permissions';
|
|
||||||
|
|
||||||
static jsonSchema = {
|
|
||||||
type: 'object',
|
|
||||||
required: ['action', 'subject'],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
id: { type: 'string', format: 'uuid' },
|
|
||||||
action: { type: 'string', minLength: 1 },
|
|
||||||
subject: { type: 'string', minLength: 1 },
|
|
||||||
conditions: { type: 'array', items: { type: 'string' } },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Permission;
|
|
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Model,
|
Model,
|
||||||
Page,
|
Page,
|
||||||
ModelClass,
|
|
||||||
PartialModelObject,
|
PartialModelObject,
|
||||||
ForClassMethod,
|
ForClassMethod,
|
||||||
AnyQueryBuilder,
|
AnyQueryBuilder,
|
||||||
@@ -9,10 +8,6 @@ import {
|
|||||||
|
|
||||||
const DELETED_COLUMN_NAME = 'deleted_at';
|
const DELETED_COLUMN_NAME = 'deleted_at';
|
||||||
|
|
||||||
const supportsSoftDeletion = (modelClass: ModelClass<any>) => {
|
|
||||||
return modelClass.jsonSchema.properties.deletedAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildQueryBuidlerForClass = (): ForClassMethod => {
|
const buildQueryBuidlerForClass = (): ForClassMethod => {
|
||||||
return (modelClass) => {
|
return (modelClass) => {
|
||||||
const qb: AnyQueryBuilder = Model.QueryBuilder.forClass.call(
|
const qb: AnyQueryBuilder = Model.QueryBuilder.forClass.call(
|
||||||
@@ -20,7 +15,7 @@ const buildQueryBuidlerForClass = (): ForClassMethod => {
|
|||||||
modelClass
|
modelClass
|
||||||
);
|
);
|
||||||
qb.onBuild((builder) => {
|
qb.onBuild((builder) => {
|
||||||
if (!builder.context().withSoftDeleted && supportsSoftDeletion(qb.modelClass())) {
|
if (!builder.context().withSoftDeleted) {
|
||||||
builder.whereNull(
|
builder.whereNull(
|
||||||
`${qb.modelClass().tableName}.${DELETED_COLUMN_NAME}`
|
`${qb.modelClass().tableName}.${DELETED_COLUMN_NAME}`
|
||||||
);
|
);
|
||||||
@@ -43,13 +38,9 @@ class ExtendedQueryBuilder<M extends Model, R = M[]> extends Model.QueryBuilder<
|
|||||||
static forClass: ForClassMethod = buildQueryBuidlerForClass();
|
static forClass: ForClassMethod = buildQueryBuidlerForClass();
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
if (supportsSoftDeletion(this.modelClass())) {
|
return this.patch({
|
||||||
return this.patch({
|
[DELETED_COLUMN_NAME]: new Date().toISOString(),
|
||||||
[DELETED_COLUMN_NAME]: new Date().toISOString(),
|
} as unknown as PartialModelObject<M>);
|
||||||
} as unknown as PartialModelObject<M>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.delete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hardDelete() {
|
hardDelete() {
|
||||||
|
@@ -1,61 +0,0 @@
|
|||||||
import Base from './base';
|
|
||||||
import Permission from './permission';
|
|
||||||
import User from './user';
|
|
||||||
|
|
||||||
class Role extends Base {
|
|
||||||
id!: string;
|
|
||||||
name!: string;
|
|
||||||
key: string;
|
|
||||||
description: string;
|
|
||||||
users?: User[];
|
|
||||||
permissions?: Permission[];
|
|
||||||
|
|
||||||
static tableName = 'roles';
|
|
||||||
|
|
||||||
static jsonSchema = {
|
|
||||||
type: 'object',
|
|
||||||
required: ['name', 'key'],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
id: { type: 'string', format: 'uuid' },
|
|
||||||
name: { type: 'string', minLength: 1 },
|
|
||||||
key: { type: 'string', minLength: 1 },
|
|
||||||
description: { type: ['string', 'null'], minLength: 1, maxLength: 255 },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static get virtualAttributes() {
|
|
||||||
return ['isAdmin'];
|
|
||||||
}
|
|
||||||
|
|
||||||
static relationMappings = () => ({
|
|
||||||
users: {
|
|
||||||
relation: Base.HasManyRelation,
|
|
||||||
modelClass: User,
|
|
||||||
join: {
|
|
||||||
from: 'roles.id',
|
|
||||||
to: 'users.role_id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
permissions: {
|
|
||||||
relation: Base.ManyToManyRelation,
|
|
||||||
modelClass: Permission,
|
|
||||||
join: {
|
|
||||||
from: 'roles.id',
|
|
||||||
through: {
|
|
||||||
from: 'roles_permissions.role_id',
|
|
||||||
to: 'roles_permissions.permission_id',
|
|
||||||
},
|
|
||||||
to: 'permissions.id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
get isAdmin() {
|
|
||||||
return this.key === 'admin';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Role;
|
|
@@ -1,79 +0,0 @@
|
|||||||
import { URL } from 'node:url';
|
|
||||||
import type { SamlConfig } from '@node-saml/passport-saml';
|
|
||||||
import appConfig from '../config/app';
|
|
||||||
import Base from './base';
|
|
||||||
import Identity from './identity.ee';
|
|
||||||
|
|
||||||
class SamlAuthProvider extends Base {
|
|
||||||
id!: string;
|
|
||||||
name: string;
|
|
||||||
certificate: string;
|
|
||||||
signatureAlgorithm: SamlConfig["signatureAlgorithm"];
|
|
||||||
issuer: string;
|
|
||||||
entryPoint: string;
|
|
||||||
firstnameAttributeName: string;
|
|
||||||
surnameAttributeName: string;
|
|
||||||
emailAttributeName: string;
|
|
||||||
roleAttributeName: string;
|
|
||||||
defaultRoleId: string;
|
|
||||||
|
|
||||||
static tableName = 'saml_auth_providers';
|
|
||||||
|
|
||||||
static jsonSchema = {
|
|
||||||
type: 'object',
|
|
||||||
required: [
|
|
||||||
'name',
|
|
||||||
'certificate',
|
|
||||||
'signatureAlgorithm',
|
|
||||||
'entryPoint',
|
|
||||||
'issuer',
|
|
||||||
'firstnameAttributeName',
|
|
||||||
'surnameAttributeName',
|
|
||||||
'emailAttributeName',
|
|
||||||
'roleAttributeName',
|
|
||||||
'defaultRoleId',
|
|
||||||
],
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
id: { type: 'string', format: 'uuid' },
|
|
||||||
name: { type: 'string', minLength: 1 },
|
|
||||||
certificate: { type: 'string', minLength: 1 },
|
|
||||||
signatureAlgorithm: { type: 'string', enum: ['sha1', 'sha256', 'sha512'] },
|
|
||||||
issuer: { type: 'string', minLength: 1 },
|
|
||||||
entryPoint: { type: 'string', minLength: 1 },
|
|
||||||
firstnameAttributeName: { type: 'string', minLength: 1 },
|
|
||||||
surnameAttributeName: { type: 'string', minLength: 1 },
|
|
||||||
emailAttributeName: { type: 'string', minLength: 1 },
|
|
||||||
roleAttributeName: { type: 'string', minLength: 1 },
|
|
||||||
defaultRoleId: { type: 'string', format: 'uuid' }
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static relationMappings = () => ({
|
|
||||||
identities: {
|
|
||||||
relation: Base.HasOneRelation,
|
|
||||||
modelClass: Identity,
|
|
||||||
join: {
|
|
||||||
from: 'identities.provider_id',
|
|
||||||
to: 'saml_auth_providers.id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
get config(): SamlConfig {
|
|
||||||
const callbackUrl = new URL(
|
|
||||||
`/login/saml/${this.issuer}/callback`,
|
|
||||||
appConfig.baseUrl
|
|
||||||
).toString();
|
|
||||||
|
|
||||||
return {
|
|
||||||
callbackUrl,
|
|
||||||
cert: this.certificate,
|
|
||||||
entryPoint: this.entryPoint,
|
|
||||||
issuer: this.issuer,
|
|
||||||
signatureAlgorithm: this.signatureAlgorithm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SamlAuthProvider;
|
|
@@ -46,9 +46,6 @@ class Step extends Base {
|
|||||||
position: { type: 'integer' },
|
position: { type: 'integer' },
|
||||||
parameters: { type: 'object' },
|
parameters: { type: 'object' },
|
||||||
webhookPath: { type: ['string', 'null'] },
|
webhookPath: { type: ['string', 'null'] },
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -46,9 +46,6 @@ class Subscription extends Base {
|
|||||||
nextBillDate: { type: 'string' },
|
nextBillDate: { type: 'string' },
|
||||||
lastBillDate: { type: 'string' },
|
lastBillDate: { type: 'string' },
|
||||||
cancellationEffectiveDate: { type: 'string' },
|
cancellationEffectiveDate: { type: 'string' },
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,7 +84,7 @@ class Subscription extends Base {
|
|||||||
return (
|
return (
|
||||||
this.status === 'deleted' &&
|
this.status === 'deleted' &&
|
||||||
Number(this.cancellationEffectiveDate) >
|
Number(this.cancellationEffectiveDate) >
|
||||||
DateTime.now().startOf('day').toMillis()
|
DateTime.now().startOf('day').toMillis()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,9 +24,6 @@ class UsageData extends Base {
|
|||||||
subscriptionId: { type: 'string', format: 'uuid' },
|
subscriptionId: { type: 'string', format: 'uuid' },
|
||||||
consumedTaskCount: { type: 'integer' },
|
consumedTaskCount: { type: 'integer' },
|
||||||
nextResetAt: { type: 'string' },
|
nextResetAt: { type: 'string' },
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,20 +1,14 @@
|
|||||||
import crypto from 'node:crypto';
|
|
||||||
import { QueryContext, ModelOptions } from 'objection';
|
import { QueryContext, ModelOptions } from 'objection';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
|
import crypto from 'crypto';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { PureAbility, fieldPatternMatcher, mongoQueryMatcher } from '@casl/ability';
|
|
||||||
import type { Subject } from '@casl/ability';
|
|
||||||
|
|
||||||
import appConfig from '../config/app';
|
import appConfig from '../config/app';
|
||||||
import Base from './base';
|
import Base from './base';
|
||||||
import ExtendedQueryBuilder from './query-builder';
|
import ExtendedQueryBuilder from './query-builder';
|
||||||
import Connection from './connection';
|
import Connection from './connection';
|
||||||
import Flow from './flow';
|
import Flow from './flow';
|
||||||
import Step from './step';
|
import Step from './step';
|
||||||
import Role from './role';
|
|
||||||
import Permission from './permission';
|
|
||||||
import Execution from './execution';
|
import Execution from './execution';
|
||||||
import Identity from './identity.ee';
|
|
||||||
import UsageData from './usage-data.ee';
|
import UsageData from './usage-data.ee';
|
||||||
import Subscription from './subscription.ee';
|
import Subscription from './subscription.ee';
|
||||||
|
|
||||||
@@ -22,8 +16,8 @@ class User extends Base {
|
|||||||
id!: string;
|
id!: string;
|
||||||
fullName!: string;
|
fullName!: string;
|
||||||
email!: string;
|
email!: string;
|
||||||
roleId: string;
|
|
||||||
password!: string;
|
password!: string;
|
||||||
|
role: string;
|
||||||
resetPasswordToken: string;
|
resetPasswordToken: string;
|
||||||
resetPasswordTokenSentAt: string;
|
resetPasswordTokenSentAt: string;
|
||||||
trialExpiryDate: string;
|
trialExpiryDate: string;
|
||||||
@@ -35,28 +29,19 @@ class User extends Base {
|
|||||||
currentUsageData?: UsageData;
|
currentUsageData?: UsageData;
|
||||||
subscriptions?: Subscription[];
|
subscriptions?: Subscription[];
|
||||||
currentSubscription?: Subscription;
|
currentSubscription?: Subscription;
|
||||||
role: Role;
|
|
||||||
permissions: Permission[];
|
|
||||||
identities: Identity[];
|
|
||||||
|
|
||||||
static tableName = 'users';
|
static tableName = 'users';
|
||||||
|
|
||||||
static jsonSchema = {
|
static jsonSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
required: ['fullName', 'email'],
|
required: ['fullName', 'email', 'password'],
|
||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
id: { type: 'string', format: 'uuid' },
|
id: { type: 'string', format: 'uuid' },
|
||||||
fullName: { type: 'string', minLength: 1 },
|
fullName: { type: 'string', minLength: 1 },
|
||||||
email: { type: 'string', format: 'email', minLength: 1, maxLength: 255 },
|
email: { type: 'string', format: 'email', minLength: 1, maxLength: 255 },
|
||||||
password: { type: 'string' },
|
password: { type: 'string', minLength: 1, maxLength: 255 },
|
||||||
resetPasswordToken: { type: 'string' },
|
role: { type: 'string', enum: ['admin', 'user'] },
|
||||||
resetPasswordTokenSentAt: { type: 'string' },
|
|
||||||
trialExpiryDate: { type: 'string' },
|
|
||||||
roleId: { type: 'string', format: 'uuid' },
|
|
||||||
deletedAt: { type: 'string' },
|
|
||||||
createdAt: { type: 'string' },
|
|
||||||
updatedAt: { type: 'string' },
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,34 +124,6 @@ class User extends Base {
|
|||||||
builder.orderBy('created_at', 'desc').limit(1).first();
|
builder.orderBy('created_at', 'desc').limit(1).first();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
role: {
|
|
||||||
relation: Base.HasOneRelation,
|
|
||||||
modelClass: Role,
|
|
||||||
join: {
|
|
||||||
from: 'roles.id',
|
|
||||||
to: 'users.role_id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
permissions: {
|
|
||||||
relation: Base.ManyToManyRelation,
|
|
||||||
modelClass: Permission,
|
|
||||||
join: {
|
|
||||||
from: 'users.role_id',
|
|
||||||
through: {
|
|
||||||
from: 'roles_permissions.role_id',
|
|
||||||
to: 'roles_permissions.permission_id',
|
|
||||||
},
|
|
||||||
to: 'permissions.id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
identities: {
|
|
||||||
relation: Base.HasManyRelation,
|
|
||||||
modelClass: Identity,
|
|
||||||
join: {
|
|
||||||
from: 'identities.user_id',
|
|
||||||
to: 'users.id',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
login(password: string) {
|
login(password: string) {
|
||||||
@@ -201,9 +158,7 @@ class User extends Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async generateHash() {
|
async generateHash() {
|
||||||
if (this.password) {
|
this.password = await bcrypt.hash(this.password, 10);
|
||||||
this.password = await bcrypt.hash(this.password, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async startTrialPeriod() {
|
async startTrialPeriod() {
|
||||||
@@ -277,7 +232,9 @@ class User extends Base {
|
|||||||
async $beforeUpdate(opt: ModelOptions, queryContext: QueryContext) {
|
async $beforeUpdate(opt: ModelOptions, queryContext: QueryContext) {
|
||||||
await super.$beforeUpdate(opt, queryContext);
|
await super.$beforeUpdate(opt, queryContext);
|
||||||
|
|
||||||
await this.generateHash();
|
if (this.password) {
|
||||||
|
await this.generateHash();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async $afterInsert(queryContext: QueryContext) {
|
async $afterInsert(queryContext: QueryContext) {
|
||||||
@@ -291,34 +248,6 @@ class User extends Base {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get ability() {
|
|
||||||
if (!this.permissions) {
|
|
||||||
throw new Error('User.permissions must be fetched!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're not using mongo, but our fields, conditions match
|
|
||||||
return new PureAbility(this.permissions, {
|
|
||||||
conditionsMatcher: mongoQueryMatcher,
|
|
||||||
fieldMatcher: fieldPatternMatcher
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
can(action: string, subject: Subject) {
|
|
||||||
const can = this.ability.can(action, subject);
|
|
||||||
|
|
||||||
if (!can) throw new Error('Not authorized!');
|
|
||||||
|
|
||||||
return can;
|
|
||||||
}
|
|
||||||
|
|
||||||
cannot(action: string, subject: Subject) {
|
|
||||||
const cannot = this.ability.cannot(action, subject);
|
|
||||||
|
|
||||||
if (cannot) throw new Error('Not authorized!');
|
|
||||||
|
|
||||||
return cannot;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default User;
|
export default User;
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@automatisch/cli",
|
"name": "@automatisch/cli",
|
||||||
"version": "0.7.1",
|
"version": "0.8.0",
|
||||||
"license": "See LICENSE file",
|
"license": "See LICENSE file",
|
||||||
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"version": "oclif readme && git add README.md"
|
"version": "oclif readme && git add README.md"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@automatisch/backend": "^0.7.1",
|
"@automatisch/backend": "^0.8.0",
|
||||||
"@oclif/core": "^1",
|
"@oclif/core": "^1",
|
||||||
"@oclif/plugin-help": "^5",
|
"@oclif/plugin-help": "^5",
|
||||||
"@oclif/plugin-plugins": "^2.0.1",
|
"@oclif/plugin-plugins": "^2.0.1",
|
||||||
|
@@ -15593,11 +15593,6 @@ winston@^3.6.0, winston@^3.7.1:
|
|||||||
triple-beam "^1.3.0"
|
triple-beam "^1.3.0"
|
||||||
winston-transport "^4.5.0"
|
winston-transport "^4.5.0"
|
||||||
|
|
||||||
word-wrap@^1.2.3, word-wrap@~1.2.3:
|
|
||||||
version "1.2.3"
|
|
||||||
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz"
|
|
||||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
|
||||||
|
|
||||||
workbox-background-sync@6.5.4:
|
workbox-background-sync@6.5.4:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz#3141afba3cc8aa2ae14c24d0f6811374ba8ff6a9"
|
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz#3141afba3cc8aa2ae14c24d0f6811374ba8ff6a9"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user