Compare commits
	
		
			134 Commits
		
	
	
		
			decode-htm
			...
			changedete
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c095d3138b | ||
|   | 9548c93b4c | ||
|   | 4144944ab2 | ||
|   | 46b85519c1 | ||
|   | 5a83fc33ec | ||
|   | c80791267f | ||
|   | b30f97db3e | ||
|   | 717c81fa2b | ||
|   | ae188bc563 | ||
|   | fc4561221d | ||
|   | 5aeb4f8809 | ||
|   | c6c900bc39 | ||
|   | c18ab67a25 | ||
|   | 55ae1470d0 | ||
|   | a1136fdfb2 | ||
|   | dfe56d3aa2 | ||
|   | 3da5e13ecd | ||
|   | 40d0fe0db6 | ||
|   | 029fd2d0b0 | ||
|   | 12905ad733 | ||
|   | d257f59a3e | ||
|   | c9281b4605 | ||
|   | e9d2ae5d67 | ||
|   | 0f8e05610b | ||
|   | 9ea2196e51 | ||
|   | 97327a9033 | ||
|   | 8bf11ba7d1 | ||
|   | 523833b015 | ||
|   | e25a651d26 | ||
|   | 92a8c1483d | ||
|   | 8da3448e9c | ||
|   | f2385d8916 | ||
|   | 17a8daa526 | ||
|   | 51e254f127 | ||
|   | feccf571cd | ||
|   | 9f8ce44c1b | ||
|   | a7cfe7f23b | ||
|   | 8eaf775ef2 | ||
|   | 7b4179a87f | ||
|   | 43281bcbfe | ||
|   | 8a35d47caf | ||
|   | 759e8b6c42 | ||
|   | a8b01244af | ||
|   | 6ffb16ac67 | ||
|   | 5b66cc6c8b | ||
|   | 9a96258265 | ||
|   | a6cc1566c7 | ||
|   | d8d6227125 | ||
|   | fbfa67e471 | ||
|   | ab897ada5a | ||
|   | 3bcd3f3cb7 | ||
|   | acbede8631 | ||
|   | fa8c7571d7 | ||
|   | a58575c5a1 | ||
|   | 51f7009a80 | ||
|   | ba24c77f06 | ||
|   | 6b712c9a90 | ||
|   | 033b15a158 | ||
|   | e398bb84d4 | ||
|   | 05e902ab0c | ||
|   | 36eee61cb5 | ||
|   | 2409ce6fce | ||
|   | 999d61e520 | ||
|   | 08d2418190 | ||
|   | 6c07faeaaf | ||
|   | 3f5cfbf5a2 | ||
|   | 28f7707c75 | ||
|   | dc8358f6ce | ||
|   | ffcda04677 | ||
|   | 64ef655abb | ||
|   | 67887b1220 | ||
|   | 07803d1263 | ||
|   | f5ff7f7e13 | ||
|   | 641d062b82 | ||
|   | bb28a06ee9 | ||
|   | 81338c60f2 | ||
|   | f47fa5d272 | ||
|   | 29e9e012a5 | ||
|   | f89ddb5847 | ||
|   | c721f063ef | ||
|   | a709565336 | ||
|   | 91e484aef1 | ||
|   | 6ba94dcc8e | ||
|   | 788530be45 | ||
|   | 7ed392e854 | ||
|   | 3932e554da | ||
|   | 1a21624618 | ||
|   | 9f292ff018 | ||
|   | dbb24b3a9b | ||
|   | 35b2639837 | ||
|   | 35951199cd | ||
|   | 79af909c51 | ||
|   | 3482aa7b76 | ||
|   | 5dbc1f59ef | ||
|   | 2166a3220e | ||
|   | 24a7d1ef10 | ||
|   | 18ffbb7317 | ||
|   | 363874de6a | ||
|   | 68d1719b11 | ||
|   | 1a75d81268 | ||
|   | 2163be4227 | ||
|   | b54afcd922 | ||
|   | 0a86641a0f | ||
|   | 18464c746a | ||
|   | ba92cddae1 | ||
|   | 2a4f8ed45f | ||
|   | 135a0028be | ||
|   | 4da6e8372f | ||
|   | 6a7cdf2570 | ||
|   | 73c929f25e | ||
|   | 754c2d41c2 | ||
|   | 7201e48111 | ||
|   | 0f39007f92 | ||
|   | ab811daba7 | ||
|   | 1428bf8ffa | ||
|   | 53f7f38e23 | ||
|   | ce6ad9e0d3 | ||
|   | 17a0c6123a | ||
|   | 10edc5a8ad | ||
|   | bdf07d6bd5 | ||
|   | 10ff65e71a | ||
|   | 9395097fda | ||
|   | 47eb0e00e3 | ||
|   | 57d5f34ac5 | ||
|   | 06b040412a | ||
|   | cab040c74a | ||
|   | 21706a7d15 | ||
|   | 22e4b8aaeb | ||
|   | bd43a6021a | ||
|   | a90b58b6db | ||
|   | 92a9b096ec | ||
|   | 7a6aa99840 | ||
|   | 5657f0d793 | ||
|   | 798529007e | 
| @@ -36,7 +36,6 @@ services: | |||||||
|   keycloak: |   keycloak: | ||||||
|     image: quay.io/keycloak/keycloak:21.1 |     image: quay.io/keycloak/keycloak:21.1 | ||||||
|     restart: always |     restart: always | ||||||
|     container_name: keycloak |  | ||||||
|     environment: |     environment: | ||||||
|       - KEYCLOAK_ADMIN=admin |       - KEYCLOAK_ADMIN=admin | ||||||
|       - KEYCLOAK_ADMIN_PASSWORD=admin |       - KEYCLOAK_ADMIN_PASSWORD=admin | ||||||
|   | |||||||
| @@ -4,5 +4,9 @@ | |||||||
| **/.devcontainer | **/.devcontainer | ||||||
| **/.github | **/.github | ||||||
| **/.vscode | **/.vscode | ||||||
|  | **/.env | ||||||
|  | **/.env.test | ||||||
|  | **/.env.production | ||||||
|  | **/yarn-error.log | ||||||
| packages/docs | packages/docs | ||||||
| packages/e2e-test | packages/e2e-test | ||||||
|   | |||||||
| @@ -1,14 +1,25 @@ | |||||||
| # syntax=docker/dockerfile:1 | # syntax=docker/dockerfile:1 | ||||||
| FROM node:18-alpine | FROM node:18-alpine | ||||||
| WORKDIR /automatisch |  | ||||||
|  | ENV PORT 3000 | ||||||
|  |  | ||||||
|  | RUN \ | ||||||
|  |   apk --no-cache add --virtual build-dependencies python3 build-base git | ||||||
|  |  | ||||||
|  | WORKDIR /automatisch | ||||||
|  |  | ||||||
|  | # copy the app, note .dockerignore | ||||||
|  | COPY . /automatisch | ||||||
|  |  | ||||||
|  | RUN yarn | ||||||
|  |  | ||||||
|  | RUN cd packages/web && yarn build | ||||||
|  |  | ||||||
| RUN \ | RUN \ | ||||||
|   apk --no-cache add --virtual build-dependencies python3 build-base && \ |  | ||||||
|   yarn global add @automatisch/cli@0.10.0 --network-timeout 1000000 && \ |  | ||||||
|   rm -rf /usr/local/share/.cache/ && \ |   rm -rf /usr/local/share/.cache/ && \ | ||||||
|   apk del build-dependencies |   apk del build-dependencies | ||||||
|  |  | ||||||
| COPY ./entrypoint.sh /entrypoint.sh | COPY ./docker/entrypoint.sh /entrypoint.sh | ||||||
|  |  | ||||||
| EXPOSE 3000 | EXPOSE 3000 | ||||||
| ENTRYPOINT ["sh", "/entrypoint.sh"] | ENTRYPOINT ["sh", "/entrypoint.sh"] | ||||||
|   | |||||||
| @@ -1,24 +0,0 @@ | |||||||
| # syntax=docker/dockerfile:1 |  | ||||||
| FROM node:18-alpine |  | ||||||
|  |  | ||||||
| ENV PORT 3000 |  | ||||||
|  |  | ||||||
| RUN \ |  | ||||||
|   apk --no-cache add --virtual build-dependencies python3 build-base git |  | ||||||
|  |  | ||||||
| RUN git clone https://github.com/automatisch/automatisch.git |  | ||||||
|  |  | ||||||
| WORKDIR /automatisch |  | ||||||
|  |  | ||||||
| RUN yarn install |  | ||||||
|  |  | ||||||
| RUN if [ "$WORKER" != "true" ]; then cd packages/web && yarn build; fi |  | ||||||
|  |  | ||||||
| RUN \ |  | ||||||
|   rm -rf /usr/local/share/.cache/ && \ |  | ||||||
|   apk del build-dependencies |  | ||||||
|  |  | ||||||
| COPY ./docker/entrypoint-cloud.sh /entrypoint-cloud.sh |  | ||||||
|  |  | ||||||
| EXPOSE 3000 |  | ||||||
| ENTRYPOINT ["sh", "/entrypoint-cloud.sh"] |  | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| # syntax=docker/dockerfile:1 | # syntax=docker/dockerfile:1 | ||||||
| FROM automatischio/automatisch:0.10.0 | FROM automatischio/automatisch:latest | ||||||
| WORKDIR /automatisch | WORKDIR /automatisch | ||||||
|  |  | ||||||
| RUN apk add --no-cache openssl dos2unix | RUN apk add --no-cache openssl dos2unix | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| #!/bin/sh |  | ||||||
|  |  | ||||||
| set -e |  | ||||||
|  |  | ||||||
| cd packages/backend |  | ||||||
|  |  | ||||||
| if [ -n "$WORKER" ]; then |  | ||||||
|   yarn start:worker |  | ||||||
| else |  | ||||||
|   yarn db:migrate |  | ||||||
|   yarn db:seed:user |  | ||||||
|   yarn start |  | ||||||
| fi |  | ||||||
| @@ -2,8 +2,12 @@ | |||||||
|  |  | ||||||
| set -e | set -e | ||||||
|  |  | ||||||
|  | cd packages/backend | ||||||
|  |  | ||||||
| if [ -n "$WORKER" ]; then | if [ -n "$WORKER" ]; then | ||||||
|   automatisch start-worker |   yarn start:worker | ||||||
| else | else | ||||||
|   automatisch start |   yarn db:migrate | ||||||
|  |   yarn db:seed:user | ||||||
|  |   yarn start | ||||||
| fi | fi | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import appConfig from '../../src/config/app.js'; | |||||||
| import logger from '../../src/helpers/logger.js'; | import logger from '../../src/helpers/logger.js'; | ||||||
| import client from './client.js'; | import client from './client.js'; | ||||||
| import User from '../../src/models/user.js'; | import User from '../../src/models/user.js'; | ||||||
|  | import Config from '../../src/models/config.js'; | ||||||
| import Role from '../../src/models/role.js'; | import Role from '../../src/models/role.js'; | ||||||
| import '../../src/config/orm.js'; | import '../../src/config/orm.js'; | ||||||
| import process from 'process'; | import process from 'process'; | ||||||
| @@ -21,6 +22,14 @@ export async function createUser( | |||||||
|   email = 'user@automatisch.io', |   email = 'user@automatisch.io', | ||||||
|   password = 'sample' |   password = 'sample' | ||||||
| ) { | ) { | ||||||
|  |   if (appConfig.disableSeedUser) { | ||||||
|  |     logger.info('Seed user is disabled.'); | ||||||
|  |  | ||||||
|  |     process.exit(0); | ||||||
|  |  | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   const UNIQUE_VIOLATION_CODE = '23505'; |   const UNIQUE_VIOLATION_CODE = '23505'; | ||||||
|  |  | ||||||
|   const role = await fetchAdminRole(); |   const role = await fetchAdminRole(); | ||||||
| @@ -37,6 +46,8 @@ export async function createUser( | |||||||
|     if (userCount === 0) { |     if (userCount === 0) { | ||||||
|       const user = await User.query().insertAndFetch(userParams); |       const user = await User.query().insertAndFetch(userParams); | ||||||
|       logger.info(`User has been saved: ${user.email}`); |       logger.info(`User has been saved: ${user.email}`); | ||||||
|  |  | ||||||
|  |       await Config.markInstallationCompleted(); | ||||||
|     } else { |     } else { | ||||||
|       logger.info('No need to seed a user.'); |       logger.info('No need to seed a user.'); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ | |||||||
|     "accounting": "^0.4.1", |     "accounting": "^0.4.1", | ||||||
|     "ajv-formats": "^2.1.1", |     "ajv-formats": "^2.1.1", | ||||||
|     "axios": "1.6.0", |     "axios": "1.6.0", | ||||||
|     "bcrypt": "^5.0.1", |     "bcrypt": "^5.1.0", | ||||||
|     "bullmq": "^3.0.0", |     "bullmq": "^3.0.0", | ||||||
|     "cors": "^2.8.5", |     "cors": "^2.8.5", | ||||||
|     "crypto-js": "^4.1.1", |     "crypto-js": "^4.1.1", | ||||||
| @@ -67,6 +67,7 @@ | |||||||
|     "pluralize": "^8.0.0", |     "pluralize": "^8.0.0", | ||||||
|     "raw-body": "^2.5.2", |     "raw-body": "^2.5.2", | ||||||
|     "showdown": "^2.1.0", |     "showdown": "^2.1.0", | ||||||
|  |     "uuid": "^9.0.1", | ||||||
|     "winston": "^3.7.1", |     "winston": "^3.7.1", | ||||||
|     "xmlrpc": "^1.3.2" |     "xmlrpc": "^1.3.2" | ||||||
|   }, |   }, | ||||||
| @@ -95,6 +96,7 @@ | |||||||
|     "url": "https://github.com/automatisch/automatisch/issues" |     "url": "https://github.com/automatisch/automatisch/issues" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |     "node-gyp": "^10.1.0", | ||||||
|     "nodemon": "^2.0.13", |     "nodemon": "^2.0.13", | ||||||
|     "supertest": "^6.3.3", |     "supertest": "^6.3.3", | ||||||
|     "vitest": "^1.1.3" |     "vitest": "^1.1.3" | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ export default defineApp({ | |||||||
|     'https://azure.microsoft.com/en-us/products/ai-services/openai-service', |     'https://azure.microsoft.com/en-us/products/ai-services/openai-service', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   iconUrl: '{BASE_URL}/apps/azure-openai/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/azure-openai/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/azure-openai/connection', |   authDocUrl: '{DOCS_URL}/apps/azure-openai/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'Carbone', |   name: 'Carbone', | ||||||
|   key: 'carbone', |   key: 'carbone', | ||||||
|   iconUrl: '{BASE_URL}/apps/carbone/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/carbone/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/carbone/connection', |   authDocUrl: '{DOCS_URL}/apps/carbone/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://carbone.io', |   baseUrl: 'https://carbone.io', | ||||||
|   apiBaseUrl: 'https://api.carbone.io', |   apiBaseUrl: 'https://api.carbone.io', | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 9.9 KiB | 
							
								
								
									
										44
									
								
								packages/backend/src/apps/changedetection/auth/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								packages/backend/src/apps/changedetection/auth/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | import verifyCredentials from './verify-credentials.js'; | ||||||
|  | import isStillVerified from './is-still-verified.js'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   fields: [ | ||||||
|  |     { | ||||||
|  |       key: 'screenName', | ||||||
|  |       label: 'Screen Name', | ||||||
|  |       type: 'string', | ||||||
|  |       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: 'Instance URL', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'apiKey', | ||||||
|  |       label: 'API Key', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: 'Changedetection API key of your account.', | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   verifyCredentials, | ||||||
|  |   isStillVerified, | ||||||
|  | }; | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | import verifyCredentials from './verify-credentials.js'; | ||||||
|  |  | ||||||
|  | const isStillVerified = async ($) => { | ||||||
|  |   await verifyCredentials($); | ||||||
|  |   return true; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default isStillVerified; | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | const verifyCredentials = async ($) => { | ||||||
|  |   await $.http.get('/v1/systeminfo'); | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     screenName: $.auth.data.screenName, | ||||||
|  |     apiKey: $.auth.data.apiKey, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default verifyCredentials; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | const addAuthHeader = ($, requestConfig) => { | ||||||
|  |   if ($.auth.data?.apiKey) { | ||||||
|  |     requestConfig.headers['x-api-key'] = $.auth.data.apiKey; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return requestConfig; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default addAuthHeader; | ||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | const setBaseUrl = ($, requestConfig) => { | ||||||
|  |   const instanceUrl = $.auth.data.instanceUrl; | ||||||
|  |   if (instanceUrl) { | ||||||
|  |     requestConfig.baseURL = `${instanceUrl}/api`; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return requestConfig; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default setBaseUrl; | ||||||
							
								
								
									
										17
									
								
								packages/backend/src/apps/changedetection/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/backend/src/apps/changedetection/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | import defineApp from '../../helpers/define-app.js'; | ||||||
|  | import addAuthHeader from './common/add-auth-header.js'; | ||||||
|  | import auth from './auth/index.js'; | ||||||
|  | import setBaseUrl from './common/set-base-url.js'; | ||||||
|  |  | ||||||
|  | export default defineApp({ | ||||||
|  |   name: 'Changedetection', | ||||||
|  |   key: 'changedetection', | ||||||
|  |   iconUrl: '{BASE_URL}/apps/changedetection/assets/favicon.svg', | ||||||
|  |   authDocUrl: '{DOCS_URL}/apps/changedetection/connection', | ||||||
|  |   supportsConnections: true, | ||||||
|  |   baseUrl: 'https://changedetection.io', | ||||||
|  |   apiBaseUrl: '', | ||||||
|  |   primaryColor: '3056d3', | ||||||
|  |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|  |   auth, | ||||||
|  | }); | ||||||
| @@ -5,7 +5,7 @@ export default defineApp({ | |||||||
|   name: 'Datastore', |   name: 'Datastore', | ||||||
|   key: 'datastore', |   key: 'datastore', | ||||||
|   iconUrl: '{BASE_URL}/apps/datastore/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/datastore/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/datastore/connection', |   authDocUrl: '{DOCS_URL}/apps/datastore/connection', | ||||||
|   supportsConnections: false, |   supportsConnections: false, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'DeepL', |   name: 'DeepL', | ||||||
|   key: 'deepl', |   key: 'deepl', | ||||||
|   iconUrl: '{BASE_URL}/apps/deepl/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/deepl/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/deepl/connection', |   authDocUrl: '{DOCS_URL}/apps/deepl/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://deepl.com', |   baseUrl: 'https://deepl.com', | ||||||
|   apiBaseUrl: 'https://api.deepl.com', |   apiBaseUrl: 'https://api.deepl.com', | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ export default defineApp({ | |||||||
|   name: 'Delay', |   name: 'Delay', | ||||||
|   key: 'delay', |   key: 'delay', | ||||||
|   iconUrl: '{BASE_URL}/apps/delay/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/delay/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/delay/connection', |   authDocUrl: '{DOCS_URL}/apps/delay/connection', | ||||||
|   supportsConnections: false, |   supportsConnections: false, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   name: 'Discord', |   name: 'Discord', | ||||||
|   key: 'discord', |   key: 'discord', | ||||||
|   iconUrl: '{BASE_URL}/apps/discord/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/discord/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/discord/connection', |   authDocUrl: '{DOCS_URL}/apps/discord/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://discord.com', |   baseUrl: 'https://discord.com', | ||||||
|   apiBaseUrl: 'https://discord.com/api', |   apiBaseUrl: 'https://discord.com/api', | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								packages/backend/src/apps/disqus/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/backend/src/apps/disqus/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||||
|  | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||||||
|  | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||||
|  | 	 width="200px" height="200px" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve"> | ||||||
|  | <g id="background"> | ||||||
|  | 	<rect fill="#2E9FFF" width="200" height="200"/> | ||||||
|  | </g> | ||||||
|  | <g id="Layer_2"> | ||||||
|  | </g> | ||||||
|  | <path fill="#FFFFFF" d="M102.535,167.5c-16.518,0-31.621-6.036-43.298-16.021L30.5,155.405l11.102-27.401 | ||||||
|  | 	c-3.868-8.535-6.038-18.01-6.038-28.004c0-37.277,29.984-67.5,66.971-67.5c36.984,0,66.965,30.223,66.965,67.5 | ||||||
|  | 	C169.5,137.284,139.52,167.5,102.535,167.5z M139.102,99.807v-0.188c0-19.479-13.736-33.367-37.42-33.367h-25.58v67.5h25.201 | ||||||
|  | 	C125.171,133.753,139.102,119.284,139.102,99.807L139.102,99.807z M101.964,117.168h-7.482V82.841h7.482 | ||||||
|  | 	c10.989,0,18.283,6.265,18.283,17.07v0.188C120.247,110.995,112.953,117.168,101.964,117.168z"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										21
									
								
								packages/backend/src/apps/disqus/auth/generate-auth-url.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/backend/src/apps/disqus/auth/generate-auth-url.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import { URLSearchParams } from 'url'; | ||||||
|  | import authScope from '../common/auth-scope.js'; | ||||||
|  |  | ||||||
|  | export default async function generateAuthUrl($) { | ||||||
|  |   const oauthRedirectUrlField = $.app.auth.fields.find( | ||||||
|  |     (field) => field.key == 'oAuthRedirectUrl' | ||||||
|  |   ); | ||||||
|  |   const redirectUri = oauthRedirectUrlField.value; | ||||||
|  |   const searchParams = new URLSearchParams({ | ||||||
|  |     client_id: $.auth.data.apiKey, | ||||||
|  |     scope: authScope.join(','), | ||||||
|  |     response_type: 'code', | ||||||
|  |     redirect_uri: redirectUri, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const url = `https://disqus.com/api/oauth/2.0/authorize/?${searchParams.toString()}`; | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     url, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								packages/backend/src/apps/disqus/auth/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								packages/backend/src/apps/disqus/auth/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | import generateAuthUrl from './generate-auth-url.js'; | ||||||
|  | import verifyCredentials from './verify-credentials.js'; | ||||||
|  | import refreshToken from './refresh-token.js'; | ||||||
|  | import isStillVerified from './is-still-verified.js'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   fields: [ | ||||||
|  |     { | ||||||
|  |       key: 'oAuthRedirectUrl', | ||||||
|  |       label: 'OAuth Redirect URL', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: true, | ||||||
|  |       value: '{WEB_APP_URL}/app/disqus/connections/add', | ||||||
|  |       placeholder: null, | ||||||
|  |       description: | ||||||
|  |         'When asked to input a redirect URL in Disqus, enter the URL above.', | ||||||
|  |       clickToCopy: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'apiKey', | ||||||
|  |       label: 'API Key', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'apiSecret', | ||||||
|  |       label: 'API Secret', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   generateAuthUrl, | ||||||
|  |   verifyCredentials, | ||||||
|  |   isStillVerified, | ||||||
|  |   refreshToken, | ||||||
|  | }; | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | import getCurrentUser from '../common/get-current-user.js'; | ||||||
|  |  | ||||||
|  | const isStillVerified = async ($) => { | ||||||
|  |   const currentUser = await getCurrentUser($); | ||||||
|  |   return !!currentUser.response.username; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default isStillVerified; | ||||||
							
								
								
									
										26
									
								
								packages/backend/src/apps/disqus/auth/refresh-token.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/backend/src/apps/disqus/auth/refresh-token.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | import { URLSearchParams } from 'node:url'; | ||||||
|  | import authScope from '../common/auth-scope.js'; | ||||||
|  |  | ||||||
|  | const refreshToken = async ($) => { | ||||||
|  |   const params = new URLSearchParams({ | ||||||
|  |     grant_type: 'refresh_token', | ||||||
|  |     client_id: $.auth.data.apiKey, | ||||||
|  |     client_secret: $.auth.data.apiSecret, | ||||||
|  |     refresh_token: $.auth.data.refreshToken, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const { data } = await $.http.post( | ||||||
|  |     `https://disqus.com/api/oauth/2.0/access_token/`, | ||||||
|  |     params.toString() | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     accessToken: data.access_token, | ||||||
|  |     refreshToken: data.refresh_token, | ||||||
|  |     expiresIn: data.expires_in, | ||||||
|  |     scope: authScope.join(','), | ||||||
|  |     tokenType: data.token_type, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default refreshToken; | ||||||
							
								
								
									
										34
									
								
								packages/backend/src/apps/disqus/auth/verify-credentials.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								packages/backend/src/apps/disqus/auth/verify-credentials.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | import { URLSearchParams } from 'url'; | ||||||
|  |  | ||||||
|  | const verifyCredentials = async ($) => { | ||||||
|  |   const oauthRedirectUrlField = $.app.auth.fields.find( | ||||||
|  |     (field) => field.key == 'oAuthRedirectUrl' | ||||||
|  |   ); | ||||||
|  |   const redirectUri = oauthRedirectUrlField.value; | ||||||
|  |   const params = new URLSearchParams({ | ||||||
|  |     grant_type: 'authorization_code', | ||||||
|  |     client_id: $.auth.data.apiKey, | ||||||
|  |     client_secret: $.auth.data.apiSecret, | ||||||
|  |     redirect_uri: redirectUri, | ||||||
|  |     code: $.auth.data.code, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const { data } = await $.http.post( | ||||||
|  |     `https://disqus.com/api/oauth/2.0/access_token/`, | ||||||
|  |     params.toString() | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     accessToken: data.access_token, | ||||||
|  |     tokenType: data.token_type, | ||||||
|  |     apiKey: $.auth.data.apiKey, | ||||||
|  |     apiSecret: $.auth.data.apiSecret, | ||||||
|  |     scope: $.auth.data.scope, | ||||||
|  |     userId: data.user_id, | ||||||
|  |     expiresIn: data.expires_in, | ||||||
|  |     refreshToken: data.refresh_token, | ||||||
|  |     screenName: data.username, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default verifyCredentials; | ||||||
							
								
								
									
										15
									
								
								packages/backend/src/apps/disqus/common/add-auth-header.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/backend/src/apps/disqus/common/add-auth-header.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | import { URLSearchParams } from 'url'; | ||||||
|  |  | ||||||
|  | const addAuthHeader = ($, requestConfig) => { | ||||||
|  |   const params = new URLSearchParams({ | ||||||
|  |     access_token: $.auth.data.accessToken, | ||||||
|  |     api_key: $.auth.data.apiKey, | ||||||
|  |     api_secret: $.auth.data.apiSecret, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   requestConfig.params = params; | ||||||
|  |  | ||||||
|  |   return requestConfig; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default addAuthHeader; | ||||||
							
								
								
									
										3
									
								
								packages/backend/src/apps/disqus/common/auth-scope.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/backend/src/apps/disqus/common/auth-scope.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | const authScope = ['read', 'write', 'admin', 'email']; | ||||||
|  |  | ||||||
|  | export default authScope; | ||||||
							
								
								
									
										10
									
								
								packages/backend/src/apps/disqus/common/get-current-user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/backend/src/apps/disqus/common/get-current-user.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | const getCurrentUser = async ($) => { | ||||||
|  |   try { | ||||||
|  |     const { data: currentUser } = await $.http.get('/3.0/users/details.json'); | ||||||
|  |     return currentUser; | ||||||
|  |   } catch (error) { | ||||||
|  |     throw new Error('You are not authenticated.'); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default getCurrentUser; | ||||||
							
								
								
									
										3
									
								
								packages/backend/src/apps/disqus/dynamic-data/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/backend/src/apps/disqus/dynamic-data/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | import listForums from './list-forums/index.js'; | ||||||
|  |  | ||||||
|  | export default [listForums]; | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | export default { | ||||||
|  |   name: 'List forums', | ||||||
|  |   key: 'listForums', | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const forums = { | ||||||
|  |       data: [], | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const params = { | ||||||
|  |       limit: 100, | ||||||
|  |       order: 'desc', | ||||||
|  |       cursor: undefined, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let more; | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get('/3.0/users/listForums.json', { | ||||||
|  |         params, | ||||||
|  |       }); | ||||||
|  |       params.cursor = data.cursor.next; | ||||||
|  |       more = data.cursor.hasNext; | ||||||
|  |  | ||||||
|  |       if (data.response?.length) { | ||||||
|  |         for (const forum of data.response) { | ||||||
|  |           forums.data.push({ | ||||||
|  |             value: forum.id, | ||||||
|  |             name: forum.id, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (more); | ||||||
|  |  | ||||||
|  |     return forums; | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										20
									
								
								packages/backend/src/apps/disqus/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								packages/backend/src/apps/disqus/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | import defineApp from '../../helpers/define-app.js'; | ||||||
|  | import addAuthHeader from './common/add-auth-header.js'; | ||||||
|  | import auth from './auth/index.js'; | ||||||
|  | import dynamicData from './dynamic-data/index.js'; | ||||||
|  | import triggers from './triggers/index.js'; | ||||||
|  |  | ||||||
|  | export default defineApp({ | ||||||
|  |   name: 'Disqus', | ||||||
|  |   key: 'disqus', | ||||||
|  |   baseUrl: 'https://disqus.com', | ||||||
|  |   apiBaseUrl: 'https://disqus.com/api', | ||||||
|  |   iconUrl: '{BASE_URL}/apps/disqus/assets/favicon.svg', | ||||||
|  |   authDocUrl: '{DOCS_URL}/apps/disqus/connection', | ||||||
|  |   primaryColor: '2E9FFF', | ||||||
|  |   supportsConnections: true, | ||||||
|  |   beforeRequest: [addAuthHeader], | ||||||
|  |   auth, | ||||||
|  |   dynamicData, | ||||||
|  |   triggers, | ||||||
|  | }); | ||||||
							
								
								
									
										4
									
								
								packages/backend/src/apps/disqus/triggers/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								packages/backend/src/apps/disqus/triggers/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | import newComments from './new-comments/index.js'; | ||||||
|  | import newFlaggedComments from './new-flagged-comments/index.js'; | ||||||
|  |  | ||||||
|  | export default [newComments, newFlaggedComments]; | ||||||
| @@ -0,0 +1,92 @@ | |||||||
|  | import defineTrigger from '../../../../helpers/define-trigger.js'; | ||||||
|  | import { URLSearchParams } from 'url'; | ||||||
|  |  | ||||||
|  | export default defineTrigger({ | ||||||
|  |   name: 'New comments', | ||||||
|  |   key: 'newComments', | ||||||
|  |   pollInterval: 15, | ||||||
|  |   description: 'Triggers when a new comment is posted in a forum using Disqus.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Post Types', | ||||||
|  |       key: 'postTypes', | ||||||
|  |       type: 'dynamic', | ||||||
|  |       required: false, | ||||||
|  |       description: | ||||||
|  |         'Which posts should be considered for inclusion in the trigger?', | ||||||
|  |       fields: [ | ||||||
|  |         { | ||||||
|  |           label: 'Type', | ||||||
|  |           key: 'type', | ||||||
|  |           type: 'dropdown', | ||||||
|  |           required: false, | ||||||
|  |           description: '', | ||||||
|  |           variables: true, | ||||||
|  |           options: [ | ||||||
|  |             { label: 'Unapproved Posts', value: 'unapproved' }, | ||||||
|  |             { label: 'Approved Posts', value: 'approved' }, | ||||||
|  |             { label: 'Spam Posts', value: 'spam' }, | ||||||
|  |             { label: 'Deleted Posts', value: 'deleted' }, | ||||||
|  |             { label: 'Flagged Posts', value: 'flagged' }, | ||||||
|  |             { label: 'Highlighted Posts', value: 'highlighted' }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Forum', | ||||||
|  |       key: 'forumId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: 'Select the forum where you want comments to be triggered.', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listForums', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const forumId = $.step.parameters.forumId; | ||||||
|  |     const postTypes = $.step.parameters.postTypes; | ||||||
|  |     const formattedCommentTypes = postTypes | ||||||
|  |       .filter((type) => type.type !== '') | ||||||
|  |       .map((type) => type.type); | ||||||
|  |  | ||||||
|  |     const params = new URLSearchParams({ | ||||||
|  |       limit: '100', | ||||||
|  |       forum: forumId, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (formattedCommentTypes.length) { | ||||||
|  |       formattedCommentTypes.forEach((type) => params.append('include', type)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let more; | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get( | ||||||
|  |         `/3.0/posts/list.json?${params.toString()}` | ||||||
|  |       ); | ||||||
|  |       params.set('cursor', data.cursor.next); | ||||||
|  |       more = data.cursor.hasNext; | ||||||
|  |  | ||||||
|  |       if (data.response?.length) { | ||||||
|  |         for (const comment of data.response) { | ||||||
|  |           $.pushTriggerItem({ | ||||||
|  |             raw: comment, | ||||||
|  |             meta: { | ||||||
|  |               internalId: comment.id, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (more); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -0,0 +1,60 @@ | |||||||
|  | import defineTrigger from '../../../../helpers/define-trigger.js'; | ||||||
|  | import { URLSearchParams } from 'url'; | ||||||
|  |  | ||||||
|  | export default defineTrigger({ | ||||||
|  |   name: 'New flagged comments', | ||||||
|  |   key: 'newFlaggedComments', | ||||||
|  |   pollInterval: 15, | ||||||
|  |   description: 'Triggers when a Disqus comment is marked with a flag', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Forum', | ||||||
|  |       key: 'forumId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: 'Select the forum where you want comments to be triggered.', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listForums', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const forumId = $.step.parameters.forumId; | ||||||
|  |     const isFlaggedFilter = 5; | ||||||
|  |  | ||||||
|  |     const params = new URLSearchParams({ | ||||||
|  |       limit: 100, | ||||||
|  |       forum: forumId, | ||||||
|  |       filters: [isFlaggedFilter], | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     let more; | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get( | ||||||
|  |         `/3.0/posts/list.json?${params.toString()}` | ||||||
|  |       ); | ||||||
|  |       params.set('cursor', data.cursor.next); | ||||||
|  |       more = data.cursor.hasNext; | ||||||
|  |  | ||||||
|  |       if (data.response?.length) { | ||||||
|  |         for (const comment of data.response) { | ||||||
|  |           $.pushTriggerItem({ | ||||||
|  |             raw: comment, | ||||||
|  |             meta: { | ||||||
|  |               internalId: comment.id, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (more); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'Dropbox', |   name: 'Dropbox', | ||||||
|   key: 'dropbox', |   key: 'dropbox', | ||||||
|   iconUrl: '{BASE_URL}/apps/dropbox/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/dropbox/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/dropbox/connection', |   authDocUrl: '{DOCS_URL}/apps/dropbox/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://dropbox.com', |   baseUrl: 'https://dropbox.com', | ||||||
|   apiBaseUrl: 'https://api.dropboxapi.com', |   apiBaseUrl: 'https://api.dropboxapi.com', | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ export default defineApp({ | |||||||
|   name: 'Filter', |   name: 'Filter', | ||||||
|   key: 'filter', |   key: 'filter', | ||||||
|   iconUrl: '{BASE_URL}/apps/filter/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/filter/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/filter/connection', |   authDocUrl: '{DOCS_URL}/apps/filter/connection', | ||||||
|   supportsConnections: false, |   supportsConnections: false, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ export default defineApp({ | |||||||
|   name: 'Flickr', |   name: 'Flickr', | ||||||
|   key: 'flickr', |   key: 'flickr', | ||||||
|   iconUrl: '{BASE_URL}/apps/flickr/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/flickr/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/flickr/connection', |   authDocUrl: '{DOCS_URL}/apps/flickr/connection', | ||||||
|   docUrl: 'https://automatisch.io/docs/flickr', |   docUrl: 'https://automatisch.io/docs/flickr', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'Flowers Software', |   name: 'Flowers Software', | ||||||
|   key: 'flowers-software', |   key: 'flowers-software', | ||||||
|   iconUrl: '{BASE_URL}/apps/flowers-software/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/flowers-software/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/flowers-software/connection', |   authDocUrl: '{DOCS_URL}/apps/flowers-software/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://flowers-software.com', |   baseUrl: 'https://flowers-software.com', | ||||||
|   apiBaseUrl: 'https://webapp.flowers-software.com/api', |   apiBaseUrl: 'https://webapp.flowers-software.com/api', | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import defineAction from '../../../../helpers/define-action.js'; | |||||||
|  |  | ||||||
| import base64ToString from './transformers/base64-to-string.js'; | import base64ToString from './transformers/base64-to-string.js'; | ||||||
| import capitalize from './transformers/capitalize.js'; | import capitalize from './transformers/capitalize.js'; | ||||||
|  | import encodeUriComponent from './transformers/encode-uri-component.js'; | ||||||
| import extractEmailAddress from './transformers/extract-email-address.js'; | import extractEmailAddress from './transformers/extract-email-address.js'; | ||||||
| import extractNumber from './transformers/extract-number.js'; | import extractNumber from './transformers/extract-number.js'; | ||||||
| import htmlToMarkdown from './transformers/html-to-markdown.js'; | import htmlToMarkdown from './transformers/html-to-markdown.js'; | ||||||
| @@ -10,12 +11,14 @@ import markdownToHtml from './transformers/markdown-to-html.js'; | |||||||
| import pluralize from './transformers/pluralize.js'; | import pluralize from './transformers/pluralize.js'; | ||||||
| import replace from './transformers/replace.js'; | import replace from './transformers/replace.js'; | ||||||
| import stringToBase64 from './transformers/string-to-base64.js'; | import stringToBase64 from './transformers/string-to-base64.js'; | ||||||
|  | import encodeUri from './transformers/encode-uri.js'; | ||||||
| import trimWhitespace from './transformers/trim-whitespace.js'; | import trimWhitespace from './transformers/trim-whitespace.js'; | ||||||
| import useDefaultValue from './transformers/use-default-value.js'; | import useDefaultValue from './transformers/use-default-value.js'; | ||||||
|  |  | ||||||
| const transformers = { | const transformers = { | ||||||
|   base64ToString, |   base64ToString, | ||||||
|   capitalize, |   capitalize, | ||||||
|  |   encodeUriComponent, | ||||||
|   extractEmailAddress, |   extractEmailAddress, | ||||||
|   extractNumber, |   extractNumber, | ||||||
|   htmlToMarkdown, |   htmlToMarkdown, | ||||||
| @@ -24,6 +27,7 @@ const transformers = { | |||||||
|   pluralize, |   pluralize, | ||||||
|   replace, |   replace, | ||||||
|   stringToBase64, |   stringToBase64, | ||||||
|  |   encodeUri, | ||||||
|   trimWhitespace, |   trimWhitespace, | ||||||
|   useDefaultValue, |   useDefaultValue, | ||||||
| }; | }; | ||||||
| @@ -43,6 +47,10 @@ export default defineAction({ | |||||||
|       options: [ |       options: [ | ||||||
|         { label: 'Base64 to String', value: 'base64ToString' }, |         { label: 'Base64 to String', value: 'base64ToString' }, | ||||||
|         { label: 'Capitalize', value: 'capitalize' }, |         { label: 'Capitalize', value: 'capitalize' }, | ||||||
|  |         { | ||||||
|  |           label: 'Encode URI Component', | ||||||
|  |           value: 'encodeUriComponent', | ||||||
|  |         }, | ||||||
|         { label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' }, |         { label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' }, | ||||||
|         { label: 'Convert Markdown to HTML', value: 'markdownToHtml' }, |         { label: 'Convert Markdown to HTML', value: 'markdownToHtml' }, | ||||||
|         { label: 'Extract Email Address', value: 'extractEmailAddress' }, |         { label: 'Extract Email Address', value: 'extractEmailAddress' }, | ||||||
| @@ -51,6 +59,7 @@ export default defineAction({ | |||||||
|         { label: 'Pluralize', value: 'pluralize' }, |         { label: 'Pluralize', value: 'pluralize' }, | ||||||
|         { label: 'Replace', value: 'replace' }, |         { label: 'Replace', value: 'replace' }, | ||||||
|         { label: 'String to Base64', value: 'stringToBase64' }, |         { label: 'String to Base64', value: 'stringToBase64' }, | ||||||
|  |         { label: 'Encode URI', value: 'encodeUri' }, | ||||||
|         { label: 'Trim Whitespace', value: 'trimWhitespace' }, |         { label: 'Trim Whitespace', value: 'trimWhitespace' }, | ||||||
|         { label: 'Use Default Value', value: 'useDefaultValue' }, |         { label: 'Use Default Value', value: 'useDefaultValue' }, | ||||||
|       ], |       ], | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | const encodeUriComponent = ($) => { | ||||||
|  |   const input = $.step.parameters.input; | ||||||
|  |   const encodedString = encodeURIComponent(input); | ||||||
|  |  | ||||||
|  |   return encodedString; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default encodeUriComponent; | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | const encodeUri = ($) => { | ||||||
|  |   const input = $.step.parameters.input; | ||||||
|  |   const encodedString = encodeURI(input); | ||||||
|  |  | ||||||
|  |   return encodedString; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default encodeUri; | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| import base64ToString from './text/base64-to-string.js'; | import base64ToString from './text/base64-to-string.js'; | ||||||
| import capitalize from './text/capitalize.js'; | import capitalize from './text/capitalize.js'; | ||||||
|  | import encodeUriComponent from './text/encode-uri-component.js'; | ||||||
| import extractEmailAddress from './text/extract-email-address.js'; | import extractEmailAddress from './text/extract-email-address.js'; | ||||||
| import extractNumber from './text/extract-number.js'; | import extractNumber from './text/extract-number.js'; | ||||||
| import htmlToMarkdown from './text/html-to-markdown.js'; | import htmlToMarkdown from './text/html-to-markdown.js'; | ||||||
| @@ -8,6 +9,7 @@ import markdownToHtml from './text/markdown-to-html.js'; | |||||||
| import pluralize from './text/pluralize.js'; | import pluralize from './text/pluralize.js'; | ||||||
| import replace from './text/replace.js'; | import replace from './text/replace.js'; | ||||||
| import stringToBase64 from './text/string-to-base64.js'; | import stringToBase64 from './text/string-to-base64.js'; | ||||||
|  | import encodeUri from './text/encode-uri.js'; | ||||||
| import trimWhitespace from './text/trim-whitespace.js'; | import trimWhitespace from './text/trim-whitespace.js'; | ||||||
| import useDefaultValue from './text/use-default-value.js'; | import useDefaultValue from './text/use-default-value.js'; | ||||||
| import performMathOperation from './numbers/perform-math-operation.js'; | import performMathOperation from './numbers/perform-math-operation.js'; | ||||||
| @@ -19,6 +21,7 @@ import formatDateTime from './date-time/format-date-time.js'; | |||||||
| const options = { | const options = { | ||||||
|   base64ToString, |   base64ToString, | ||||||
|   capitalize, |   capitalize, | ||||||
|  |   encodeUriComponent, | ||||||
|   extractEmailAddress, |   extractEmailAddress, | ||||||
|   extractNumber, |   extractNumber, | ||||||
|   htmlToMarkdown, |   htmlToMarkdown, | ||||||
| @@ -27,6 +30,7 @@ const options = { | |||||||
|   pluralize, |   pluralize, | ||||||
|   replace, |   replace, | ||||||
|   stringToBase64, |   stringToBase64, | ||||||
|  |   encodeUri, | ||||||
|   trimWhitespace, |   trimWhitespace, | ||||||
|   useDefaultValue, |   useDefaultValue, | ||||||
|   performMathOperation, |   performMathOperation, | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | const encodeUriComponent = [ | ||||||
|  |   { | ||||||
|  |     label: 'Input', | ||||||
|  |     key: 'input', | ||||||
|  |     type: 'string', | ||||||
|  |     required: true, | ||||||
|  |     description: 'URI Component to encode', | ||||||
|  |     variables: true, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | export default encodeUriComponent; | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | const encodeUri = [ | ||||||
|  |   { | ||||||
|  |     label: 'Input', | ||||||
|  |     key: 'input', | ||||||
|  |     type: 'string', | ||||||
|  |     required: true, | ||||||
|  |     description: 'URI to encode', | ||||||
|  |     variables: true, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | export default encodeUri; | ||||||
| @@ -6,7 +6,7 @@ export default defineApp({ | |||||||
|   name: 'Formatter', |   name: 'Formatter', | ||||||
|   key: 'formatter', |   key: 'formatter', | ||||||
|   iconUrl: '{BASE_URL}/apps/formatter/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/formatter/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/formatter/connection', |   authDocUrl: '{DOCS_URL}/apps/formatter/connection', | ||||||
|   supportsConnections: false, |   supportsConnections: false, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://ghost.org', |   baseUrl: 'https://ghost.org', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   iconUrl: '{BASE_URL}/apps/ghost/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/ghost/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/ghost/connection', |   authDocUrl: '{DOCS_URL}/apps/ghost/connection', | ||||||
|   primaryColor: '15171A', |   primaryColor: '15171A', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://github.com', |   baseUrl: 'https://github.com', | ||||||
|   apiBaseUrl: 'https://api.github.com', |   apiBaseUrl: 'https://api.github.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/github/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/github/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/github/connection', |   authDocUrl: '{DOCS_URL}/apps/github/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://gitlab.com', |   baseUrl: 'https://gitlab.com', | ||||||
|   apiBaseUrl: 'https://gitlab.com', |   apiBaseUrl: 'https://gitlab.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/gitlab/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/gitlab/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/gitlab/connection', |   authDocUrl: '{DOCS_URL}/apps/gitlab/connection', | ||||||
|   primaryColor: 'FC6D26', |   primaryColor: 'FC6D26', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://calendar.google.com', |   baseUrl: 'https://calendar.google.com', | ||||||
|   apiBaseUrl: 'https://www.googleapis.com/calendar', |   apiBaseUrl: 'https://www.googleapis.com/calendar', | ||||||
|   iconUrl: '{BASE_URL}/apps/google-calendar/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/google-calendar/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/google-calendar/connection', |   authDocUrl: '{DOCS_URL}/apps/google-calendar/connection', | ||||||
|   primaryColor: '448AFF', |   primaryColor: '448AFF', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://drive.google.com', |   baseUrl: 'https://drive.google.com', | ||||||
|   apiBaseUrl: 'https://www.googleapis.com/drive', |   apiBaseUrl: 'https://www.googleapis.com/drive', | ||||||
|   iconUrl: '{BASE_URL}/apps/google-drive/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/google-drive/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/google-drive/connection', |   authDocUrl: '{DOCS_URL}/apps/google-drive/connection', | ||||||
|   primaryColor: '1FA463', |   primaryColor: '1FA463', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://docs.google.com/forms', |   baseUrl: 'https://docs.google.com/forms', | ||||||
|   apiBaseUrl: 'https://forms.googleapis.com', |   apiBaseUrl: 'https://forms.googleapis.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/google-forms/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/google-forms/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/google-forms/connection', |   authDocUrl: '{DOCS_URL}/apps/google-forms/connection', | ||||||
|   primaryColor: '673AB7', |   primaryColor: '673AB7', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://docs.google.com/spreadsheets', |   baseUrl: 'https://docs.google.com/spreadsheets', | ||||||
|   apiBaseUrl: 'https://sheets.googleapis.com', |   apiBaseUrl: 'https://sheets.googleapis.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/google-sheets/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/google-sheets/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/google-sheets/connection', |   authDocUrl: '{DOCS_URL}/apps/google-sheets/connection', | ||||||
|   primaryColor: '0F9D58', |   primaryColor: '0F9D58', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Create task list', | ||||||
|  |   key: 'createTaskList', | ||||||
|  |   description: 'Creates a new task list.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'List Title', | ||||||
|  |       key: 'listTitle', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const listTitle = $.step.parameters.listTitle; | ||||||
|  |  | ||||||
|  |     const body = { | ||||||
|  |       title: listTitle, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const { data } = await $.http.post('/tasks/v1/users/@me/lists', body); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: data, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -0,0 +1,70 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Create task', | ||||||
|  |   key: 'createTask', | ||||||
|  |   description: 'Creates a new task.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Task List', | ||||||
|  |       key: 'taskListId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listTaskLists', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Title', | ||||||
|  |       key: 'title', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Notes', | ||||||
|  |       key: 'notes', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Due Date', | ||||||
|  |       key: 'due', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: 'RFC 3339 timestamp.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const { taskListId, title, notes, due } = $.step.parameters; | ||||||
|  |  | ||||||
|  |     const body = { | ||||||
|  |       title, | ||||||
|  |       notes, | ||||||
|  |       due, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const { data } = await $.http.post( | ||||||
|  |       `/tasks/v1/lists/${taskListId}/tasks`, | ||||||
|  |       body | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: data, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Find task', | ||||||
|  |   key: 'findTask', | ||||||
|  |   description: 'Looking for a specific task.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Task List', | ||||||
|  |       key: 'taskListId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: 'The list to be searched.', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listTaskLists', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Title', | ||||||
|  |       key: 'title', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const taskListId = $.step.parameters.taskListId; | ||||||
|  |     const title = $.step.parameters.title; | ||||||
|  |  | ||||||
|  |     const params = { | ||||||
|  |       showCompleted: true, | ||||||
|  |       showHidden: true, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const { data } = await $.http.get(`/tasks/v1/lists/${taskListId}/tasks`, { | ||||||
|  |       params, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const filteredTask = data.items?.filter((task) => | ||||||
|  |       task.title.includes(title) | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: filteredTask[0], | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
							
								
								
									
										6
									
								
								packages/backend/src/apps/google-tasks/actions/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/backend/src/apps/google-tasks/actions/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | import createTask from './create-task/index.js'; | ||||||
|  | import createTaskList from './create-task-list/index.js'; | ||||||
|  | import findTask from './find-task/index.js'; | ||||||
|  | import updateTask from './update-task/index.js'; | ||||||
|  |  | ||||||
|  | export default [createTask, createTaskList, findTask, updateTask]; | ||||||
| @@ -0,0 +1,108 @@ | |||||||
|  | import defineAction from '../../../../helpers/define-action.js'; | ||||||
|  |  | ||||||
|  | export default defineAction({ | ||||||
|  |   name: 'Update task', | ||||||
|  |   key: 'updateTask', | ||||||
|  |   description: 'Updates an existing task.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Task List', | ||||||
|  |       key: 'taskListId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listTaskLists', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Task', | ||||||
|  |       key: 'taskId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: 'Ensure that you choose a list before proceeding.', | ||||||
|  |       variables: true, | ||||||
|  |       dependsOn: ['parameters.taskListId'], | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listTasks', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'parameters.taskListId', | ||||||
|  |             value: '{parameters.taskListId}', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Title', | ||||||
|  |       key: 'title', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: 'Provide a new title for the revised task.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Status', | ||||||
|  |       key: 'status', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: false, | ||||||
|  |       description: | ||||||
|  |         'Specify the status of the updated task. If you opt for a custom value, enter either "needsAttention" or "completed."', | ||||||
|  |       variables: true, | ||||||
|  |       options: [ | ||||||
|  |         { label: 'Incomplete', value: 'needsAction' }, | ||||||
|  |         { label: 'Complete', value: 'completed' }, | ||||||
|  |       ], | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Notes', | ||||||
|  |       key: 'notes', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: 'Provide a note for the revised task.', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       label: 'Due Date', | ||||||
|  |       key: 'due', | ||||||
|  |       type: 'string', | ||||||
|  |       required: false, | ||||||
|  |       description: | ||||||
|  |         'Specify the deadline for the task (as a RFC 3339 timestamp).', | ||||||
|  |       variables: true, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const { taskListId, taskId, title, status, notes, due } = $.step.parameters; | ||||||
|  |  | ||||||
|  |     const body = { | ||||||
|  |       title, | ||||||
|  |       status, | ||||||
|  |       notes, | ||||||
|  |       due, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const { data } = await $.http.patch( | ||||||
|  |       `/tasks/v1/lists/${taskListId}/tasks/${taskId}`, | ||||||
|  |       body | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     $.setActionItem({ | ||||||
|  |       raw: data, | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
							
								
								
									
										16
									
								
								packages/backend/src/apps/google-tasks/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/backend/src/apps/google-tasks/assets/favicon.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <svg version="1.1" | ||||||
|  | 	 id="svg8849" inkscape:version="1.1 (c68e22c387, 2021-05-23)" sodipodi:docname="google tasks.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" | ||||||
|  | 	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="527.1px" height="500px" | ||||||
|  | 	 viewBox="0 0 527.1 500" enable-background="new 0 0 527.1 500" xml:space="preserve"> | ||||||
|  | <sodipodi:namedview  bordercolor="#eeeeee" borderopacity="1" id="namedview8851" inkscape:bbox-nodes="true" inkscape:bbox-paths="true" inkscape:current-layer="layer1" inkscape:cx="280.62992" inkscape:cy="277.14384" inkscape:document-units="mm" inkscape:object-paths="true" inkscape:pagecheckerboard="0" inkscape:pageopacity="0" inkscape:pageshadow="0" inkscape:snap-bbox="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-center="true" inkscape:snap-global="true" inkscape:snap-intersection-paths="true" inkscape:snap-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-page="true" inkscape:snap-smooth-nodes="true" inkscape:snap-text-baseline="true" inkscape:window-height="1009" inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="1912" inkscape:window-y="760" inkscape:zoom="1.4342733" pagecolor="#505050" showgrid="false" units="px"> | ||||||
|  | 	</sodipodi:namedview> | ||||||
|  | <g> | ||||||
|  | 	<polygon fill="#0066DA" points="410.4,58.3 368.8,81.2 348.2,120.6 368.8,168.8 407.8,211 450,187.5 475.9,142.8 450,87.5 	"/> | ||||||
|  | 	<path fill="#2684FC" d="M249.3,219.4l98.9-98.9c29.1,22.1,50.5,53.8,59.6,90.4L272.1,346.7c-12.2,12.2-32,12.2-44.2,0l-91.5-91.5 | ||||||
|  | 		c-9.8-9.8-9.8-25.6,0-35.3l39-39c9.8-9.8,25.6-9.8,35.3,0L249.3,219.4z M519.8,63.6l-39.7-39.7c-9.7-9.7-25.6-9.7-35.3,0 | ||||||
|  | 		l-34.4,34.4c27.5,23,49.9,51.8,65.5,84.5l43.9-43.9C529.6,89.2,529.6,73.3,519.8,63.6z M412.5,250c0,89.8-72.8,162.5-162.5,162.5 | ||||||
|  | 		S87.5,339.8,87.5,250S160.2,87.5,250,87.5c36.9,0,70.9,12.3,98.2,33.1l62.2-62.2C367,21.9,311.1,0,250,0C111.9,0,0,111.9,0,250 | ||||||
|  | 		s111.9,250,250,250s250-111.9,250-250c0-38.3-8.7-74.7-24.1-107.2L407.8,211C410.8,223.5,412.5,236.6,412.5,250z"/> | ||||||
|  | </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 2.2 KiB | 
| @@ -0,0 +1,23 @@ | |||||||
|  | import { URLSearchParams } from 'url'; | ||||||
|  | import authScope from '../common/auth-scope.js'; | ||||||
|  |  | ||||||
|  | export default async function generateAuthUrl($) { | ||||||
|  |   const oauthRedirectUrlField = $.app.auth.fields.find( | ||||||
|  |     (field) => field.key == 'oAuthRedirectUrl' | ||||||
|  |   ); | ||||||
|  |   const redirectUri = oauthRedirectUrlField.value; | ||||||
|  |   const searchParams = new URLSearchParams({ | ||||||
|  |     client_id: $.auth.data.clientId, | ||||||
|  |     redirect_uri: redirectUri, | ||||||
|  |     prompt: 'select_account', | ||||||
|  |     scope: authScope.join(' '), | ||||||
|  |     response_type: 'code', | ||||||
|  |     access_type: 'offline', | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const url = `https://accounts.google.com/o/oauth2/v2/auth?${searchParams.toString()}`; | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     url, | ||||||
|  |   }); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								packages/backend/src/apps/google-tasks/auth/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								packages/backend/src/apps/google-tasks/auth/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | import generateAuthUrl from './generate-auth-url.js'; | ||||||
|  | import verifyCredentials from './verify-credentials.js'; | ||||||
|  | import refreshToken from './refresh-token.js'; | ||||||
|  | import isStillVerified from './is-still-verified.js'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   fields: [ | ||||||
|  |     { | ||||||
|  |       key: 'oAuthRedirectUrl', | ||||||
|  |       label: 'OAuth Redirect URL', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: true, | ||||||
|  |       value: '{WEB_APP_URL}/app/google-tasks/connections/add', | ||||||
|  |       placeholder: null, | ||||||
|  |       description: | ||||||
|  |         'When asked to input a redirect URL in Google Cloud, enter the URL above.', | ||||||
|  |       clickToCopy: true, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'clientId', | ||||||
|  |       label: 'Client ID', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       key: 'clientSecret', | ||||||
|  |       label: 'Client Secret', | ||||||
|  |       type: 'string', | ||||||
|  |       required: true, | ||||||
|  |       readOnly: false, | ||||||
|  |       value: null, | ||||||
|  |       placeholder: null, | ||||||
|  |       description: null, | ||||||
|  |       clickToCopy: false, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   generateAuthUrl, | ||||||
|  |   verifyCredentials, | ||||||
|  |   isStillVerified, | ||||||
|  |   refreshToken, | ||||||
|  | }; | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | import getCurrentUser from '../common/get-current-user.js'; | ||||||
|  |  | ||||||
|  | const isStillVerified = async ($) => { | ||||||
|  |   const currentUser = await getCurrentUser($); | ||||||
|  |   return !!currentUser.resourceName; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default isStillVerified; | ||||||
							
								
								
									
										25
									
								
								packages/backend/src/apps/google-tasks/auth/refresh-token.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								packages/backend/src/apps/google-tasks/auth/refresh-token.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | import { URLSearchParams } from 'node:url'; | ||||||
|  | import authScope from '../common/auth-scope.js'; | ||||||
|  |  | ||||||
|  | const refreshToken = async ($) => { | ||||||
|  |   const params = new URLSearchParams({ | ||||||
|  |     client_id: $.auth.data.clientId, | ||||||
|  |     client_secret: $.auth.data.clientSecret, | ||||||
|  |     grant_type: 'refresh_token', | ||||||
|  |     refresh_token: $.auth.data.refreshToken, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const { data } = await $.http.post( | ||||||
|  |     'https://oauth2.googleapis.com/token', | ||||||
|  |     params.toString() | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     accessToken: data.access_token, | ||||||
|  |     expiresIn: data.expires_in, | ||||||
|  |     scope: authScope.join(' '), | ||||||
|  |     tokenType: data.token_type, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default refreshToken; | ||||||
| @@ -0,0 +1,42 @@ | |||||||
|  | import getCurrentUser from '../common/get-current-user.js'; | ||||||
|  |  | ||||||
|  | const verifyCredentials = async ($) => { | ||||||
|  |   const oauthRedirectUrlField = $.app.auth.fields.find( | ||||||
|  |     (field) => field.key == 'oAuthRedirectUrl' | ||||||
|  |   ); | ||||||
|  |   const redirectUri = oauthRedirectUrlField.value; | ||||||
|  |   const { data } = await $.http.post(`https://oauth2.googleapis.com/token`, { | ||||||
|  |     client_id: $.auth.data.clientId, | ||||||
|  |     client_secret: $.auth.data.clientSecret, | ||||||
|  |     code: $.auth.data.code, | ||||||
|  |     grant_type: 'authorization_code', | ||||||
|  |     redirect_uri: redirectUri, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     accessToken: data.access_token, | ||||||
|  |     tokenType: data.token_type, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const currentUser = await getCurrentUser($); | ||||||
|  |  | ||||||
|  |   const { displayName } = currentUser.names.find( | ||||||
|  |     (name) => name.metadata.primary | ||||||
|  |   ); | ||||||
|  |   const { value: email } = currentUser.emailAddresses.find( | ||||||
|  |     (emailAddress) => emailAddress.metadata.primary | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   await $.auth.set({ | ||||||
|  |     clientId: $.auth.data.clientId, | ||||||
|  |     clientSecret: $.auth.data.clientSecret, | ||||||
|  |     scope: $.auth.data.scope, | ||||||
|  |     idToken: data.id_token, | ||||||
|  |     expiresIn: data.expires_in, | ||||||
|  |     refreshToken: data.refresh_token, | ||||||
|  |     resourceName: currentUser.resourceName, | ||||||
|  |     screenName: `${displayName} - ${email}`, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default verifyCredentials; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | const addAuthHeader = ($, requestConfig) => { | ||||||
|  |   if ($.auth.data?.accessToken) { | ||||||
|  |     requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return requestConfig; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default addAuthHeader; | ||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | const authScope = [ | ||||||
|  |   'https://www.googleapis.com/auth/tasks', | ||||||
|  |   'https://www.googleapis.com/auth/userinfo.email', | ||||||
|  |   'https://www.googleapis.com/auth/userinfo.profile', | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | export default authScope; | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | const getCurrentUser = async ($) => { | ||||||
|  |   const { data: currentUser } = await $.http.get( | ||||||
|  |     'https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses' | ||||||
|  |   ); | ||||||
|  |   return currentUser; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export default getCurrentUser; | ||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | import listTaskLists from './list-task-lists/index.js'; | ||||||
|  | import listTasks from './list-tasks/index.js'; | ||||||
|  |  | ||||||
|  | export default [listTaskLists, listTasks]; | ||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | export default { | ||||||
|  |   name: 'List task lists', | ||||||
|  |   key: 'listTaskLists', | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const taskLists = { | ||||||
|  |       data: [], | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     const params = { | ||||||
|  |       maxResults: 100, | ||||||
|  |       pageToken: undefined, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get('/tasks/v1/users/@me/lists', { | ||||||
|  |         params, | ||||||
|  |       }); | ||||||
|  |       params.pageToken = data.nextPageToken; | ||||||
|  |  | ||||||
|  |       if (data.items) { | ||||||
|  |         for (const taskList of data.items) { | ||||||
|  |           taskLists.data.push({ | ||||||
|  |             value: taskList.id, | ||||||
|  |             name: taskList.title, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (params.pageToken); | ||||||
|  |  | ||||||
|  |     return taskLists; | ||||||
|  |   }, | ||||||
|  | }; | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | export default { | ||||||
|  |   name: 'List tasks', | ||||||
|  |   key: 'listTasks', | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const tasks = { | ||||||
|  |       data: [], | ||||||
|  |     }; | ||||||
|  |     const taskListId = $.step.parameters.taskListId; | ||||||
|  |  | ||||||
|  |     const params = { | ||||||
|  |       maxResults: 100, | ||||||
|  |       pageToken: undefined, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (!taskListId) { | ||||||
|  |       return tasks; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get(`/tasks/v1/lists/${taskListId}/tasks`, { | ||||||
|  |         params, | ||||||
|  |       }); | ||||||
|  |       params.pageToken = data.nextPageToken; | ||||||
|  |  | ||||||
|  |       if (data.items) { | ||||||
|  |         for (const task of data.items) { | ||||||
|  |           if (task.title !== '') { | ||||||
|  |             tasks.data.push({ | ||||||
|  |               value: task.id, | ||||||
|  |               name: task.title, | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (params.pageToken); | ||||||
|  |  | ||||||
|  |     return tasks; | ||||||
|  |   }, | ||||||
|  | }; | ||||||
							
								
								
									
										22
									
								
								packages/backend/src/apps/google-tasks/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								packages/backend/src/apps/google-tasks/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import defineApp from '../../helpers/define-app.js'; | ||||||
|  | import addAuthHeader from './common/add-auth-header.js'; | ||||||
|  | import auth from './auth/index.js'; | ||||||
|  | import actions from './actions/index.js'; | ||||||
|  | import triggers from './triggers/index.js'; | ||||||
|  | import dynamicData from './dynamic-data/index.js'; | ||||||
|  |  | ||||||
|  | export default defineApp({ | ||||||
|  |   name: 'Google Tasks', | ||||||
|  |   key: 'google-tasks', | ||||||
|  |   baseUrl: 'https://calendar.google.com/calendar/u/0/r/tasks', | ||||||
|  |   apiBaseUrl: 'https://tasks.googleapis.com', | ||||||
|  |   iconUrl: '{BASE_URL}/apps/google-tasks/assets/favicon.svg', | ||||||
|  |   authDocUrl: '{DOCS_URL}/apps/google-tasks/connection', | ||||||
|  |   primaryColor: '0066DA', | ||||||
|  |   supportsConnections: true, | ||||||
|  |   beforeRequest: [addAuthHeader], | ||||||
|  |   auth, | ||||||
|  |   actions, | ||||||
|  |   dynamicData, | ||||||
|  |   triggers, | ||||||
|  | }); | ||||||
							
								
								
									
										5
									
								
								packages/backend/src/apps/google-tasks/triggers/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								packages/backend/src/apps/google-tasks/triggers/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import newCompletedTasks from './new-completed-tasks/index.js'; | ||||||
|  | import newTaskLists from './new-task-lists/index.js'; | ||||||
|  | import newTasks from './new-tasks/index.js'; | ||||||
|  |  | ||||||
|  | export default [newCompletedTasks, newTaskLists, newTasks]; | ||||||
| @@ -0,0 +1,59 @@ | |||||||
|  | import defineTrigger from '../../../../helpers/define-trigger.js'; | ||||||
|  |  | ||||||
|  | export default defineTrigger({ | ||||||
|  |   name: 'New completed tasks', | ||||||
|  |   key: 'newCompletedTasks', | ||||||
|  |   pollInterval: 15, | ||||||
|  |   description: 'Triggers when a task is finished within a specified task list.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Task List', | ||||||
|  |       key: 'taskListId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listTaskLists', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const taskListId = $.step.parameters.taskListId; | ||||||
|  |  | ||||||
|  |     const params = { | ||||||
|  |       maxResults: 100, | ||||||
|  |       showCompleted: true, | ||||||
|  |       showHidden: true, | ||||||
|  |       pageToken: undefined, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get(`/tasks/v1/lists/${taskListId}/tasks`, { | ||||||
|  |         params, | ||||||
|  |       }); | ||||||
|  |       params.pageToken = data.nextPageToken; | ||||||
|  |  | ||||||
|  |       if (data.items?.length) { | ||||||
|  |         for (const task of data.items) { | ||||||
|  |           if (task.status === 'completed') { | ||||||
|  |             $.pushTriggerItem({ | ||||||
|  |               raw: task, | ||||||
|  |               meta: { | ||||||
|  |                 internalId: task.id, | ||||||
|  |               }, | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (params.pageToken); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | import defineTrigger from '../../../../helpers/define-trigger.js'; | ||||||
|  |  | ||||||
|  | export default defineTrigger({ | ||||||
|  |   name: 'New task lists', | ||||||
|  |   key: 'newTaskLists', | ||||||
|  |   pollInterval: 15, | ||||||
|  |   description: 'Triggers when a new task list is created.', | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const params = { | ||||||
|  |       maxResults: 100, | ||||||
|  |       pageToken: undefined, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get('/tasks/v1/users/@me/lists'); | ||||||
|  |       params.pageToken = data.nextPageToken; | ||||||
|  |  | ||||||
|  |       if (data.items?.length) { | ||||||
|  |         for (const taskList of data.items.reverse()) { | ||||||
|  |           $.pushTriggerItem({ | ||||||
|  |             raw: taskList, | ||||||
|  |             meta: { | ||||||
|  |               internalId: taskList.id, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (params.pageToken); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -0,0 +1,53 @@ | |||||||
|  | import defineTrigger from '../../../../helpers/define-trigger.js'; | ||||||
|  |  | ||||||
|  | export default defineTrigger({ | ||||||
|  |   name: 'New tasks', | ||||||
|  |   key: 'newTasks', | ||||||
|  |   pollInterval: 15, | ||||||
|  |   description: 'Triggers when a new task is created.', | ||||||
|  |   arguments: [ | ||||||
|  |     { | ||||||
|  |       label: 'Task List', | ||||||
|  |       key: 'taskListId', | ||||||
|  |       type: 'dropdown', | ||||||
|  |       required: true, | ||||||
|  |       description: '', | ||||||
|  |       variables: true, | ||||||
|  |       source: { | ||||||
|  |         type: 'query', | ||||||
|  |         name: 'getDynamicData', | ||||||
|  |         arguments: [ | ||||||
|  |           { | ||||||
|  |             name: 'key', | ||||||
|  |             value: 'listTaskLists', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   async run($) { | ||||||
|  |     const taskListId = $.step.parameters.taskListId; | ||||||
|  |  | ||||||
|  |     const params = { | ||||||
|  |       maxResults: 100, | ||||||
|  |       pageToken: undefined, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |       const { data } = await $.http.get(`/tasks/v1/lists/${taskListId}/tasks`); | ||||||
|  |       params.pageToken = data.nextPageToken; | ||||||
|  |  | ||||||
|  |       if (data.items?.length) { | ||||||
|  |         for (const task of data.items) { | ||||||
|  |           $.pushTriggerItem({ | ||||||
|  |             raw: task, | ||||||
|  |             meta: { | ||||||
|  |               internalId: task.id, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } while (params.pageToken); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://tryhelix.ai', |   baseUrl: 'https://tryhelix.ai', | ||||||
|   apiBaseUrl: 'https://app.tryhelix.ai', |   apiBaseUrl: 'https://app.tryhelix.ai', | ||||||
|   iconUrl: '{BASE_URL}/apps/helix/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/helix/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/helix/connection', |   authDocUrl: '{DOCS_URL}/apps/helix/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ export default defineApp({ | |||||||
|   name: 'HTTP Request', |   name: 'HTTP Request', | ||||||
|   key: 'http-request', |   key: 'http-request', | ||||||
|   iconUrl: '{BASE_URL}/apps/http-request/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/http-request/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/http-request/connection', |   authDocUrl: '{DOCS_URL}/apps/http-request/connection', | ||||||
|   supportsConnections: false, |   supportsConnections: false, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'HubSpot', |   name: 'HubSpot', | ||||||
|   key: 'hubspot', |   key: 'hubspot', | ||||||
|   iconUrl: '{BASE_URL}/apps/hubspot/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/hubspot/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/hubspot/connection', |   authDocUrl: '{DOCS_URL}/apps/hubspot/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://www.hubspot.com', |   baseUrl: 'https://www.hubspot.com', | ||||||
|   apiBaseUrl: 'https://api.hubapi.com', |   apiBaseUrl: 'https://api.hubapi.com', | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://invoiceninja.com', |   baseUrl: 'https://invoiceninja.com', | ||||||
|   apiBaseUrl: 'https://invoicing.co/api', |   apiBaseUrl: 'https://invoicing.co/api', | ||||||
|   iconUrl: '{BASE_URL}/apps/invoice-ninja/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/invoice-ninja/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/invoice-ninja/connection', |   authDocUrl: '{DOCS_URL}/apps/invoice-ninja/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   name: 'Mattermost', |   name: 'Mattermost', | ||||||
|   key: 'mattermost', |   key: 'mattermost', | ||||||
|   iconUrl: '{BASE_URL}/apps/mattermost/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/mattermost/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/mattermost/connection', |   authDocUrl: '{DOCS_URL}/apps/mattermost/connection', | ||||||
|   baseUrl: 'https://mattermost.com', |   baseUrl: 'https://mattermost.com', | ||||||
|   apiBaseUrl: '', // there is no cloud version of this app, user always need to provide address of own instance when creating connection |   apiBaseUrl: '', // there is no cloud version of this app, user always need to provide address of own instance when creating connection | ||||||
|   primaryColor: '4a154b', |   primaryColor: '4a154b', | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://miro.com', |   baseUrl: 'https://miro.com', | ||||||
|   apiBaseUrl: 'https://api.miro.com', |   apiBaseUrl: 'https://api.miro.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/miro/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/miro/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/miro/connection', |   authDocUrl: '{DOCS_URL}/apps/miro/connection', | ||||||
|   primaryColor: 'F2CA02', |   primaryColor: 'F2CA02', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://notion.com', |   baseUrl: 'https://notion.com', | ||||||
|   apiBaseUrl: 'https://api.notion.com', |   apiBaseUrl: 'https://api.notion.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/notion/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/notion/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/notion/connection', |   authDocUrl: '{DOCS_URL}/apps/notion/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader, addNotionVersionHeader], |   beforeRequest: [addAuthHeader, addNotionVersionHeader], | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'Ntfy', |   name: 'Ntfy', | ||||||
|   key: 'ntfy', |   key: 'ntfy', | ||||||
|   iconUrl: '{BASE_URL}/apps/ntfy/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/ntfy/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/ntfy/connection', |   authDocUrl: '{DOCS_URL}/apps/ntfy/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://ntfy.sh', |   baseUrl: 'https://ntfy.sh', | ||||||
|   apiBaseUrl: 'https://ntfy.sh', |   apiBaseUrl: 'https://ntfy.sh', | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ export default defineApp({ | |||||||
|   name: 'Odoo', |   name: 'Odoo', | ||||||
|   key: 'odoo', |   key: 'odoo', | ||||||
|   iconUrl: '{BASE_URL}/apps/odoo/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/odoo/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/odoo/connection', |   authDocUrl: '{DOCS_URL}/apps/odoo/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://odoo.com', |   baseUrl: 'https://odoo.com', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://openai.com', |   baseUrl: 'https://openai.com', | ||||||
|   apiBaseUrl: 'https://api.openai.com', |   apiBaseUrl: 'https://api.openai.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/openai/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/openai/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/openai/connection', |   authDocUrl: '{DOCS_URL}/apps/openai/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ export default defineApp({ | |||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   iconUrl: '{BASE_URL}/apps/pipedrive/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/pipedrive/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/pipedrive/connection', |   authDocUrl: '{DOCS_URL}/apps/pipedrive/connection', | ||||||
|   primaryColor: 'FFFFFF', |   primaryColor: 'FFFFFF', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ export default defineApp({ | |||||||
|   name: 'Placetel', |   name: 'Placetel', | ||||||
|   key: 'placetel', |   key: 'placetel', | ||||||
|   iconUrl: '{BASE_URL}/apps/placetel/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/placetel/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/placetel/connection', |   authDocUrl: '{DOCS_URL}/apps/placetel/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://placetel.de', |   baseUrl: 'https://placetel.de', | ||||||
|   apiBaseUrl: 'https://api.placetel.de', |   apiBaseUrl: 'https://api.placetel.de', | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ export default defineApp({ | |||||||
|   name: 'PostgreSQL', |   name: 'PostgreSQL', | ||||||
|   key: 'postgresql', |   key: 'postgresql', | ||||||
|   iconUrl: '{BASE_URL}/apps/postgresql/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/postgresql/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/postgresql/connection', |   authDocUrl: '{DOCS_URL}/apps/postgresql/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://pushover.net', |   baseUrl: 'https://pushover.net', | ||||||
|   apiBaseUrl: 'https://api.pushover.net', |   apiBaseUrl: 'https://api.pushover.net', | ||||||
|   iconUrl: '{BASE_URL}/apps/pushover/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/pushover/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/pushover/connection', |   authDocUrl: '{DOCS_URL}/apps/pushover/connection', | ||||||
|   primaryColor: '249DF1', |   primaryColor: '249DF1', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   auth, |   auth, | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ export default defineApp({ | |||||||
|   baseUrl: 'https://www.reddit.com', |   baseUrl: 'https://www.reddit.com', | ||||||
|   apiBaseUrl: 'https://oauth.reddit.com', |   apiBaseUrl: 'https://oauth.reddit.com', | ||||||
|   iconUrl: '{BASE_URL}/apps/reddit/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/reddit/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/reddit/connection', |   authDocUrl: '{DOCS_URL}/apps/reddit/connection', | ||||||
|   primaryColor: 'FF4500', |   primaryColor: 'FF4500', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [addAuthHeader], |   beforeRequest: [addAuthHeader], | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ export default defineApp({ | |||||||
|   name: 'Remove.bg', |   name: 'Remove.bg', | ||||||
|   key: 'removebg', |   key: 'removebg', | ||||||
|   iconUrl: '{BASE_URL}/apps/removebg/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/removebg/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/removebg/connection', |   authDocUrl: '{DOCS_URL}/apps/removebg/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://www.remove.bg', |   baseUrl: 'https://www.remove.bg', | ||||||
|   apiBaseUrl: 'https://api.remove.bg/v1.0', |   apiBaseUrl: 'https://api.remove.bg/v1.0', | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ export default defineApp({ | |||||||
|   name: 'RSS', |   name: 'RSS', | ||||||
|   key: 'rss', |   key: 'rss', | ||||||
|   iconUrl: '{BASE_URL}/apps/rss/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/rss/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/rss/connection', |   authDocUrl: '{DOCS_URL}/apps/rss/connection', | ||||||
|   supportsConnections: false, |   supportsConnections: false, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export default defineApp({ | |||||||
|   name: 'Salesforce', |   name: 'Salesforce', | ||||||
|   key: 'salesforce', |   key: 'salesforce', | ||||||
|   iconUrl: '{BASE_URL}/apps/salesforce/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/salesforce/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/connections/salesforce', |   authDocUrl: '{DOCS_URL}/connections/salesforce', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://salesforce.com', |   baseUrl: 'https://salesforce.com', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ export default defineApp({ | |||||||
|   key: 'scheduler', |   key: 'scheduler', | ||||||
|   iconUrl: '{BASE_URL}/apps/scheduler/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/scheduler/assets/favicon.svg', | ||||||
|   docUrl: 'https://automatisch.io/docs/scheduler', |   docUrl: 'https://automatisch.io/docs/scheduler', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/scheduler/connection', |   authDocUrl: '{DOCS_URL}/apps/scheduler/connection', | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   primaryColor: '0059F7', |   primaryColor: '0059F7', | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ export default defineApp({ | |||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   iconUrl: '{BASE_URL}/apps/self-hosted-llm/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/self-hosted-llm/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/self-hosted-llm/connection', |   authDocUrl: '{DOCS_URL}/apps/self-hosted-llm/connection', | ||||||
|   primaryColor: '000000', |   primaryColor: '000000', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   beforeRequest: [setBaseUrl, addAuthHeader], |   beforeRequest: [setBaseUrl, addAuthHeader], | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export default defineApp({ | |||||||
|   name: 'SignalWire', |   name: 'SignalWire', | ||||||
|   key: 'signalwire', |   key: 'signalwire', | ||||||
|   iconUrl: '{BASE_URL}/apps/signalwire/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/signalwire/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/signalwire/connection', |   authDocUrl: '{DOCS_URL}/apps/signalwire/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://signalwire.com', |   baseUrl: 'https://signalwire.com', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export default defineApp({ | |||||||
|   name: 'Slack', |   name: 'Slack', | ||||||
|   key: 'slack', |   key: 'slack', | ||||||
|   iconUrl: '{BASE_URL}/apps/slack/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/slack/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/slack/connection', |   authDocUrl: '{DOCS_URL}/apps/slack/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: 'https://slack.com', |   baseUrl: 'https://slack.com', | ||||||
|   apiBaseUrl: 'https://slack.com/api', |   apiBaseUrl: 'https://slack.com/api', | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ export default defineApp({ | |||||||
|   name: 'SMTP', |   name: 'SMTP', | ||||||
|   key: 'smtp', |   key: 'smtp', | ||||||
|   iconUrl: '{BASE_URL}/apps/smtp/assets/favicon.svg', |   iconUrl: '{BASE_URL}/apps/smtp/assets/favicon.svg', | ||||||
|   authDocUrl: 'https://automatisch.io/docs/apps/smtp/connection', |   authDocUrl: '{DOCS_URL}/apps/smtp/connection', | ||||||
|   supportsConnections: true, |   supportsConnections: true, | ||||||
|   baseUrl: '', |   baseUrl: '', | ||||||
|   apiBaseUrl: '', |   apiBaseUrl: '', | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user