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: | ||||
|     image: quay.io/keycloak/keycloak:21.1 | ||||
|     restart: always | ||||
|     container_name: keycloak | ||||
|     environment: | ||||
|       - KEYCLOAK_ADMIN=admin | ||||
|       - KEYCLOAK_ADMIN_PASSWORD=admin | ||||
|   | ||||
| @@ -4,5 +4,9 @@ | ||||
| **/.devcontainer | ||||
| **/.github | ||||
| **/.vscode | ||||
| **/.env | ||||
| **/.env.test | ||||
| **/.env.production | ||||
| **/yarn-error.log | ||||
| packages/docs | ||||
| packages/e2e-test | ||||
|   | ||||
| @@ -1,14 +1,25 @@ | ||||
| # syntax=docker/dockerfile:1 | ||||
| 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 \ | ||||
|   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/ && \ | ||||
|   apk del build-dependencies | ||||
|  | ||||
| COPY ./entrypoint.sh /entrypoint.sh | ||||
| COPY ./docker/entrypoint.sh /entrypoint.sh | ||||
|  | ||||
| EXPOSE 3000 | ||||
| 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 | ||||
| FROM automatischio/automatisch:0.10.0 | ||||
| FROM automatischio/automatisch:latest | ||||
| WORKDIR /automatisch | ||||
|  | ||||
| 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 | ||||
|  | ||||
| cd packages/backend | ||||
|  | ||||
| if [ -n "$WORKER" ]; then | ||||
|   automatisch start-worker | ||||
|   yarn start:worker | ||||
| else | ||||
|   automatisch start | ||||
|   yarn db:migrate | ||||
|   yarn db:seed:user | ||||
|   yarn start | ||||
| fi | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import appConfig from '../../src/config/app.js'; | ||||
| import logger from '../../src/helpers/logger.js'; | ||||
| import client from './client.js'; | ||||
| import User from '../../src/models/user.js'; | ||||
| import Config from '../../src/models/config.js'; | ||||
| import Role from '../../src/models/role.js'; | ||||
| import '../../src/config/orm.js'; | ||||
| import process from 'process'; | ||||
| @@ -21,6 +22,14 @@ export async function createUser( | ||||
|   email = 'user@automatisch.io', | ||||
|   password = 'sample' | ||||
| ) { | ||||
|   if (appConfig.disableSeedUser) { | ||||
|     logger.info('Seed user is disabled.'); | ||||
|  | ||||
|     process.exit(0); | ||||
|  | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const UNIQUE_VIOLATION_CODE = '23505'; | ||||
|  | ||||
|   const role = await fetchAdminRole(); | ||||
| @@ -37,6 +46,8 @@ export async function createUser( | ||||
|     if (userCount === 0) { | ||||
|       const user = await User.query().insertAndFetch(userParams); | ||||
|       logger.info(`User has been saved: ${user.email}`); | ||||
|  | ||||
|       await Config.markInstallationCompleted(); | ||||
|     } else { | ||||
|       logger.info('No need to seed a user.'); | ||||
|     } | ||||
|   | ||||
| @@ -31,7 +31,7 @@ | ||||
|     "accounting": "^0.4.1", | ||||
|     "ajv-formats": "^2.1.1", | ||||
|     "axios": "1.6.0", | ||||
|     "bcrypt": "^5.0.1", | ||||
|     "bcrypt": "^5.1.0", | ||||
|     "bullmq": "^3.0.0", | ||||
|     "cors": "^2.8.5", | ||||
|     "crypto-js": "^4.1.1", | ||||
| @@ -67,6 +67,7 @@ | ||||
|     "pluralize": "^8.0.0", | ||||
|     "raw-body": "^2.5.2", | ||||
|     "showdown": "^2.1.0", | ||||
|     "uuid": "^9.0.1", | ||||
|     "winston": "^3.7.1", | ||||
|     "xmlrpc": "^1.3.2" | ||||
|   }, | ||||
| @@ -95,6 +96,7 @@ | ||||
|     "url": "https://github.com/automatisch/automatisch/issues" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "node-gyp": "^10.1.0", | ||||
|     "nodemon": "^2.0.13", | ||||
|     "supertest": "^6.3.3", | ||||
|     "vitest": "^1.1.3" | ||||
|   | ||||
| @@ -11,7 +11,7 @@ export default defineApp({ | ||||
|     'https://azure.microsoft.com/en-us/products/ai-services/openai-service', | ||||
|   apiBaseUrl: '', | ||||
|   iconUrl: '{BASE_URL}/apps/azure-openai/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/azure-openai/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/azure-openai/connection', | ||||
|   primaryColor: '000000', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export default defineApp({ | ||||
|   name: 'Carbone', | ||||
|   key: 'carbone', | ||||
|   iconUrl: '{BASE_URL}/apps/carbone/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/carbone/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/carbone/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://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', | ||||
|   key: 'datastore', | ||||
|   iconUrl: '{BASE_URL}/apps/datastore/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/datastore/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/datastore/connection', | ||||
|   supportsConnections: false, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export default defineApp({ | ||||
|   name: 'DeepL', | ||||
|   key: 'deepl', | ||||
|   iconUrl: '{BASE_URL}/apps/deepl/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/deepl/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/deepl/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://deepl.com', | ||||
|   apiBaseUrl: 'https://api.deepl.com', | ||||
|   | ||||
| @@ -5,7 +5,7 @@ export default defineApp({ | ||||
|   name: 'Delay', | ||||
|   key: 'delay', | ||||
|   iconUrl: '{BASE_URL}/apps/delay/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/delay/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/delay/connection', | ||||
|   supportsConnections: false, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   name: 'Discord', | ||||
|   key: 'discord', | ||||
|   iconUrl: '{BASE_URL}/apps/discord/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/discord/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/discord/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://discord.com', | ||||
|   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', | ||||
|   key: 'dropbox', | ||||
|   iconUrl: '{BASE_URL}/apps/dropbox/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/dropbox/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/dropbox/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://dropbox.com', | ||||
|   apiBaseUrl: 'https://api.dropboxapi.com', | ||||
|   | ||||
| @@ -5,7 +5,7 @@ export default defineApp({ | ||||
|   name: 'Filter', | ||||
|   key: 'filter', | ||||
|   iconUrl: '{BASE_URL}/apps/filter/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/filter/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/filter/connection', | ||||
|   supportsConnections: false, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -8,7 +8,7 @@ export default defineApp({ | ||||
|   name: 'Flickr', | ||||
|   key: 'flickr', | ||||
|   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', | ||||
|   primaryColor: '000000', | ||||
|   supportsConnections: true, | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export default defineApp({ | ||||
|   name: 'Flowers Software', | ||||
|   key: 'flowers-software', | ||||
|   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, | ||||
|   baseUrl: 'https://flowers-software.com', | ||||
|   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 capitalize from './transformers/capitalize.js'; | ||||
| import encodeUriComponent from './transformers/encode-uri-component.js'; | ||||
| import extractEmailAddress from './transformers/extract-email-address.js'; | ||||
| import extractNumber from './transformers/extract-number.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 replace from './transformers/replace.js'; | ||||
| import stringToBase64 from './transformers/string-to-base64.js'; | ||||
| import encodeUri from './transformers/encode-uri.js'; | ||||
| import trimWhitespace from './transformers/trim-whitespace.js'; | ||||
| import useDefaultValue from './transformers/use-default-value.js'; | ||||
|  | ||||
| const transformers = { | ||||
|   base64ToString, | ||||
|   capitalize, | ||||
|   encodeUriComponent, | ||||
|   extractEmailAddress, | ||||
|   extractNumber, | ||||
|   htmlToMarkdown, | ||||
| @@ -24,6 +27,7 @@ const transformers = { | ||||
|   pluralize, | ||||
|   replace, | ||||
|   stringToBase64, | ||||
|   encodeUri, | ||||
|   trimWhitespace, | ||||
|   useDefaultValue, | ||||
| }; | ||||
| @@ -43,6 +47,10 @@ export default defineAction({ | ||||
|       options: [ | ||||
|         { label: 'Base64 to String', value: 'base64ToString' }, | ||||
|         { label: 'Capitalize', value: 'capitalize' }, | ||||
|         { | ||||
|           label: 'Encode URI Component', | ||||
|           value: 'encodeUriComponent', | ||||
|         }, | ||||
|         { label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' }, | ||||
|         { label: 'Convert Markdown to HTML', value: 'markdownToHtml' }, | ||||
|         { label: 'Extract Email Address', value: 'extractEmailAddress' }, | ||||
| @@ -51,6 +59,7 @@ export default defineAction({ | ||||
|         { label: 'Pluralize', value: 'pluralize' }, | ||||
|         { label: 'Replace', value: 'replace' }, | ||||
|         { label: 'String to Base64', value: 'stringToBase64' }, | ||||
|         { label: 'Encode URI', value: 'encodeUri' }, | ||||
|         { label: 'Trim Whitespace', value: 'trimWhitespace' }, | ||||
|         { 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 capitalize from './text/capitalize.js'; | ||||
| import encodeUriComponent from './text/encode-uri-component.js'; | ||||
| import extractEmailAddress from './text/extract-email-address.js'; | ||||
| import extractNumber from './text/extract-number.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 replace from './text/replace.js'; | ||||
| import stringToBase64 from './text/string-to-base64.js'; | ||||
| import encodeUri from './text/encode-uri.js'; | ||||
| import trimWhitespace from './text/trim-whitespace.js'; | ||||
| import useDefaultValue from './text/use-default-value.js'; | ||||
| import performMathOperation from './numbers/perform-math-operation.js'; | ||||
| @@ -19,6 +21,7 @@ import formatDateTime from './date-time/format-date-time.js'; | ||||
| const options = { | ||||
|   base64ToString, | ||||
|   capitalize, | ||||
|   encodeUriComponent, | ||||
|   extractEmailAddress, | ||||
|   extractNumber, | ||||
|   htmlToMarkdown, | ||||
| @@ -27,6 +30,7 @@ const options = { | ||||
|   pluralize, | ||||
|   replace, | ||||
|   stringToBase64, | ||||
|   encodeUri, | ||||
|   trimWhitespace, | ||||
|   useDefaultValue, | ||||
|   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', | ||||
|   key: 'formatter', | ||||
|   iconUrl: '{BASE_URL}/apps/formatter/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/formatter/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/formatter/connection', | ||||
|   supportsConnections: false, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://ghost.org', | ||||
|   apiBaseUrl: '', | ||||
|   iconUrl: '{BASE_URL}/apps/ghost/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/ghost/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/ghost/connection', | ||||
|   primaryColor: '15171A', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -11,7 +11,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://github.com', | ||||
|   apiBaseUrl: 'https://api.github.com', | ||||
|   iconUrl: '{BASE_URL}/apps/github/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/github/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/github/connection', | ||||
|   primaryColor: '000000', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -11,7 +11,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://gitlab.com', | ||||
|   apiBaseUrl: 'https://gitlab.com', | ||||
|   iconUrl: '{BASE_URL}/apps/gitlab/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/gitlab/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/gitlab/connection', | ||||
|   primaryColor: 'FC6D26', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://calendar.google.com', | ||||
|   apiBaseUrl: 'https://www.googleapis.com/calendar', | ||||
|   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', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://drive.google.com', | ||||
|   apiBaseUrl: 'https://www.googleapis.com/drive', | ||||
|   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', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://docs.google.com/forms', | ||||
|   apiBaseUrl: 'https://forms.googleapis.com', | ||||
|   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', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -12,7 +12,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://docs.google.com/spreadsheets', | ||||
|   apiBaseUrl: 'https://sheets.googleapis.com', | ||||
|   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', | ||||
|   supportsConnections: true, | ||||
|   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', | ||||
|   apiBaseUrl: 'https://app.tryhelix.ai', | ||||
|   iconUrl: '{BASE_URL}/apps/helix/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/helix/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/helix/connection', | ||||
|   primaryColor: '000000', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -5,7 +5,7 @@ export default defineApp({ | ||||
|   name: 'HTTP Request', | ||||
|   key: 'http-request', | ||||
|   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, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export default defineApp({ | ||||
|   name: 'HubSpot', | ||||
|   key: 'hubspot', | ||||
|   iconUrl: '{BASE_URL}/apps/hubspot/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/hubspot/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/hubspot/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://www.hubspot.com', | ||||
|   apiBaseUrl: 'https://api.hubapi.com', | ||||
|   | ||||
| @@ -12,7 +12,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://invoiceninja.com', | ||||
|   apiBaseUrl: 'https://invoicing.co/api', | ||||
|   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', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   name: 'Mattermost', | ||||
|   key: 'mattermost', | ||||
|   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', | ||||
|   apiBaseUrl: '', // there is no cloud version of this app, user always need to provide address of own instance when creating connection | ||||
|   primaryColor: '4a154b', | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://miro.com', | ||||
|   apiBaseUrl: 'https://api.miro.com', | ||||
|   iconUrl: '{BASE_URL}/apps/miro/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/miro/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/miro/connection', | ||||
|   primaryColor: 'F2CA02', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -12,7 +12,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://notion.com', | ||||
|   apiBaseUrl: 'https://api.notion.com', | ||||
|   iconUrl: '{BASE_URL}/apps/notion/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/notion/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/notion/connection', | ||||
|   primaryColor: '000000', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader, addNotionVersionHeader], | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export default defineApp({ | ||||
|   name: 'Ntfy', | ||||
|   key: 'ntfy', | ||||
|   iconUrl: '{BASE_URL}/apps/ntfy/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/ntfy/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/ntfy/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://ntfy.sh', | ||||
|   apiBaseUrl: 'https://ntfy.sh', | ||||
|   | ||||
| @@ -6,7 +6,7 @@ export default defineApp({ | ||||
|   name: 'Odoo', | ||||
|   key: 'odoo', | ||||
|   iconUrl: '{BASE_URL}/apps/odoo/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/odoo/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/odoo/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://odoo.com', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://openai.com', | ||||
|   apiBaseUrl: 'https://api.openai.com', | ||||
|   iconUrl: '{BASE_URL}/apps/openai/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/openai/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/openai/connection', | ||||
|   primaryColor: '000000', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -12,7 +12,7 @@ export default defineApp({ | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   iconUrl: '{BASE_URL}/apps/pipedrive/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/pipedrive/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/pipedrive/connection', | ||||
|   primaryColor: 'FFFFFF', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -8,7 +8,7 @@ export default defineApp({ | ||||
|   name: 'Placetel', | ||||
|   key: 'placetel', | ||||
|   iconUrl: '{BASE_URL}/apps/placetel/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/placetel/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/placetel/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://placetel.de', | ||||
|   apiBaseUrl: 'https://api.placetel.de', | ||||
|   | ||||
| @@ -6,7 +6,7 @@ export default defineApp({ | ||||
|   name: 'PostgreSQL', | ||||
|   key: 'postgresql', | ||||
|   iconUrl: '{BASE_URL}/apps/postgresql/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/postgresql/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/postgresql/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -9,7 +9,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://pushover.net', | ||||
|   apiBaseUrl: 'https://api.pushover.net', | ||||
|   iconUrl: '{BASE_URL}/apps/pushover/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/pushover/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/pushover/connection', | ||||
|   primaryColor: '249DF1', | ||||
|   supportsConnections: true, | ||||
|   auth, | ||||
|   | ||||
| @@ -10,7 +10,7 @@ export default defineApp({ | ||||
|   baseUrl: 'https://www.reddit.com', | ||||
|   apiBaseUrl: 'https://oauth.reddit.com', | ||||
|   iconUrl: '{BASE_URL}/apps/reddit/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/reddit/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/reddit/connection', | ||||
|   primaryColor: 'FF4500', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [addAuthHeader], | ||||
|   | ||||
| @@ -7,7 +7,7 @@ export default defineApp({ | ||||
|   name: 'Remove.bg', | ||||
|   key: 'removebg', | ||||
|   iconUrl: '{BASE_URL}/apps/removebg/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/removebg/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/removebg/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://www.remove.bg', | ||||
|   apiBaseUrl: 'https://api.remove.bg/v1.0', | ||||
|   | ||||
| @@ -5,7 +5,7 @@ export default defineApp({ | ||||
|   name: 'RSS', | ||||
|   key: 'rss', | ||||
|   iconUrl: '{BASE_URL}/apps/rss/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/rss/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/rss/connection', | ||||
|   supportsConnections: false, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -9,7 +9,7 @@ export default defineApp({ | ||||
|   name: 'Salesforce', | ||||
|   key: 'salesforce', | ||||
|   iconUrl: '{BASE_URL}/apps/salesforce/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/connections/salesforce', | ||||
|   authDocUrl: '{DOCS_URL}/connections/salesforce', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://salesforce.com', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -6,7 +6,7 @@ export default defineApp({ | ||||
|   key: 'scheduler', | ||||
|   iconUrl: '{BASE_URL}/apps/scheduler/assets/favicon.svg', | ||||
|   docUrl: 'https://automatisch.io/docs/scheduler', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/scheduler/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/scheduler/connection', | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   primaryColor: '0059F7', | ||||
|   | ||||
| @@ -11,7 +11,7 @@ export default defineApp({ | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   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', | ||||
|   supportsConnections: true, | ||||
|   beforeRequest: [setBaseUrl, addAuthHeader], | ||||
|   | ||||
| @@ -9,7 +9,7 @@ export default defineApp({ | ||||
|   name: 'SignalWire', | ||||
|   key: 'signalwire', | ||||
|   iconUrl: '{BASE_URL}/apps/signalwire/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/signalwire/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/signalwire/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://signalwire.com', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
| @@ -9,7 +9,7 @@ export default defineApp({ | ||||
|   name: 'Slack', | ||||
|   key: 'slack', | ||||
|   iconUrl: '{BASE_URL}/apps/slack/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/slack/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/slack/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: 'https://slack.com', | ||||
|   apiBaseUrl: 'https://slack.com/api', | ||||
|   | ||||
| @@ -6,7 +6,7 @@ export default defineApp({ | ||||
|   name: 'SMTP', | ||||
|   key: 'smtp', | ||||
|   iconUrl: '{BASE_URL}/apps/smtp/assets/favicon.svg', | ||||
|   authDocUrl: 'https://automatisch.io/docs/apps/smtp/connection', | ||||
|   authDocUrl: '{DOCS_URL}/apps/smtp/connection', | ||||
|   supportsConnections: true, | ||||
|   baseUrl: '', | ||||
|   apiBaseUrl: '', | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user