Compare commits
44 Commits
shared-con
...
bin-js
Author | SHA1 | Date | |
---|---|---|---|
![]() |
283c644285 | ||
![]() |
b693c12500 | ||
![]() |
cbe3db8187 | ||
![]() |
7bccbc9471 | ||
![]() |
a452520f1a | ||
![]() |
a331b34b49 | ||
![]() |
8d6f0f8e9e | ||
![]() |
47dd5a1949 | ||
![]() |
387f8fd44c | ||
![]() |
b69b1f6f67 | ||
![]() |
d1427ffd54 | ||
![]() |
00c876dd93 | ||
![]() |
9d1aa9e59a | ||
![]() |
aceebba99a | ||
![]() |
7d6a8c4607 | ||
![]() |
5fd90355ae | ||
![]() |
9b01a2a4da | ||
![]() |
a2986d70a0 | ||
![]() |
5dfa38ca99 | ||
![]() |
157c6812cc | ||
![]() |
215ff4b74a | ||
![]() |
ca7b8b865a | ||
![]() |
fd0b12f6a1 | ||
![]() |
2f3b739f9e | ||
![]() |
3255ddca63 | ||
![]() |
b5460712e6 | ||
![]() |
6f7dcc2b6e | ||
![]() |
70d4800cb1 | ||
![]() |
a8b85cdb0d | ||
![]() |
fe10523972 | ||
![]() |
c975a56245 | ||
![]() |
d070e976b0 | ||
![]() |
0caf6bfabb | ||
![]() |
b842d7938f | ||
![]() |
cebbf84375 | ||
![]() |
8608431490 | ||
![]() |
78ba18b176 | ||
![]() |
f8c30c8526 | ||
![]() |
693c9b85a5 | ||
![]() |
70bb7defd1 | ||
![]() |
160377ca31 | ||
![]() |
2c0ce77a4e | ||
![]() |
77fbb0c9da | ||
![]() |
5971425d23 |
5
.github/workflows/playwright.yml
vendored
5
.github/workflows/playwright.yml
vendored
@@ -4,6 +4,11 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/backend/**'
|
||||
- 'packages/e2e-tests/**'
|
||||
- 'packages/web/**'
|
||||
- '!packages/backend/src/apps/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
|
28
packages/backend/.eslintrc.js
Normal file
28
packages/backend/.eslintrc.js
Normal file
@@ -0,0 +1,28 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.test.ts', '**/test/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': ['off'],
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
@@ -6,10 +6,9 @@ import Role from '../../src/models/role';
|
||||
import '../../src/config/orm';
|
||||
|
||||
async function fetchAdminRole() {
|
||||
const role = await Role
|
||||
.query()
|
||||
const role = await Role.query()
|
||||
.where({
|
||||
key: 'admin'
|
||||
key: 'admin',
|
||||
})
|
||||
.limit(1)
|
||||
.first();
|
||||
@@ -41,7 +40,7 @@ export async function createUser(
|
||||
logger.info('No need to seed a user.');
|
||||
}
|
||||
} catch (err) {
|
||||
if ((err as any).nativeError.code !== UNIQUE_VIOLATION_CODE) {
|
||||
if (err.nativeError.code !== UNIQUE_VIOLATION_CODE) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -68,7 +67,7 @@ export const createDatabase = async (database = appConfig.postgresDatabase) => {
|
||||
await client.query(`CREATE DATABASE ${database}`);
|
||||
logger.info(`Database: ${database} created!`);
|
||||
} catch (err) {
|
||||
if ((err as any).code !== DUPLICATE_DB_CODE) {
|
||||
if (err.code !== DUPLICATE_DB_CODE) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ export const createDatabaseUser = async (user = appConfig.postgresUsername) => {
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
if ((err as any).code !== DUPLICATE_OBJECT_CODE) {
|
||||
if (err.code !== DUPLICATE_OBJECT_CODE) {
|
||||
throw err;
|
||||
}
|
||||
|
@@ -59,8 +59,8 @@
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.1",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"knex": "^2.4.0",
|
||||
"libphonenumber-js": "^1.10.48",
|
||||
"knex": "^2.5.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"luxon": "2.5.2",
|
||||
"memory-cache": "^0.2.0",
|
||||
@@ -69,7 +69,7 @@
|
||||
"node-html-markdown": "^1.3.0",
|
||||
"nodemailer": "6.7.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"objection": "^3.1.1",
|
||||
"objection": "^3.0.0",
|
||||
"passport": "^0.6.0",
|
||||
"pg": "^8.7.1",
|
||||
"php-serialize": "^4.0.2",
|
||||
|
3
packages/backend/src/apps/azure-openai/actions/index.ts
Normal file
3
packages/backend/src/apps/azure-openai/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import sendPrompt from './send-prompt';
|
||||
|
||||
export default [sendPrompt];
|
@@ -0,0 +1,87 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
const castFloatOrUndefined = (value: string | null) => {
|
||||
return value === '' ? undefined : parseFloat(value);
|
||||
}
|
||||
|
||||
export default defineAction({
|
||||
name: 'Send prompt',
|
||||
key: 'sendPrompt',
|
||||
description: 'Creates a completion for the provided prompt and parameters.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Prompt',
|
||||
key: 'prompt',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: 'The text to analyze.'
|
||||
},
|
||||
{
|
||||
label: 'Temperature',
|
||||
key: 'temperature',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'What sampling temperature to use, between 0 and 2. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. We generally recommend altering this or Top P but not both.'
|
||||
},
|
||||
{
|
||||
label: 'Maximum tokens',
|
||||
key: 'maxTokens',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'The maximum number of tokens to generate in the completion.'
|
||||
},
|
||||
{
|
||||
label: 'Stop Sequence',
|
||||
key: 'stopSequence',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'Single stop sequence where the API will stop generating further tokens. The returned text will not contain the stop sequence.'
|
||||
},
|
||||
{
|
||||
label: 'Top P',
|
||||
key: 'topP',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.'
|
||||
},
|
||||
{
|
||||
label: 'Frequency Penalty',
|
||||
key: 'frequencyPenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`
|
||||
},
|
||||
{
|
||||
label: 'Presence Penalty',
|
||||
key: 'presencePenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.`
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const payload = {
|
||||
model: $.step.parameters.model as string,
|
||||
prompt: $.step.parameters.prompt as string,
|
||||
temperature: castFloatOrUndefined($.step.parameters.temperature as string),
|
||||
max_tokens: castFloatOrUndefined($.step.parameters.maxTokens as string),
|
||||
stop: ($.step.parameters.stopSequence as string || null),
|
||||
top_p: castFloatOrUndefined($.step.parameters.topP as string),
|
||||
frequency_penalty: castFloatOrUndefined($.step.parameters.frequencyPenalty as string),
|
||||
presence_penalty: castFloatOrUndefined($.step.parameters.presencePenalty as string),
|
||||
};
|
||||
const { data } = await $.http.post(`/deployments/${$.auth.data.deploymentId}/completions`, payload);
|
||||
|
||||
$.setActionItem({
|
||||
raw: data,
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,6 @@
|
||||
<svg width="256px" height="260px" viewBox="0 0 256 260" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<title>OpenAI</title>
|
||||
<g>
|
||||
<path d="M239.183914,106.202783 C245.054304,88.5242096 243.02228,69.1733805 233.607599,53.0998864 C219.451678,28.4588021 190.999703,15.7836129 163.213007,21.739505 C147.554077,4.32145883 123.794909,-3.42398554 100.87901,1.41873898 C77.9631105,6.26146349 59.3690093,22.9572536 52.0959621,45.2214219 C33.8436494,48.9644867 18.0901721,60.392749 8.86672513,76.5818033 C-5.443491,101.182962 -2.19544431,132.215255 16.8986662,153.320094 C11.0060865,170.990656 13.0197283,190.343991 22.4238231,206.422991 C36.5975553,231.072344 65.0680342,243.746566 92.8695738,237.783372 C105.235639,251.708249 123.001113,259.630942 141.623968,259.52692 C170.105359,259.552169 195.337611,241.165718 204.037777,214.045661 C222.28734,210.296356 238.038489,198.869783 247.267014,182.68528 C261.404453,158.127515 258.142494,127.262775 239.183914,106.202783 L239.183914,106.202783 Z M141.623968,242.541207 C130.255682,242.559177 119.243876,238.574642 110.519381,231.286197 L112.054146,230.416496 L163.724595,200.590881 C166.340648,199.056444 167.954321,196.256818 167.970781,193.224005 L167.970781,120.373788 L189.815614,133.010026 C190.034132,133.121423 190.186235,133.330564 190.224885,133.572774 L190.224885,193.940229 C190.168603,220.758427 168.442166,242.484864 141.623968,242.541207 Z M37.1575749,197.93062 C31.456498,188.086359 29.4094818,176.546984 31.3766237,165.342426 L32.9113895,166.263285 L84.6329973,196.088901 C87.2389349,197.618207 90.4682717,197.618207 93.0742093,196.088901 L156.255402,159.663793 L156.255402,184.885111 C156.243557,185.149771 156.111725,185.394602 155.89729,185.550176 L103.561776,215.733903 C80.3054953,229.131632 50.5924954,221.165435 37.1575749,197.93062 Z M23.5493181,85.3811273 C29.2899861,75.4733097 38.3511911,67.9162648 49.1287482,64.0478825 L49.1287482,125.438515 C49.0891492,128.459425 50.6965386,131.262556 53.3237748,132.754232 L116.198014,169.025864 L94.3531808,181.662102 C94.1132325,181.789434 93.8257461,181.789434 93.5857979,181.662102 L41.3526015,151.529534 C18.1419426,138.076098 10.1817681,108.385562 23.5493181,85.125333 L23.5493181,85.3811273 Z M203.0146,127.075598 L139.935725,90.4458545 L161.7294,77.8607748 C161.969348,77.7334434 162.256834,77.7334434 162.496783,77.8607748 L214.729979,108.044502 C231.032329,117.451747 240.437294,135.426109 238.871504,154.182739 C237.305714,172.939368 225.050719,189.105572 207.414262,195.67963 L207.414262,134.288998 C207.322521,131.276867 205.650697,128.535853 203.0146,127.075598 Z M224.757116,94.3850867 L223.22235,93.4642272 L171.60306,63.3828173 C168.981293,61.8443751 165.732456,61.8443751 163.110689,63.3828173 L99.9806554,99.8079259 L99.9806554,74.5866077 C99.9533004,74.3254088 100.071095,74.0701869 100.287609,73.9215426 L152.520805,43.7889738 C168.863098,34.3743518 189.174256,35.2529043 204.642579,46.0434841 C220.110903,56.8340638 227.949269,75.5923959 224.757116,94.1804513 L224.757116,94.3850867 Z M88.0606409,139.097931 L66.2158076,126.512851 C65.9950399,126.379091 65.8450965,126.154176 65.8065367,125.898945 L65.8065367,65.684966 C65.8314495,46.8285367 76.7500605,29.6846032 93.8270852,21.6883055 C110.90411,13.6920079 131.063833,16.2835462 145.5632,28.338998 L144.028434,29.2086986 L92.3579852,59.0343142 C89.7419327,60.5687513 88.1282597,63.3683767 88.1117998,66.4011901 L88.0606409,139.097931 Z M99.9294965,113.5185 L128.06687,97.3011417 L156.255402,113.5185 L156.255402,145.953218 L128.169187,162.170577 L99.9806554,145.953218 L99.9294965,113.5185 Z" fill="#000000"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
58
packages/backend/src/apps/azure-openai/auth/index.ts
Normal file
58
packages/backend/src/apps/azure-openai/auth/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'screenName',
|
||||
label: 'Screen Name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description:
|
||||
'Screen name of your connection to be used on Automatisch UI.',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'yourResourceName',
|
||||
label: 'Your Resource Name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'The name of your Azure OpenAI Resource.',
|
||||
docUrl: 'https://automatisch.io/docs/azure-openai#your-resource-name',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'deploymentId',
|
||||
label: 'Deployment ID',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'The deployment name you chose when you deployed the model.',
|
||||
docUrl: 'https://automatisch.io/docs/azure-openai#deployment-id',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Azure OpenAI API key of your account.',
|
||||
docUrl: 'https://automatisch.io/docs/azure-openai#api-key',
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
await $.http.get('/fine_tuning/jobs');
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
@@ -0,0 +1,7 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
await $.http.get('/fine_tuning/jobs');
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
@@ -0,0 +1,15 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
if ($.auth.data?.apiKey) {
|
||||
requestConfig.headers['api-key'] = $.auth.data.apiKey as string;
|
||||
}
|
||||
|
||||
requestConfig.params = {
|
||||
'api-version': '2023-10-01-preview'
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
@@ -0,0 +1,13 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
|
||||
const yourResourceName = $.auth.data.yourResourceName as string;
|
||||
|
||||
if (yourResourceName) {
|
||||
requestConfig.baseURL = `https://${yourResourceName}.openai.azure.com/openai`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default setBaseUrl;
|
0
packages/backend/src/apps/azure-openai/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/azure-openai/index.d.ts
vendored
Normal file
19
packages/backend/src/apps/azure-openai/index.ts
Normal file
19
packages/backend/src/apps/azure-openai/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import setBaseUrl from './common/set-base-url';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Azure OpenAI',
|
||||
key: 'azure-openai',
|
||||
baseUrl: 'https://azure.microsoft.com/en-us/products/ai-services/openai-service',
|
||||
apiBaseUrl: '',
|
||||
iconUrl: '{BASE_URL}/apps/azure-openai/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/azure-openai/connection',
|
||||
primaryColor: '000000',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
});
|
3
packages/backend/src/apps/helix/actions/index.ts
Normal file
3
packages/backend/src/apps/helix/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import newChat from './new-chat';
|
||||
|
||||
export default [newChat];
|
50
packages/backend/src/apps/helix/actions/new-chat/index.ts
Normal file
50
packages/backend/src/apps/helix/actions/new-chat/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import FormData from 'form-data';
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'New chat',
|
||||
key: 'newChat',
|
||||
description: 'Create a new chat session for Helix AI.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Input',
|
||||
key: 'input',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Prompt to start the chat with.',
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const formData = new FormData();
|
||||
formData.append('input', $.step.parameters.input as string);
|
||||
formData.append('mode', 'inference');
|
||||
formData.append('type', 'text');
|
||||
|
||||
const sessionResponse = await $.http.post('/api/v1/sessions', formData, {
|
||||
headers: {
|
||||
...formData.getHeaders(),
|
||||
},
|
||||
});
|
||||
|
||||
const sessionId = sessionResponse.data.id;
|
||||
|
||||
let chatGenerated = false;
|
||||
|
||||
while (!chatGenerated) {
|
||||
const response = await $.http.get(`/api/v1/sessions/${sessionId}`);
|
||||
|
||||
const message =
|
||||
response.data.interactions[response.data.interactions.length - 1];
|
||||
|
||||
if (message.creator === 'system' && message.state === 'complete') {
|
||||
$.setActionItem({
|
||||
raw: message,
|
||||
});
|
||||
|
||||
chatGenerated = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
6
packages/backend/src/apps/helix/assets/favicon.svg
Normal file
6
packages/backend/src/apps/helix/assets/favicon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg role="img" viewBox="0 0 47 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.9268 3.41382L15.7675 21.2642L15.5102 52.1134V58.1779C15.4898 58.7861 15.631 59.3888 15.9194 59.9247C16.2078 60.4606 16.6331 60.9105 17.1519 61.2285L24.1843 65.296L25.6299 66.1291C27.7314 67.3481 30.1162 67.9938 32.5456 68.0014C34.975 68.009 37.3638 67.3783 39.4729 66.1724C41.5819 64.9666 43.3372 63.2278 44.563 61.1303C45.7888 59.0328 46.442 56.65 46.4574 54.2206L46.7147 23.3837V17.258C46.7207 16.6458 46.564 16.0429 46.2604 15.5112C45.9569 14.9795 45.5175 14.538 44.9873 14.2319L39.7559 11.218L36.5337 9.36804L21.0602 0.436718C20.5374 0.146102 19.9481 -0.00382494 19.3501 0.00160738C18.752 0.0070397 18.1655 0.167645 17.6482 0.467709C17.1308 0.767772 16.7001 1.19701 16.3984 1.71343C16.0967 2.22985 15.9342 2.81576 15.9268 3.41382Z" fill="#16C3E4" fill-opacity="0.8"></path>
|
||||
<g style="mix-blend-mode:hard-light">
|
||||
<path d="M31.3986 35.0108L31.1636 63.0174C31.1434 63.6524 30.9604 64.2716 30.6322 64.8156C30.304 65.3596 29.8415 65.8101 29.2892 66.1241C28.7368 66.4381 28.1131 66.605 27.4777 66.6088C26.8423 66.6126 26.2166 66.4532 25.6605 66.1458L1.80552 52.1981C1.25401 51.8682 0.797643 51.4008 0.481139 50.8416C0.164634 50.2823 -0.00115693 49.6505 6.0768e-06 49.0079L0.23497 21.1867C0.25531 20.5542 0.437144 19.9374 0.763178 19.395C1.08921 18.8526 1.5486 18.4026 2.09766 18.0879C2.64672 17.7731 3.26719 17.604 3.90005 17.5967C4.53292 17.5894 5.15714 17.744 5.71334 18.046L29.5684 31.8082C30.1263 32.1361 30.5887 32.6041 30.9098 33.1659C31.2309 33.7277 31.3994 34.3637 31.3986 35.0108Z" fill="#C43BBB"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
45
packages/backend/src/apps/helix/auth/index.ts
Normal file
45
packages/backend/src/apps/helix/auth/index.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'screenName',
|
||||
label: 'Screen Name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description:
|
||||
'Screen name of your connection to be used on Automatisch UI.',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'instanceUrl',
|
||||
label: 'Helix instance URL',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
readOnly: false,
|
||||
value: 'https://app.tryhelix.ai',
|
||||
placeholder: 'https://app.tryhelix.ai',
|
||||
description:
|
||||
'Your Helix instance URL. Default is https://app.tryhelix.ai.',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Helix API Key of your account.',
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
};
|
@@ -0,0 +1,9 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
import verifyCredentials from './verify-credentials';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
await verifyCredentials($);
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
11
packages/backend/src/apps/helix/auth/verify-credentials.ts
Normal file
11
packages/backend/src/apps/helix/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
await $.http.get('/api/v1/sessions');
|
||||
|
||||
await $.auth.set({
|
||||
screenName: $.auth.data.screenName,
|
||||
});
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
12
packages/backend/src/apps/helix/common/add-auth-header.ts
Normal file
12
packages/backend/src/apps/helix/common/add-auth-header.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
if ($.auth.data?.apiKey) {
|
||||
const authorizationHeader = `Bearer ${$.auth.data.apiKey}`;
|
||||
requestConfig.headers.Authorization = authorizationHeader;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
13
packages/backend/src/apps/helix/common/set-base-url.ts
Normal file
13
packages/backend/src/apps/helix/common/set-base-url.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
|
||||
if ($.auth.data.instanceUrl) {
|
||||
requestConfig.baseURL = $.auth.data.instanceUrl as string;
|
||||
} else if ($.app.apiBaseUrl) {
|
||||
requestConfig.baseURL = $.app.apiBaseUrl as string;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default setBaseUrl;
|
0
packages/backend/src/apps/helix/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/helix/index.d.ts
vendored
Normal file
19
packages/backend/src/apps/helix/index.ts
Normal file
19
packages/backend/src/apps/helix/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import setBaseUrl from './common/set-base-url';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Helix',
|
||||
key: 'helix',
|
||||
baseUrl: 'https://tryhelix.ai',
|
||||
apiBaseUrl: 'https://app.tryhelix.ai',
|
||||
iconUrl: '{BASE_URL}/apps/helix/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/helix/connection',
|
||||
primaryColor: '000000',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
});
|
@@ -11,7 +11,7 @@ export default {
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Host name of your Odoo Server',
|
||||
description: 'Host name of your Odoo Server (e.g. sub.domain.com without the protocol)',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
@@ -25,6 +25,27 @@ export default {
|
||||
description: 'Port that the host is running on, defaults to 443 (HTTPS)',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'secure',
|
||||
label: 'Secure',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: 'true',
|
||||
description: 'True if the host communicates via secure protocol.',
|
||||
variables: false,
|
||||
clickToCopy: false,
|
||||
options: [
|
||||
{
|
||||
label: 'True',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
label: 'False',
|
||||
value: 'false',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'databaseName',
|
||||
label: 'Database Name',
|
||||
@@ -40,7 +61,7 @@ export default {
|
||||
key: 'email',
|
||||
label: 'Email Address',
|
||||
type: 'string' as const,
|
||||
requires: true,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
|
@@ -32,8 +32,10 @@ export const asyncMethodCall = async <T = number>($: IGlobalVariable, { method,
|
||||
export const getClient = ($: IGlobalVariable, { path = 'common' }) => {
|
||||
const host = $.auth.data.host as string;
|
||||
const port = Number($.auth.data.port as string);
|
||||
const secure = $.auth.data.secure === 'true';
|
||||
const createClientFunction = secure ? xmlrpc.createSecureClient : xmlrpc.createClient;
|
||||
|
||||
return xmlrpc.createClient(
|
||||
return createClientFunction(
|
||||
{
|
||||
host,
|
||||
port,
|
||||
|
@@ -105,7 +105,7 @@ export default defineAction({
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`
|
||||
},
|
||||
{
|
||||
label: 'presencePenalty',
|
||||
label: 'Presence Penalty',
|
||||
key: 'presencePenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
|
@@ -75,7 +75,7 @@ export default defineAction({
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`
|
||||
},
|
||||
{
|
||||
label: 'presencePenalty',
|
||||
label: 'Presence Penalty',
|
||||
key: 'presencePenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
|
@@ -0,0 +1,4 @@
|
||||
import sendPrompt from './send-prompt';
|
||||
import sendChatPrompt from './send-chat-prompt';
|
||||
|
||||
export default [sendChatPrompt, sendPrompt];
|
@@ -0,0 +1,137 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
type TMessage = {
|
||||
role: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
const castFloatOrUndefined = (value: string | null) => {
|
||||
return value === '' ? undefined : parseFloat(value);
|
||||
}
|
||||
|
||||
export default defineAction({
|
||||
name: 'Send chat prompt',
|
||||
key: 'sendChatPrompt',
|
||||
description: 'Creates a completion for the provided prompt and parameters.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Model',
|
||||
key: 'model',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listModels',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Messages',
|
||||
key: 'messages',
|
||||
type: 'dynamic' as const,
|
||||
required: true,
|
||||
description: 'Add or remove messages as needed',
|
||||
value: [{ role: 'system', body: '' }],
|
||||
fields: [
|
||||
{
|
||||
label: 'Role',
|
||||
key: 'role',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
label: 'System',
|
||||
value: 'system',
|
||||
},
|
||||
{
|
||||
label: 'User',
|
||||
value: 'user',
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Content',
|
||||
key: 'content',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Temperature',
|
||||
key: 'temperature',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'What sampling temperature to use. Higher values mean the model will take more risk. Try 0.9 for more creative applications, and 0 for ones with a well-defined answer. We generally recommend altering this or Top P but not both.'
|
||||
},
|
||||
{
|
||||
label: 'Maximum tokens',
|
||||
key: 'maxTokens',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'The maximum number of tokens to generate in the completion.'
|
||||
},
|
||||
{
|
||||
label: 'Stop Sequence',
|
||||
key: 'stopSequence',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'Single stop sequence where the API will stop generating further tokens. The returned text will not contain the stop sequence.'
|
||||
},
|
||||
{
|
||||
label: 'Top P',
|
||||
key: 'topP',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with Top P probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.'
|
||||
},
|
||||
{
|
||||
label: 'Frequency Penalty',
|
||||
key: 'frequencyPenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`
|
||||
},
|
||||
{
|
||||
label: 'Presence Penalty',
|
||||
key: 'presencePenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.`
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const payload = {
|
||||
model: $.step.parameters.model as string,
|
||||
temperature: castFloatOrUndefined($.step.parameters.temperature as string),
|
||||
max_tokens: castFloatOrUndefined($.step.parameters.maxTokens as string),
|
||||
stop: ($.step.parameters.stopSequence as string || null),
|
||||
top_p: castFloatOrUndefined($.step.parameters.topP as string),
|
||||
frequency_penalty: castFloatOrUndefined($.step.parameters.frequencyPenalty as string),
|
||||
presence_penalty: castFloatOrUndefined($.step.parameters.presencePenalty as string),
|
||||
messages: ($.step.parameters.messages as TMessage[]).map(message => ({
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
})),
|
||||
};
|
||||
const { data } = await $.http.post('/v1/chat/completions', payload);
|
||||
|
||||
$.setActionItem({
|
||||
raw: data,
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,104 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
const castFloatOrUndefined = (value: string | null) => {
|
||||
return value === '' ? undefined : parseFloat(value);
|
||||
}
|
||||
|
||||
export default defineAction({
|
||||
name: 'Send prompt',
|
||||
key: 'sendPrompt',
|
||||
description: 'Creates a completion for the provided prompt and parameters.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Model',
|
||||
key: 'model',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listModels',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Prompt',
|
||||
key: 'prompt',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: 'The text to analyze.'
|
||||
},
|
||||
{
|
||||
label: 'Temperature',
|
||||
key: 'temperature',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'What sampling temperature to use. Higher values mean the model will take more risk. Try 0.9 for more creative applications, and 0 for ones with a well-defined answer. We generally recommend altering this or Top P but not both.'
|
||||
},
|
||||
{
|
||||
label: 'Maximum tokens',
|
||||
key: 'maxTokens',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'The maximum number of tokens to generate in the completion.'
|
||||
},
|
||||
{
|
||||
label: 'Stop Sequence',
|
||||
key: 'stopSequence',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'Single stop sequence where the API will stop generating further tokens. The returned text will not contain the stop sequence.'
|
||||
},
|
||||
{
|
||||
label: 'Top P',
|
||||
key: 'topP',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with Top P probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.'
|
||||
},
|
||||
{
|
||||
label: 'Frequency Penalty',
|
||||
key: 'frequencyPenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`
|
||||
},
|
||||
{
|
||||
label: 'Presence Penalty',
|
||||
key: 'presencePenalty',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.`
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const payload = {
|
||||
model: $.step.parameters.model as string,
|
||||
prompt: $.step.parameters.prompt as string,
|
||||
temperature: castFloatOrUndefined($.step.parameters.temperature as string),
|
||||
max_tokens: castFloatOrUndefined($.step.parameters.maxTokens as string),
|
||||
stop: ($.step.parameters.stopSequence as string || null),
|
||||
top_p: castFloatOrUndefined($.step.parameters.topP as string),
|
||||
frequency_penalty: castFloatOrUndefined($.step.parameters.frequencyPenalty as string),
|
||||
presence_penalty: castFloatOrUndefined($.step.parameters.presencePenalty as string),
|
||||
};
|
||||
const { data } = await $.http.post('/v1/completions', payload);
|
||||
|
||||
$.setActionItem({
|
||||
raw: data,
|
||||
});
|
||||
},
|
||||
});
|
@@ -0,0 +1,6 @@
|
||||
<svg width="256px" height="260px" viewBox="0 0 256 260" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<title>OpenAI</title>
|
||||
<g>
|
||||
<path d="M239.183914,106.202783 C245.054304,88.5242096 243.02228,69.1733805 233.607599,53.0998864 C219.451678,28.4588021 190.999703,15.7836129 163.213007,21.739505 C147.554077,4.32145883 123.794909,-3.42398554 100.87901,1.41873898 C77.9631105,6.26146349 59.3690093,22.9572536 52.0959621,45.2214219 C33.8436494,48.9644867 18.0901721,60.392749 8.86672513,76.5818033 C-5.443491,101.182962 -2.19544431,132.215255 16.8986662,153.320094 C11.0060865,170.990656 13.0197283,190.343991 22.4238231,206.422991 C36.5975553,231.072344 65.0680342,243.746566 92.8695738,237.783372 C105.235639,251.708249 123.001113,259.630942 141.623968,259.52692 C170.105359,259.552169 195.337611,241.165718 204.037777,214.045661 C222.28734,210.296356 238.038489,198.869783 247.267014,182.68528 C261.404453,158.127515 258.142494,127.262775 239.183914,106.202783 L239.183914,106.202783 Z M141.623968,242.541207 C130.255682,242.559177 119.243876,238.574642 110.519381,231.286197 L112.054146,230.416496 L163.724595,200.590881 C166.340648,199.056444 167.954321,196.256818 167.970781,193.224005 L167.970781,120.373788 L189.815614,133.010026 C190.034132,133.121423 190.186235,133.330564 190.224885,133.572774 L190.224885,193.940229 C190.168603,220.758427 168.442166,242.484864 141.623968,242.541207 Z M37.1575749,197.93062 C31.456498,188.086359 29.4094818,176.546984 31.3766237,165.342426 L32.9113895,166.263285 L84.6329973,196.088901 C87.2389349,197.618207 90.4682717,197.618207 93.0742093,196.088901 L156.255402,159.663793 L156.255402,184.885111 C156.243557,185.149771 156.111725,185.394602 155.89729,185.550176 L103.561776,215.733903 C80.3054953,229.131632 50.5924954,221.165435 37.1575749,197.93062 Z M23.5493181,85.3811273 C29.2899861,75.4733097 38.3511911,67.9162648 49.1287482,64.0478825 L49.1287482,125.438515 C49.0891492,128.459425 50.6965386,131.262556 53.3237748,132.754232 L116.198014,169.025864 L94.3531808,181.662102 C94.1132325,181.789434 93.8257461,181.789434 93.5857979,181.662102 L41.3526015,151.529534 C18.1419426,138.076098 10.1817681,108.385562 23.5493181,85.125333 L23.5493181,85.3811273 Z M203.0146,127.075598 L139.935725,90.4458545 L161.7294,77.8607748 C161.969348,77.7334434 162.256834,77.7334434 162.496783,77.8607748 L214.729979,108.044502 C231.032329,117.451747 240.437294,135.426109 238.871504,154.182739 C237.305714,172.939368 225.050719,189.105572 207.414262,195.67963 L207.414262,134.288998 C207.322521,131.276867 205.650697,128.535853 203.0146,127.075598 Z M224.757116,94.3850867 L223.22235,93.4642272 L171.60306,63.3828173 C168.981293,61.8443751 165.732456,61.8443751 163.110689,63.3828173 L99.9806554,99.8079259 L99.9806554,74.5866077 C99.9533004,74.3254088 100.071095,74.0701869 100.287609,73.9215426 L152.520805,43.7889738 C168.863098,34.3743518 189.174256,35.2529043 204.642579,46.0434841 C220.110903,56.8340638 227.949269,75.5923959 224.757116,94.1804513 L224.757116,94.3850867 Z M88.0606409,139.097931 L66.2158076,126.512851 C65.9950399,126.379091 65.8450965,126.154176 65.8065367,125.898945 L65.8065367,65.684966 C65.8314495,46.8285367 76.7500605,29.6846032 93.8270852,21.6883055 C110.90411,13.6920079 131.063833,16.2835462 145.5632,28.338998 L144.028434,29.2086986 L92.3579852,59.0343142 C89.7419327,60.5687513 88.1282597,63.3683767 88.1117998,66.4011901 L88.0606409,139.097931 Z M99.9294965,113.5185 L128.06687,97.3011417 L156.255402,113.5185 L156.255402,145.953218 L128.169187,162.170577 L99.9806554,145.953218 L99.9294965,113.5185 Z" fill="#000000"></path>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
44
packages/backend/src/apps/self-hosted-llm/auth/index.ts
Normal file
44
packages/backend/src/apps/self-hosted-llm/auth/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import verifyCredentials from './verify-credentials';
|
||||
import isStillVerified from './is-still-verified';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'screenName',
|
||||
label: 'Screen Name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description:
|
||||
'Screen name of your connection to be used on Automatisch UI.',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'apiUrl',
|
||||
label: 'API URL',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
docUrl: 'https://automatisch.io/docs/self-hosted-llm#api-url',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
docUrl: 'https://automatisch.io/docs/self-hosted-llm#api-key',
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const isStillVerified = async ($: IGlobalVariable) => {
|
||||
const r = await $.http.get('/v1/models');
|
||||
return true;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
@@ -0,0 +1,7 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||
await $.http.get('/v1/models');
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
@@ -0,0 +1,11 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
if ($.auth.data?.apiKey) {
|
||||
requestConfig.headers.Authorization = `Bearer ${$.auth.data.apiKey}`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
@@ -0,0 +1,11 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
|
||||
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
|
||||
if ($.auth.data.apiUrl) {
|
||||
requestConfig.baseURL = $.auth.data.apiUrl as string;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default setBaseUrl;
|
@@ -0,0 +1,3 @@
|
||||
import listModels from './list-models';
|
||||
|
||||
export default [listModels];
|
@@ -0,0 +1,19 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List models',
|
||||
key: 'listModels',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const response = await $.http.get('/v1/models');
|
||||
|
||||
const models = response.data.data.map((model: { id: string }) => {
|
||||
return {
|
||||
value: model.id,
|
||||
name: model.id,
|
||||
};
|
||||
});
|
||||
|
||||
return { data: models };
|
||||
},
|
||||
};
|
0
packages/backend/src/apps/self-hosted-llm/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/self-hosted-llm/index.d.ts
vendored
Normal file
21
packages/backend/src/apps/self-hosted-llm/index.ts
Normal file
21
packages/backend/src/apps/self-hosted-llm/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import setBaseUrl from './common/set-base-url';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
import dynamicData from './dynamic-data';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Self-hosted LLM',
|
||||
key: 'self-hosted-llm',
|
||||
baseUrl: '',
|
||||
apiBaseUrl: '',
|
||||
iconUrl: '{BASE_URL}/apps/self-hosted-llm/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/self-hosted-llm/connection',
|
||||
primaryColor: '000000',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
dynamicData,
|
||||
});
|
@@ -1,6 +1,7 @@
|
||||
import { URL } from 'node:url';
|
||||
import * as dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import process from 'node:process';
|
||||
|
||||
if (process.env.APP_ENV === 'test') {
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../.env.test') });
|
||||
@@ -8,56 +9,6 @@ if (process.env.APP_ENV === 'test') {
|
||||
dotenv.config();
|
||||
}
|
||||
|
||||
type AppConfig = {
|
||||
host: string;
|
||||
protocol: string;
|
||||
port: string;
|
||||
webAppUrl: string;
|
||||
webhookUrl: string;
|
||||
appEnv: string;
|
||||
logLevel: string;
|
||||
isDev: boolean;
|
||||
isTest: boolean;
|
||||
isProd: boolean;
|
||||
postgresDatabase: string;
|
||||
postgresSchema: string;
|
||||
postgresPort: number;
|
||||
postgresHost: string;
|
||||
postgresUsername: string;
|
||||
postgresPassword?: string;
|
||||
version: string;
|
||||
postgresEnableSsl: boolean;
|
||||
baseUrl: string;
|
||||
encryptionKey: string;
|
||||
webhookSecretKey: string;
|
||||
appSecretKey: string;
|
||||
serveWebAppSeparately: boolean;
|
||||
redisHost: string;
|
||||
redisPort: number;
|
||||
redisUsername: string;
|
||||
redisPassword: string;
|
||||
redisTls: boolean;
|
||||
enableBullMQDashboard: boolean;
|
||||
bullMQDashboardUsername: string;
|
||||
bullMQDashboardPassword: string;
|
||||
telemetryEnabled: boolean;
|
||||
requestBodySizeLimit: string;
|
||||
smtpHost: string;
|
||||
smtpPort: number;
|
||||
smtpSecure: boolean;
|
||||
smtpUser: string;
|
||||
smtpPassword: string;
|
||||
fromEmail: string;
|
||||
isCloud: boolean;
|
||||
isSelfHosted: boolean;
|
||||
paddleVendorId: number;
|
||||
paddleVendorAuthCode: string;
|
||||
paddlePublicKey: string;
|
||||
licenseKey: string;
|
||||
sentryDsn: string;
|
||||
CI: boolean;
|
||||
};
|
||||
|
||||
const host = process.env.HOST || 'localhost';
|
||||
const protocol = process.env.PROTOCOL || 'http';
|
||||
const port = process.env.PORT || '3000';
|
||||
@@ -84,7 +35,7 @@ webhookUrl = webhookUrl.substring(0, webhookUrl.length - 1);
|
||||
|
||||
const appEnv = process.env.APP_ENV || 'development';
|
||||
|
||||
const appConfig: AppConfig = {
|
||||
const appConfig = {
|
||||
host,
|
||||
protocol,
|
||||
port,
|
||||
@@ -127,6 +78,7 @@ const appConfig: AppConfig = {
|
||||
fromEmail: process.env.FROM_EMAIL,
|
||||
isCloud: process.env.AUTOMATISCH_CLOUD === 'true',
|
||||
isSelfHosted: process.env.AUTOMATISCH_CLOUD !== 'true',
|
||||
isMation: process.env.MATION === 'true',
|
||||
paddleVendorId: Number(process.env.PADDLE_VENDOR_ID),
|
||||
paddleVendorAuthCode: process.env.PADDLE_VENDOR_AUTH_CODE,
|
||||
paddlePublicKey: process.env.PADDLE_PUBLIC_KEY,
|
@@ -4,11 +4,10 @@ import process from 'process';
|
||||
import pg from 'pg';
|
||||
pg.types.setTypeParser(20, 'text', parseInt);
|
||||
import knex from 'knex';
|
||||
import type { Knex } from 'knex';
|
||||
import knexConfig from '../../knexfile';
|
||||
import logger from '../helpers/logger';
|
||||
|
||||
export const client: Knex = knex(knexConfig);
|
||||
export const client = knex(knexConfig);
|
||||
|
||||
const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||
|
@@ -1,16 +1,6 @@
|
||||
import appConfig from './app';
|
||||
|
||||
type TRedisConfig = {
|
||||
host: string,
|
||||
port: number,
|
||||
username?: string,
|
||||
password?: string,
|
||||
tls?: Record<string, unknown>,
|
||||
enableReadyCheck?: boolean,
|
||||
enableOfflineQueue: boolean,
|
||||
}
|
||||
|
||||
const redisConfig: TRedisConfig = {
|
||||
const redisConfig = {
|
||||
host: appConfig.redisHost,
|
||||
port: appConfig.redisPort,
|
||||
username: appConfig.redisUsername,
|
@@ -1,11 +1,9 @@
|
||||
import { Response } from 'express';
|
||||
import { IJSONObject, IRequest } from '@automatisch/types';
|
||||
import crypto from 'crypto';
|
||||
import { serialize } from 'php-serialize';
|
||||
import Billing from '../../helpers/billing/index.ee';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
export default async (request: IRequest, response: Response) => {
|
||||
export default async (request, response) => {
|
||||
if (!verifyWebhook(request)) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
@@ -23,14 +21,14 @@ export default async (request: IRequest, response: Response) => {
|
||||
return response.sendStatus(200);
|
||||
};
|
||||
|
||||
const verifyWebhook = (request: IRequest) => {
|
||||
const verifyWebhook = (request) => {
|
||||
const signature = request.body.p_signature;
|
||||
|
||||
const keys = Object.keys(request.body)
|
||||
.filter((key) => key !== 'p_signature')
|
||||
.sort();
|
||||
|
||||
const sorted: IJSONObject = {};
|
||||
const sorted = {};
|
||||
keys.forEach((key) => {
|
||||
sorted[key] = request.body[key];
|
||||
});
|
@@ -1,12 +1,10 @@
|
||||
import path from 'node:path';
|
||||
import { Response } from 'express';
|
||||
import { IRequest } from '@automatisch/types';
|
||||
|
||||
import Connection from '../../models/connection';
|
||||
import logger from '../../helpers/logger';
|
||||
import handler from '../../helpers/webhook-handler';
|
||||
|
||||
export default async (request: IRequest, response: Response) => {
|
||||
export default async (request, response) => {
|
||||
const computedRequestPayload = {
|
||||
headers: request.headers,
|
||||
body: request.body,
|
||||
@@ -22,7 +20,7 @@ export default async (request: IRequest, response: Response) => {
|
||||
.findById(connectionId)
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!await connection.verifyWebhook(request)) {
|
||||
if (!(await connection.verifyWebhook(request))) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
|
@@ -1,11 +1,8 @@
|
||||
import { Response } from 'express';
|
||||
import { IRequest } from '@automatisch/types';
|
||||
|
||||
import Flow from '../../models/flow';
|
||||
import logger from '../../helpers/logger';
|
||||
import handler from '../../helpers/webhook-handler';
|
||||
|
||||
export default async (request: IRequest, response: Response) => {
|
||||
export default async (request, response) => {
|
||||
const computedRequestPayload = {
|
||||
headers: request.headers,
|
||||
body: request.body,
|
@@ -1,15 +0,0 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
return knex.schema.createTable('shared_connections', (table) => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
|
||||
table.uuid('connection_id').notNullable().references('id').inTable('connections');
|
||||
table.uuid('role_id').notNullable().references('id').inTable('roles');
|
||||
|
||||
table.timestamps(true, true);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
return knex.schema.dropTable('shared_connections');
|
||||
}
|
@@ -1,22 +1,22 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default class BaseError extends Error {
|
||||
details = {};
|
||||
statusCode?: number;
|
||||
|
||||
constructor(error?: string | IJSONObject) {
|
||||
let computedError: Record<string, unknown>;
|
||||
constructor(error) {
|
||||
let computedError;
|
||||
|
||||
try {
|
||||
computedError = JSON.parse(error as string);
|
||||
computedError = JSON.parse(error);
|
||||
} catch {
|
||||
computedError = (typeof error === 'string' || Array.isArray(error)) ? { error } : error;
|
||||
computedError =
|
||||
typeof error === 'string' || Array.isArray(error) ? { error } : error;
|
||||
}
|
||||
|
||||
let computedMessage: string;
|
||||
let computedMessage;
|
||||
|
||||
try {
|
||||
// challenge to input to see if it is stringified JSON
|
||||
JSON.parse(error as string);
|
||||
computedMessage = error as string;
|
||||
JSON.parse(error);
|
||||
computedMessage = error;
|
||||
} catch {
|
||||
if (typeof error === 'string') {
|
||||
computedMessage = error;
|
10
packages/backend/src/errors/generate-auth-url.js
Normal file
10
packages/backend/src/errors/generate-auth-url.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import BaseError from './base';
|
||||
|
||||
export default class GenerateAuthUrlError extends BaseError {
|
||||
constructor(error) {
|
||||
const computedError = error.response?.data || error.message;
|
||||
super(computedError);
|
||||
|
||||
this.message = `Error occured while creating authorization URL!`;
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import BaseError from './base';
|
||||
|
||||
export default class GenerateAuthUrlError extends BaseError {
|
||||
constructor(error: IJSONObject) {
|
||||
const computedError =
|
||||
((error.response as IJSONObject)?.data as IJSONObject) ||
|
||||
(error.message as string);
|
||||
|
||||
super(computedError);
|
||||
|
||||
this.message = `Error occured while creating authorization URL!`;
|
||||
}
|
||||
}
|
10
packages/backend/src/errors/http.js
Normal file
10
packages/backend/src/errors/http.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import BaseError from './base';
|
||||
|
||||
export default class HttpError extends BaseError {
|
||||
constructor(error) {
|
||||
const computedError = error.response?.data || error.message;
|
||||
super(computedError);
|
||||
|
||||
this.response = error.response;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import type { AxiosResponse, AxiosError } from 'axios';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import BaseError from './base';
|
||||
|
||||
export default class HttpError extends BaseError {
|
||||
response: AxiosResponse;
|
||||
|
||||
constructor(error: AxiosError) {
|
||||
const computedError =
|
||||
error.response?.data as IJSONObject ||
|
||||
error.message as string;
|
||||
|
||||
super(computedError);
|
||||
|
||||
this.response = error.response;
|
||||
}
|
||||
}
|
@@ -19,7 +19,6 @@ 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 shareConnection from './mutations/share-connection.ee';
|
||||
import updateAppAuthClient from './mutations/update-app-auth-client.ee';
|
||||
import updateAppConfig from './mutations/update-app-config.ee';
|
||||
import updateConfig from './mutations/update-config.ee';
|
||||
@@ -56,7 +55,6 @@ const mutationResolvers = {
|
||||
registerUser,
|
||||
resetConnection,
|
||||
resetPassword,
|
||||
shareConnection,
|
||||
updateAppAuthClient,
|
||||
updateAppConfig,
|
||||
updateConfig,
|
@@ -0,0 +1,17 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
|
||||
const createAppAuthClient = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const appConfig = await AppConfig.query()
|
||||
.findById(params.input.appConfigId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.insert(params.input);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default createAppAuthClient;
|
@@ -1,35 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
appConfigId: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(params.input.appConfigId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default createAppAuthClient;
|
@@ -0,0 +1,18 @@
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
|
||||
const createAppConfig = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const key = params.input.key;
|
||||
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
if (!app) throw new Error('The app cannot be found!');
|
||||
|
||||
const appConfig = await AppConfig.query().insert(params.input);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default createAppConfig;
|
@@ -1,36 +0,0 @@
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const key = params.input.key;
|
||||
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
if (!app) throw new Error('The app cannot be found!');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default createAppConfig;
|
@@ -1,21 +1,7 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
appAuthClientId: string;
|
||||
formattedData: IJSONObject;
|
||||
};
|
||||
};
|
||||
|
||||
const createConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
const { key, appAuthClientId } = params.input;
|
||||
@@ -26,16 +12,20 @@ const createConnection = async (
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
if (appConfig) {
|
||||
if (appConfig.disabled) throw new Error('This application has been disabled for new connections!');
|
||||
if (appConfig.disabled)
|
||||
throw new Error(
|
||||
'This application has been disabled for new connections!'
|
||||
);
|
||||
|
||||
if (!appConfig.allowCustomConnection && formattedData) throw new Error(`Custom connections cannot be created for ${app.name}!`);
|
||||
if (!appConfig.allowCustomConnection && formattedData)
|
||||
throw new Error(`Custom connections cannot be created for ${app.name}!`);
|
||||
|
||||
if (appConfig.shared && !formattedData) {
|
||||
const authClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.findById(appAuthClientId)
|
||||
.where({
|
||||
active: true
|
||||
active: true,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
@@ -43,8 +33,7 @@ const createConnection = async (
|
||||
}
|
||||
}
|
||||
|
||||
const createdConnection = await context
|
||||
.currentUser
|
||||
const createdConnection = await context.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.insert({
|
||||
key,
|
@@ -1,19 +1,7 @@
|
||||
import App from '../../models/app';
|
||||
import Step from '../../models/step';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
triggerAppKey: string;
|
||||
connectionId: string;
|
||||
};
|
||||
};
|
||||
|
||||
const createFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createFlow = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Flow');
|
||||
|
||||
const connectionId = params?.input?.connectionId;
|
||||
@@ -28,11 +16,11 @@ const createFlow = async (
|
||||
});
|
||||
|
||||
if (connectionId) {
|
||||
const connection = await context.currentUser
|
||||
.relatedConnectionsQuery()
|
||||
const hasConnection = await context.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.findById(connectionId);
|
||||
|
||||
if (!connection) {
|
||||
if (!hasConnection) {
|
||||
throw new Error('The connection does not exist!');
|
||||
}
|
||||
}
|
29
packages/backend/src/graphql/mutations/create-role.ee.js
Normal file
29
packages/backend/src/graphql/mutations/create-role.ee.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
import Role from '../../models/role';
|
||||
|
||||
const createRole = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Role');
|
||||
|
||||
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;
|
@@ -1,34 +0,0 @@
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
import Permission from '../../models/permission';
|
||||
import Role from '../../models/role';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
name: string;
|
||||
description: string;
|
||||
permissions: Permission[];
|
||||
};
|
||||
};
|
||||
|
||||
const createRole = async (_parent: unknown, params: Params, context: Context) => {
|
||||
context.currentUser.can('create', 'Role');
|
||||
|
||||
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;
|
@@ -1,28 +1,7 @@
|
||||
import App from '../../models/app';
|
||||
import Flow from '../../models/flow';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
appKey: string;
|
||||
flow: {
|
||||
id: string;
|
||||
};
|
||||
connection: {
|
||||
id: string;
|
||||
};
|
||||
previousStep: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const createStep = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createStep = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('update', 'Flow');
|
||||
const userFlows = context.currentUser.$relatedQuery('flows');
|
||||
const allFlows = Flow.query();
|
@@ -1,23 +1,7 @@
|
||||
import User from '../../models/user';
|
||||
import Role from '../../models/role';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
fullName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const createUser = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createUser = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'User');
|
||||
|
||||
const { fullName, email, password } = params.input;
|
||||
@@ -30,7 +14,7 @@ const createUser = async (
|
||||
throw new Error('User already exists!');
|
||||
}
|
||||
|
||||
const userPayload: Partial<User> = {
|
||||
const userPayload = {
|
||||
fullName,
|
||||
email,
|
||||
password,
|
@@ -1,21 +1,9 @@
|
||||
import Context from '../../types/express/context';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteAppAuthClient = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'App');
|
||||
|
||||
await AppAuthClient
|
||||
.query()
|
||||
await AppAuthClient.query()
|
||||
.delete()
|
||||
.findOne({
|
||||
id: params.input.id,
|
15
packages/backend/src/graphql/mutations/delete-connection.js
Normal file
15
packages/backend/src/graphql/mutations/delete-connection.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const deleteConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'Connection');
|
||||
|
||||
await context.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.delete()
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export default deleteConnection;
|
@@ -1,31 +0,0 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Connection from '../../models/connection';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const conditions = context.currentUser.can('delete', 'Connection');
|
||||
const userConnections = context.currentUser.$relatedQuery('connections');
|
||||
const allConnections = Connection.query();
|
||||
const baseQuery = conditions.isCreator ? userConnections : allConnections;
|
||||
|
||||
await baseQuery
|
||||
.clone()
|
||||
.delete()
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export default deleteConnection;
|
@@ -0,0 +1,58 @@
|
||||
import { Duration } from 'luxon';
|
||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
||||
import flowQueue from '../../queues/flow';
|
||||
import Flow from '../../models/flow';
|
||||
import ExecutionStep from '../../models/execution-step';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
const deleteCurrentUser = async (_parent, params, context) => {
|
||||
const id = context.currentUser.id;
|
||||
|
||||
const flows = await context.currentUser.$relatedQuery('flows').where({
|
||||
active: true,
|
||||
});
|
||||
|
||||
const repeatableJobs = await flowQueue.getRepeatableJobs();
|
||||
|
||||
for (const flow of flows) {
|
||||
const job = repeatableJobs.find((job) => job.id === flow.id);
|
||||
|
||||
if (job) {
|
||||
await flowQueue.removeRepeatableByKey(job.key);
|
||||
}
|
||||
}
|
||||
|
||||
const executionIds = (
|
||||
await context.currentUser
|
||||
.$relatedQuery('executions')
|
||||
.select('executions.id')
|
||||
).map((execution) => execution.id);
|
||||
const flowIds = flows.map((flow) => flow.id);
|
||||
|
||||
await ExecutionStep.query().delete().whereIn('execution_id', executionIds);
|
||||
await context.currentUser.$relatedQuery('executions').delete();
|
||||
await context.currentUser.$relatedQuery('steps').delete();
|
||||
await Flow.query().whereIn('id', flowIds).delete();
|
||||
await context.currentUser.$relatedQuery('connections').delete();
|
||||
await context.currentUser.$relatedQuery('identities').delete();
|
||||
|
||||
if (appConfig.isCloud) {
|
||||
await context.currentUser.$relatedQuery('subscriptions').delete();
|
||||
await context.currentUser.$relatedQuery('usageData').delete();
|
||||
}
|
||||
|
||||
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 deleteCurrentUser;
|
@@ -1,101 +0,0 @@
|
||||
import { Duration } from 'luxon';
|
||||
import Context from '../../types/express/context';
|
||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
||||
import flowQueue from '../../queues/flow';
|
||||
import Flow from '../../models/flow';
|
||||
import Execution from '../../models/execution';
|
||||
import User from '../../models/user';
|
||||
import ExecutionStep from '../../models/execution-step';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
const deleteCurrentUser = async (
|
||||
_parent: unknown,
|
||||
params: never,
|
||||
context: Context
|
||||
) => {
|
||||
const id = context.currentUser.id;
|
||||
|
||||
try {
|
||||
await User.transaction(async (trx) => {
|
||||
const flows = await context.currentUser
|
||||
.$relatedQuery('flows', trx)
|
||||
.where({
|
||||
active: true,
|
||||
});
|
||||
|
||||
const { count } = await context.currentUser
|
||||
.$relatedQuery('connections', trx)
|
||||
.joinRelated('sharedConnections')
|
||||
.joinRelated('steps')
|
||||
.join('flows', function () {
|
||||
this
|
||||
.on(
|
||||
'flows.id', '=', 'steps.flow_id'
|
||||
)
|
||||
.andOnVal(
|
||||
'flows.user_id', '<>', id
|
||||
)
|
||||
.andOnVal(
|
||||
'flows.active', '=', true
|
||||
)
|
||||
})
|
||||
.count()
|
||||
.first();
|
||||
|
||||
if (count) {
|
||||
throw new Error('The shared connections must be removed first!');
|
||||
}
|
||||
|
||||
const executionIds = (
|
||||
await context.currentUser
|
||||
.$relatedQuery('executions', trx)
|
||||
.select('executions.id')
|
||||
).map((execution: Execution) => execution.id);
|
||||
const flowIds = flows.map((flow) => flow.id);
|
||||
|
||||
await ExecutionStep.query(trx).delete().whereIn('execution_id', executionIds);
|
||||
await context.currentUser.$relatedQuery('executions', trx).delete();
|
||||
await context.currentUser.$relatedQuery('steps', trx).delete();
|
||||
await Flow.query(trx).whereIn('id', flowIds).delete();
|
||||
await context.currentUser.$relatedQuery('connections', trx).delete();
|
||||
await context.currentUser.$relatedQuery('identities', trx).delete();
|
||||
|
||||
if (appConfig.isCloud) {
|
||||
await context.currentUser.$relatedQuery('subscriptions', trx).delete();
|
||||
await context.currentUser.$relatedQuery('usageData', trx).delete();
|
||||
}
|
||||
|
||||
await context.currentUser.$query(trx).delete();
|
||||
|
||||
const jobName = `Delete user - ${id}`;
|
||||
const jobPayload = { id };
|
||||
const millisecondsFor30Days = Duration.fromObject({ days: 30 }).toMillis();
|
||||
const jobOptions = {
|
||||
delay: millisecondsFor30Days,
|
||||
};
|
||||
|
||||
// must be done as the last action as this cannot be reverted via the transaction!
|
||||
const repeatableJobs = await flowQueue.getRepeatableJobs();
|
||||
|
||||
for (const flow of flows) {
|
||||
const job = repeatableJobs.find((job) => job.id === flow.id);
|
||||
|
||||
if (job) {
|
||||
await flowQueue.removeRepeatableByKey(job.key);
|
||||
}
|
||||
}
|
||||
|
||||
await deleteUserQueue.add(jobName, jobPayload, jobOptions);
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
throw new Error('The user deletion has failed!');
|
||||
}
|
||||
};
|
||||
|
||||
export default deleteCurrentUser;
|
@@ -1,21 +1,9 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Flow from '../../models/flow';
|
||||
import Execution from '../../models/execution';
|
||||
import ExecutionStep from '../../models/execution-step';
|
||||
import globalVariable from '../../helpers/global-variable';
|
||||
import logger from '../../helpers/logger';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteFlow = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('delete', 'Flow');
|
||||
const isCreator = conditions.isCreator;
|
||||
const allFlows = Flow.query();
|
||||
@@ -43,13 +31,15 @@ const deleteFlow = async (
|
||||
await trigger.unregisterHook($);
|
||||
} catch (error) {
|
||||
// suppress error as the remote resource might have been already deleted
|
||||
logger.debug(`Failed to unregister webhook for flow ${flow.id}: ${error.message}`);
|
||||
logger.debug(
|
||||
`Failed to unregister webhook for flow ${flow.id}: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const executionIds = (
|
||||
await flow.$relatedQuery('executions').select('executions.id')
|
||||
).map((execution: Execution) => execution.id);
|
||||
).map((execution) => execution.id);
|
||||
|
||||
await ExecutionStep.query().delete().whereIn('execution_id', executionIds);
|
||||
|
@@ -1,18 +1,7 @@
|
||||
import Role from '../../models/role';
|
||||
import SamlAuthProvider from '../../models/saml-auth-provider.ee';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteRole = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteRole = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'Role');
|
||||
|
||||
const role = await Role.query().findById(params.input.id).throwIfNotFound();
|
@@ -1,16 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteStep = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteStep = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'Flow');
|
||||
|
||||
const step = await context.currentUser
|
@@ -1,19 +1,8 @@
|
||||
import { Duration } from 'luxon';
|
||||
import Context from '../../types/express/context';
|
||||
import User from '../../models/user';
|
||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteUser = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteUser = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'User');
|
||||
|
||||
const id = params.input.id;
|
||||
@@ -24,7 +13,7 @@ const deleteUser = async (
|
||||
const jobPayload = { id };
|
||||
const millisecondsFor30Days = Duration.fromObject({ days: 30 }).toMillis();
|
||||
const jobOptions = {
|
||||
delay: millisecondsFor30Days
|
||||
delay: millisecondsFor30Days,
|
||||
};
|
||||
|
||||
await deleteUserQueue.add(jobName, jobPayload, jobOptions);
|
@@ -1,15 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Step from '../../models/step';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
type NewStepIds = Record<string, string>;
|
||||
|
||||
function updateStepId(value: string, newStepIds: NewStepIds) {
|
||||
function updateStepId(value, newStepIds) {
|
||||
let newValue = value;
|
||||
|
||||
const stepIdEntries = Object.entries(newStepIds);
|
||||
@@ -24,9 +13,9 @@ function updateStepId(value: string, newStepIds: NewStepIds) {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
function updateStepVariables(parameters: Step['parameters'], newStepIds: NewStepIds): Step['parameters'] {
|
||||
function updateStepVariables(parameters, newStepIds) {
|
||||
const entries = Object.entries(parameters);
|
||||
return entries.reduce((result, [key, value]: [string, unknown]) => {
|
||||
return entries.reduce((result, [key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
return {
|
||||
...result,
|
||||
@@ -37,7 +26,7 @@ function updateStepVariables(parameters: Step['parameters'], newStepIds: NewStep
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
...result,
|
||||
[key]: value.map(item => updateStepVariables(item, newStepIds)),
|
||||
[key]: value.map((item) => updateStepVariables(item, newStepIds)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,11 +37,7 @@ function updateStepVariables(parameters: Step['parameters'], newStepIds: NewStep
|
||||
}, {});
|
||||
}
|
||||
|
||||
const duplicateFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const duplicateFlow = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Flow');
|
||||
|
||||
const flow = await context.currentUser
|
||||
@@ -69,17 +54,16 @@ const duplicateFlow = async (
|
||||
active: false,
|
||||
});
|
||||
|
||||
const newStepIds: NewStepIds = {};
|
||||
const newStepIds = {};
|
||||
for (const step of flow.steps) {
|
||||
const duplicatedStep = await duplicatedFlow.$relatedQuery('steps')
|
||||
.insert({
|
||||
key: step.key,
|
||||
appKey: step.appKey,
|
||||
type: step.type,
|
||||
connectionId: step.connectionId,
|
||||
position: step.position,
|
||||
parameters: updateStepVariables(step.parameters, newStepIds),
|
||||
});
|
||||
const duplicatedStep = await duplicatedFlow.$relatedQuery('steps').insert({
|
||||
key: step.key,
|
||||
appKey: step.appKey,
|
||||
type: step.type,
|
||||
connectionId: step.connectionId,
|
||||
position: step.position,
|
||||
parameters: updateStepVariables(step.parameters, newStepIds),
|
||||
});
|
||||
|
||||
if (duplicatedStep.isTrigger) {
|
||||
await duplicatedStep.updateWebhookUrl();
|
@@ -1,18 +1,7 @@
|
||||
import Context from '../../types/express/context';
|
||||
import testRun from '../../services/test-run';
|
||||
import Step from '../../models/step';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
stepId: string;
|
||||
};
|
||||
};
|
||||
|
||||
const executeFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const executeFlow = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('update', 'Flow');
|
||||
const isCreator = conditions.isCreator;
|
||||
const allSteps = Step.query();
|
||||
@@ -21,10 +10,7 @@ const executeFlow = async (
|
||||
|
||||
const { stepId } = params.input;
|
||||
|
||||
const untilStep = await baseQuery
|
||||
.clone()
|
||||
.findById(stepId)
|
||||
.throwIfNotFound();
|
||||
const untilStep = await baseQuery.clone().findById(stepId).throwIfNotFound();
|
||||
|
||||
const { executionStep } = await testRun({ stepId });
|
||||
|
@@ -6,13 +6,7 @@ import {
|
||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
} from '../../helpers/remove-job-configuration';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
|
||||
const forgotPassword = async (_parent: unknown, params: Params) => {
|
||||
const forgotPassword = async (_parent, params) => {
|
||||
const { email } = params.input;
|
||||
|
||||
const user = await User.query().findOne({ email: email.toLowerCase() });
|
@@ -1,18 +1,7 @@
|
||||
import Context from '../../types/express/context';
|
||||
import globalVariable from '../../helpers/global-variable';
|
||||
import App from '../../models/app';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const generateAuthUrl = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const generateAuthUrl = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
const connection = await context.currentUser
|
@@ -1,14 +1,7 @@
|
||||
import User from '../../models/user';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
|
||||
const login = async (_parent: unknown, params: Params) => {
|
||||
const login = async (_parent, params) => {
|
||||
const user = await User.query().findOne({
|
||||
email: params.input.email.toLowerCase(),
|
||||
});
|
@@ -1,15 +1,7 @@
|
||||
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 registerUser = async (_parent, params) => {
|
||||
const { fullName, email, password } = params.input;
|
||||
|
||||
const existingUser = await User.query().findOne({
|
22
packages/backend/src/graphql/mutations/reset-connection.js
Normal file
22
packages/backend/src/graphql/mutations/reset-connection.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const resetConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
let connection = await context.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!connection.formattedData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
connection = await connection.$query().patchAndFetch({
|
||||
formattedData: { screenName: connection.formattedData.screenName },
|
||||
});
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
||||
export default resetConnection;
|
@@ -1,38 +0,0 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Connection from '../../models/connection';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const resetConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const conditions = context.currentUser.can('update', 'Connection');
|
||||
const userConnections = context.currentUser.$relatedQuery('connections');
|
||||
const allConnections = Connection.query();
|
||||
const baseQuery = conditions.isCreator ? userConnections : allConnections;
|
||||
|
||||
let connection = await baseQuery
|
||||
.clone()
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!connection.formattedData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
connection = await connection.$query().patchAndFetch({
|
||||
formattedData: { screenName: connection.formattedData.screenName },
|
||||
});
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
||||
export default resetConnection;
|
@@ -1,13 +1,6 @@
|
||||
import User from '../../models/user';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
token: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
|
||||
const resetPassword = async (_parent: unknown, params: Params) => {
|
||||
const resetPassword = async (_parent, params) => {
|
||||
const { token, password } = params.input;
|
||||
|
||||
if (!token) {
|
@@ -1,55 +0,0 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Connection from '../../models/connection';
|
||||
import SharedConnection from '../../models/shared-connection';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
roleIds: string[];
|
||||
};
|
||||
};
|
||||
|
||||
const shareConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const conditions = context.currentUser.can('update', 'Connection');
|
||||
|
||||
if (conditions.isCreator) return;
|
||||
|
||||
const {
|
||||
id,
|
||||
roleIds,
|
||||
} = params.input;
|
||||
|
||||
const connection = await Connection
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
try {
|
||||
const updatedConnection = await Connection.transaction(async (trx) => {
|
||||
await connection.$relatedQuery('sharedConnections', trx).delete();
|
||||
|
||||
if (roleIds?.length) {
|
||||
const sharedConnections = roleIds.map((roleId) => ({
|
||||
roleId,
|
||||
connectionId: connection.id,
|
||||
}));
|
||||
|
||||
await SharedConnection.query().insert(sharedConnections);
|
||||
}
|
||||
|
||||
return await Connection
|
||||
.query(trx)
|
||||
.findById(id);
|
||||
});
|
||||
|
||||
return updatedConnection;
|
||||
} catch (err) {
|
||||
throw new Error('The connection sharing preferences could not be updated!');
|
||||
}
|
||||
};
|
||||
|
||||
export default shareConnection;
|
@@ -0,0 +1,17 @@
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
const updateAppAuthClient = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const { id, ...appAuthClientData } = params.input;
|
||||
|
||||
const appAuthClient = await AppAuthClient.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appAuthClient.$query().patch(appAuthClientData);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default updateAppAuthClient;
|
@@ -1,38 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const updateAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const {
|
||||
id,
|
||||
...appAuthClientData
|
||||
} = params.input;
|
||||
|
||||
const appAuthClient = await AppAuthClient
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appAuthClient
|
||||
.$query()
|
||||
.patch(appAuthClientData);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default updateAppAuthClient;
|
@@ -0,0 +1,15 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
|
||||
const updateAppConfig = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const { id, ...appConfigToUpdate } = params.input;
|
||||
|
||||
const appConfig = await AppConfig.query().findById(id).throwIfNotFound();
|
||||
|
||||
await appConfig.$query().patch(appConfigToUpdate);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default updateAppConfig;
|
@@ -1,39 +0,0 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const updateAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const {
|
||||
id,
|
||||
...appConfigToUpdate
|
||||
} = params.input;
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appConfig
|
||||
.$query()
|
||||
.patch(
|
||||
appConfigToUpdate
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default updateAppConfig;
|
@@ -1,18 +1,6 @@
|
||||
import type { IJSONValue } from '@automatisch/types';
|
||||
import Config from '../../models/config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
[index: string]: IJSONValue;
|
||||
};
|
||||
};
|
||||
|
||||
const updateConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateConfig = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'Config');
|
||||
|
||||
const config = params.input;
|
33
packages/backend/src/graphql/mutations/update-connection.js
Normal file
33
packages/backend/src/graphql/mutations/update-connection.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
const updateConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
let connection = await context.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
|
||||
if (params.input.appAuthClientId) {
|
||||
const appAuthClient = await AppAuthClient.query()
|
||||
.findById(params.input.appAuthClientId)
|
||||
.throwIfNotFound();
|
||||
|
||||
formattedData = appAuthClient.formattedAuthDefaults;
|
||||
}
|
||||
|
||||
connection = await connection.$query().patchAndFetch({
|
||||
formattedData: {
|
||||
...connection.formattedData,
|
||||
...formattedData,
|
||||
},
|
||||
});
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
||||
export default updateConnection;
|
@@ -1,52 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import Context from '../../types/express/context';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
import Connection from '../../models/connection';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
formattedData?: IJSONObject;
|
||||
appAuthClientId?: string;
|
||||
};
|
||||
};
|
||||
|
||||
const updateConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const conditions = context.currentUser.can('update', 'Connection');
|
||||
const userConnections = context.currentUser.$relatedQuery('connections');
|
||||
const allConnections = Connection.query();
|
||||
const baseQuery = conditions.isCreator ? userConnections : allConnections;
|
||||
|
||||
let connection = await baseQuery
|
||||
.clone()
|
||||
.findOne({
|
||||
id: params.input.id,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
|
||||
if (params.input.appAuthClientId) {
|
||||
const appAuthClient = await AppAuthClient
|
||||
.query()
|
||||
.findById(params.input.appAuthClientId)
|
||||
.throwIfNotFound();
|
||||
|
||||
formattedData = appAuthClient.formattedAuthDefaults;
|
||||
}
|
||||
|
||||
connection = await connection.$query().patchAndFetch({
|
||||
formattedData: {
|
||||
...connection.formattedData,
|
||||
...formattedData,
|
||||
},
|
||||
});
|
||||
|
||||
return connection;
|
||||
};
|
||||
|
||||
export default updateConnection;
|
@@ -0,0 +1,11 @@
|
||||
const updateCurrentUser = async (_parent, params, context) => {
|
||||
const user = await context.currentUser.$query().patchAndFetch({
|
||||
email: params.input.email,
|
||||
password: params.input.password,
|
||||
fullName: params.input.fullName,
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default updateCurrentUser;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user