From 8a0b5c24a5157e7e14d659ddc1e3a9b81745f25c Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 6 Nov 2022 17:19:27 +0100 Subject: [PATCH 1/7] feat(strava): add app definition --- packages/backend/src/apps/strava/assets/favicon.svg | 6 ++++++ packages/backend/src/apps/strava/index.d.ts | 0 packages/backend/src/apps/strava/index.ts | 12 ++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 packages/backend/src/apps/strava/assets/favicon.svg create mode 100644 packages/backend/src/apps/strava/index.d.ts create mode 100644 packages/backend/src/apps/strava/index.ts diff --git a/packages/backend/src/apps/strava/assets/favicon.svg b/packages/backend/src/apps/strava/assets/favicon.svg new file mode 100644 index 00000000..ddd7c855 --- /dev/null +++ b/packages/backend/src/apps/strava/assets/favicon.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/packages/backend/src/apps/strava/index.d.ts b/packages/backend/src/apps/strava/index.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/backend/src/apps/strava/index.ts b/packages/backend/src/apps/strava/index.ts new file mode 100644 index 00000000..1d446bea --- /dev/null +++ b/packages/backend/src/apps/strava/index.ts @@ -0,0 +1,12 @@ +import defineApp from '../../helpers/define-app'; + +export default defineApp({ + name: 'Strava', + key: 'strava', + iconUrl: '{BASE_URL}/apps/strava/assets/favicon.svg', + authDocUrl: 'https://automatisch.io/docs/connections/strava', + supportsConnections: true, + baseUrl: 'https://www.strava.com', + apiBaseUrl: 'https://www.strava.com/api', + primaryColor: 'fc4c01', +}); From 37c6b57a48e0d0dedda808393963867604d7ad05 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 6 Nov 2022 17:20:12 +0100 Subject: [PATCH 2/7] feat(strava): add authentication support --- .../src/apps/strava/auth/create-auth-data.ts | 26 ++++ .../backend/src/apps/strava/auth/index.ts | 127 ++++++++++++++++++ .../apps/strava/auth/verify-credentials.ts | 28 ++++ .../src/apps/strava/common/add-auth-header.ts | 13 ++ packages/backend/src/apps/strava/index.ts | 4 + 5 files changed, 198 insertions(+) create mode 100644 packages/backend/src/apps/strava/auth/create-auth-data.ts create mode 100644 packages/backend/src/apps/strava/auth/index.ts create mode 100644 packages/backend/src/apps/strava/auth/verify-credentials.ts create mode 100644 packages/backend/src/apps/strava/common/add-auth-header.ts diff --git a/packages/backend/src/apps/strava/auth/create-auth-data.ts b/packages/backend/src/apps/strava/auth/create-auth-data.ts new file mode 100644 index 00000000..eb4fe55f --- /dev/null +++ b/packages/backend/src/apps/strava/auth/create-auth-data.ts @@ -0,0 +1,26 @@ +import { IField, IGlobalVariable } from '@automatisch/types'; +import qs from 'qs'; + +export default async function createAuthData($: IGlobalVariable) { + try { + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); + const redirectUri = oauthRedirectUrlField.value; + const searchParams = qs.stringify({ + client_id: $.auth.data.consumerKey as string, + redirect_uri: redirectUri, + approval_prompt: 'force', + response_type: 'code', + scope: 'read_all,profile:read_all,activity:read_all,activity:write', + }); + + await $.auth.set({ + url: `${$.app.baseUrl}/oauth/authorize?${searchParams}`, + }); + } catch (error) { + throw new Error( + `Error occured while verifying credentials: ${error}` + ); + } +} diff --git a/packages/backend/src/apps/strava/auth/index.ts b/packages/backend/src/apps/strava/auth/index.ts new file mode 100644 index 00000000..34fc86ee --- /dev/null +++ b/packages/backend/src/apps/strava/auth/index.ts @@ -0,0 +1,127 @@ +import createAuthData from './create-auth-data'; +import verifyCredentials from './verify-credentials'; +import isStillVerified from './is-still-verified'; + +export default { + fields: [ + { + key: 'oAuthRedirectUrl', + label: 'OAuth Redirect URL', + type: 'string' as const, + required: true, + readOnly: true, + value: '{WEB_APP_URL}/app/strava/connections/add', + placeholder: null, + description: + 'When asked to input an OAuth callback or redirect URL in Strava OAuth, enter the URL above.', + clickToCopy: true, + }, + { + key: 'consumerKey', + label: 'Consumer Key', + type: 'string' as const, + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + clickToCopy: false, + }, + { + key: 'consumerSecret', + label: 'Consumer Secret', + type: 'string' as const, + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + clickToCopy: false, + }, + ], + authenticationSteps: [ + { + step: 1, + type: 'mutation' as const, + name: 'createConnection', + arguments: [ + { + name: 'key', + value: '{key}', + }, + { + name: 'formattedData', + value: null, + properties: [ + { + name: 'consumerKey', + value: '{fields.consumerKey}', + }, + { + name: 'consumerSecret', + value: '{fields.consumerSecret}', + }, + ], + }, + ], + }, + { + step: 2, + type: 'mutation' as const, + name: 'createAuthData', + arguments: [ + { + name: 'id', + value: '{createConnection.id}', + }, + ], + }, + { + step: 3, + type: 'openWithPopup' as const, + name: 'openAuthPopup', + arguments: [ + { + name: 'url', + value: '{createAuthData.url}', + }, + ], + }, + { + step: 4, + type: 'mutation' as const, + name: 'updateConnection', + arguments: [ + { + name: 'id', + value: '{createConnection.id}', + }, + { + name: 'formattedData', + value: null, + properties: [ + { + name: 'code', + value: '{openAuthPopup.code}', + }, + ], + }, + ], + }, + { + step: 5, + type: 'mutation' as const, + name: 'verifyConnection', + arguments: [ + { + name: 'id', + value: '{createConnection.id}', + }, + ], + }, + ], + + createAuthData, + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/strava/auth/verify-credentials.ts b/packages/backend/src/apps/strava/auth/verify-credentials.ts new file mode 100644 index 00000000..1226530d --- /dev/null +++ b/packages/backend/src/apps/strava/auth/verify-credentials.ts @@ -0,0 +1,28 @@ +import qs from 'qs'; +import { IGlobalVariable } from '@automatisch/types'; + +const verifyCredentials = async ($: IGlobalVariable) => { + try { + const searchParams = { + client_id: $.auth.data.consumerKey, + client_secret: $.auth.data.consumerSecret, + code: $.auth.data.code, + grant_type: 'authorization_code', + }; + const { data } = await $.http.post( + `/v3/oauth/token?${qs.stringify(searchParams)}a`, + ); + + await $.auth.set({ + accessToken: data.access_token, + refreshToken: data.refresh_token, + tokenType: data.token_type, + userId: data.athlete.id, + screenName: `${data.athlete.firstname} ${data.athlete.lastname}`, + }); + } catch (error) { + throw new Error('Error occured while verifying credentials!'); + } +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/strava/common/add-auth-header.ts b/packages/backend/src/apps/strava/common/add-auth-header.ts new file mode 100644 index 00000000..2420110a --- /dev/null +++ b/packages/backend/src/apps/strava/common/add-auth-header.ts @@ -0,0 +1,13 @@ +import { TBeforeRequest } from '@automatisch/types'; + +const addAuthHeader: TBeforeRequest = ($, requestConfig) => { + const { accessToken, tokenType } = $.auth.data; + + if (accessToken && tokenType) { + requestConfig.headers.Authorization = `${tokenType} ${accessToken}`; + } + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/strava/index.ts b/packages/backend/src/apps/strava/index.ts index 1d446bea..2a38b3b9 100644 --- a/packages/backend/src/apps/strava/index.ts +++ b/packages/backend/src/apps/strava/index.ts @@ -1,4 +1,6 @@ import defineApp from '../../helpers/define-app'; +import addAuthHeader from './common/add-auth-header'; +import auth from './auth'; export default defineApp({ name: 'Strava', @@ -9,4 +11,6 @@ export default defineApp({ baseUrl: 'https://www.strava.com', apiBaseUrl: 'https://www.strava.com/api', primaryColor: 'fc4c01', + beforeRequest: [addAuthHeader], + auth, }); From 8be2ed0034f7c49ebe9c972ca4545c811ddc7fe4 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 6 Nov 2022 17:20:31 +0100 Subject: [PATCH 3/7] feat(strava): add current user utility function --- .../backend/src/apps/strava/common/get-current-user.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 packages/backend/src/apps/strava/common/get-current-user.ts diff --git a/packages/backend/src/apps/strava/common/get-current-user.ts b/packages/backend/src/apps/strava/common/get-current-user.ts new file mode 100644 index 00000000..0765be96 --- /dev/null +++ b/packages/backend/src/apps/strava/common/get-current-user.ts @@ -0,0 +1,10 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; + +const getCurrentUser = async ($: IGlobalVariable): Promise => { + const response = await $.http.get('/v3/athlete'); + const currentUser = response.data; + + return currentUser; +}; + +export default getCurrentUser; From dece070d28368651856041216d40233b18f92b49 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 6 Nov 2022 17:20:45 +0100 Subject: [PATCH 4/7] feat(strava): add connection verified check --- .../src/apps/strava/auth/is-still-verified.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/backend/src/apps/strava/auth/is-still-verified.ts diff --git a/packages/backend/src/apps/strava/auth/is-still-verified.ts b/packages/backend/src/apps/strava/auth/is-still-verified.ts new file mode 100644 index 00000000..8f578344 --- /dev/null +++ b/packages/backend/src/apps/strava/auth/is-still-verified.ts @@ -0,0 +1,13 @@ +import { IGlobalVariable } from '@automatisch/types'; +import getCurrentUser from '../common/get-current-user'; + +const isStillVerified = async ($: IGlobalVariable) => { + try { + const user = await getCurrentUser($); + return !!user; + } catch (error) { + return false; + } +}; + +export default isStillVerified; From 5b881db19f2c15b61d59d255e58d2bbe3cebcb2f Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 6 Apr 2023 19:20:24 +0000 Subject: [PATCH 5/7] feat(strava): refresh token when expired --- .../src/apps/strava/auth/create-auth-data.ts | 26 ----- .../src/apps/strava/auth/generate-auth-url.ts | 20 ++++ .../backend/src/apps/strava/auth/index.ts | 95 ++----------------- .../src/apps/strava/auth/is-still-verified.ts | 8 +- .../src/apps/strava/auth/refresh-token.ts | 26 +++++ .../apps/strava/auth/verify-credentials.ts | 39 ++++---- 6 files changed, 74 insertions(+), 140 deletions(-) delete mode 100644 packages/backend/src/apps/strava/auth/create-auth-data.ts create mode 100644 packages/backend/src/apps/strava/auth/generate-auth-url.ts create mode 100644 packages/backend/src/apps/strava/auth/refresh-token.ts diff --git a/packages/backend/src/apps/strava/auth/create-auth-data.ts b/packages/backend/src/apps/strava/auth/create-auth-data.ts deleted file mode 100644 index eb4fe55f..00000000 --- a/packages/backend/src/apps/strava/auth/create-auth-data.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { IField, IGlobalVariable } from '@automatisch/types'; -import qs from 'qs'; - -export default async function createAuthData($: IGlobalVariable) { - try { - const oauthRedirectUrlField = $.app.auth.fields.find( - (field: IField) => field.key == 'oAuthRedirectUrl' - ); - const redirectUri = oauthRedirectUrlField.value; - const searchParams = qs.stringify({ - client_id: $.auth.data.consumerKey as string, - redirect_uri: redirectUri, - approval_prompt: 'force', - response_type: 'code', - scope: 'read_all,profile:read_all,activity:read_all,activity:write', - }); - - await $.auth.set({ - url: `${$.app.baseUrl}/oauth/authorize?${searchParams}`, - }); - } catch (error) { - throw new Error( - `Error occured while verifying credentials: ${error}` - ); - } -} diff --git a/packages/backend/src/apps/strava/auth/generate-auth-url.ts b/packages/backend/src/apps/strava/auth/generate-auth-url.ts new file mode 100644 index 00000000..185a1ad1 --- /dev/null +++ b/packages/backend/src/apps/strava/auth/generate-auth-url.ts @@ -0,0 +1,20 @@ +import { URLSearchParams } from 'node:url'; +import { IField, IGlobalVariable } from '@automatisch/types'; + +export default async function createAuthData($: IGlobalVariable) { + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); + const redirectUri = oauthRedirectUrlField.value as string; + const searchParams = new URLSearchParams({ + client_id: $.auth.data.clientId as string, + redirect_uri: redirectUri, + approval_prompt: 'force', + response_type: 'code', + scope: 'read_all,profile:read_all,activity:read_all,activity:write', + }); + + await $.auth.set({ + url: `${$.app.baseUrl}/oauth/authorize?${searchParams}`, + }); +} diff --git a/packages/backend/src/apps/strava/auth/index.ts b/packages/backend/src/apps/strava/auth/index.ts index 34fc86ee..df7d9b0d 100644 --- a/packages/backend/src/apps/strava/auth/index.ts +++ b/packages/backend/src/apps/strava/auth/index.ts @@ -1,6 +1,7 @@ -import createAuthData from './create-auth-data'; +import generateAuthUrl from './generate-auth-url'; import verifyCredentials from './verify-credentials'; import isStillVerified from './is-still-verified'; +import refreshToken from './refresh-token'; export default { fields: [ @@ -17,8 +18,8 @@ export default { clickToCopy: true, }, { - key: 'consumerKey', - label: 'Consumer Key', + key: 'clientId', + label: 'Client ID', type: 'string' as const, required: true, readOnly: false, @@ -28,8 +29,8 @@ export default { clickToCopy: false, }, { - key: 'consumerSecret', - label: 'Consumer Secret', + key: 'clientSecret', + label: 'Client Secret', type: 'string' as const, required: true, readOnly: false, @@ -39,89 +40,9 @@ export default { clickToCopy: false, }, ], - authenticationSteps: [ - { - step: 1, - type: 'mutation' as const, - name: 'createConnection', - arguments: [ - { - name: 'key', - value: '{key}', - }, - { - name: 'formattedData', - value: null, - properties: [ - { - name: 'consumerKey', - value: '{fields.consumerKey}', - }, - { - name: 'consumerSecret', - value: '{fields.consumerSecret}', - }, - ], - }, - ], - }, - { - step: 2, - type: 'mutation' as const, - name: 'createAuthData', - arguments: [ - { - name: 'id', - value: '{createConnection.id}', - }, - ], - }, - { - step: 3, - type: 'openWithPopup' as const, - name: 'openAuthPopup', - arguments: [ - { - name: 'url', - value: '{createAuthData.url}', - }, - ], - }, - { - step: 4, - type: 'mutation' as const, - name: 'updateConnection', - arguments: [ - { - name: 'id', - value: '{createConnection.id}', - }, - { - name: 'formattedData', - value: null, - properties: [ - { - name: 'code', - value: '{openAuthPopup.code}', - }, - ], - }, - ], - }, - { - step: 5, - type: 'mutation' as const, - name: 'verifyConnection', - arguments: [ - { - name: 'id', - value: '{createConnection.id}', - }, - ], - }, - ], - createAuthData, + generateAuthUrl, verifyCredentials, isStillVerified, + refreshToken, }; diff --git a/packages/backend/src/apps/strava/auth/is-still-verified.ts b/packages/backend/src/apps/strava/auth/is-still-verified.ts index 8f578344..d36919f2 100644 --- a/packages/backend/src/apps/strava/auth/is-still-verified.ts +++ b/packages/backend/src/apps/strava/auth/is-still-verified.ts @@ -2,12 +2,8 @@ import { IGlobalVariable } from '@automatisch/types'; import getCurrentUser from '../common/get-current-user'; const isStillVerified = async ($: IGlobalVariable) => { - try { - const user = await getCurrentUser($); - return !!user; - } catch (error) { - return false; - } + const user = await getCurrentUser($); + return !!user; }; export default isStillVerified; diff --git a/packages/backend/src/apps/strava/auth/refresh-token.ts b/packages/backend/src/apps/strava/auth/refresh-token.ts new file mode 100644 index 00000000..6f5b623f --- /dev/null +++ b/packages/backend/src/apps/strava/auth/refresh-token.ts @@ -0,0 +1,26 @@ +import { IGlobalVariable } from '@automatisch/types'; + +const refreshToken = async ($: IGlobalVariable) => { + const params = { + client_id: $.auth.data.clientId as string, + client_secret: $.auth.data.clientSecret as string, + grant_type: 'refresh_token', + refresh_token: $.auth.data.refreshToken as string, + }; + + const { data } = await $.http.post( + '/v3/oauth/token', + null, + { params } + ); + + await $.auth.set({ + accessToken: data.access_token, + expiresIn: data.expires_in, + expiresAt: data.expires_at, + tokenType: data.token_type, + refreshToken: data.refresh_token, + }); +}; + +export default refreshToken; diff --git a/packages/backend/src/apps/strava/auth/verify-credentials.ts b/packages/backend/src/apps/strava/auth/verify-credentials.ts index 1226530d..ea539036 100644 --- a/packages/backend/src/apps/strava/auth/verify-credentials.ts +++ b/packages/backend/src/apps/strava/auth/verify-credentials.ts @@ -1,28 +1,25 @@ -import qs from 'qs'; import { IGlobalVariable } from '@automatisch/types'; const verifyCredentials = async ($: IGlobalVariable) => { - try { - const searchParams = { - client_id: $.auth.data.consumerKey, - client_secret: $.auth.data.consumerSecret, - code: $.auth.data.code, - grant_type: 'authorization_code', - }; - const { data } = await $.http.post( - `/v3/oauth/token?${qs.stringify(searchParams)}a`, - ); + const params = { + client_id: $.auth.data.clientId as string, + client_secret: $.auth.data.clientSecret as string, + code: $.auth.data.code as string, + grant_type: 'authorization_code', + }; + const { data } = await $.http.post( + '/v3/oauth/token', + null, + { params } + ); - await $.auth.set({ - accessToken: data.access_token, - refreshToken: data.refresh_token, - tokenType: data.token_type, - userId: data.athlete.id, - screenName: `${data.athlete.firstname} ${data.athlete.lastname}`, - }); - } catch (error) { - throw new Error('Error occured while verifying credentials!'); - } + await $.auth.set({ + accessToken: data.access_token, + refreshToken: data.refresh_token, + tokenType: data.token_type, + athleteId: data.athlete.id, + screenName: `${data.athlete.firstname} ${data.athlete.lastname}`, + }); }; export default verifyCredentials; From 53624a63791f0053710db3ebe742cd2cc1681220 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 6 Apr 2023 19:47:09 +0000 Subject: [PATCH 6/7] feat(strava): add action to create totals and stats report --- .../create-totals-and-stats-report/index.ts | 15 +++++++++++++++ packages/backend/src/apps/strava/actions/index.ts | 3 +++ packages/backend/src/apps/strava/index.ts | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 packages/backend/src/apps/strava/actions/create-totals-and-stats-report/index.ts create mode 100644 packages/backend/src/apps/strava/actions/index.ts diff --git a/packages/backend/src/apps/strava/actions/create-totals-and-stats-report/index.ts b/packages/backend/src/apps/strava/actions/create-totals-and-stats-report/index.ts new file mode 100644 index 00000000..dd755fce --- /dev/null +++ b/packages/backend/src/apps/strava/actions/create-totals-and-stats-report/index.ts @@ -0,0 +1,15 @@ +import defineAction from '../../../../helpers/define-action'; + +export default defineAction({ + name: 'Create totals and stats report', + key: 'createTotalsAndStatsReport', + description: 'Create a report with recent, year to date, and all time stats of your activities', + + async run($) { + const { data } = await $.http.get(`/v3/athletes/${$.auth.data.athleteId}/stats`); + + $.setActionItem({ + raw: data, + }); + }, +}); diff --git a/packages/backend/src/apps/strava/actions/index.ts b/packages/backend/src/apps/strava/actions/index.ts new file mode 100644 index 00000000..df9e1d5c --- /dev/null +++ b/packages/backend/src/apps/strava/actions/index.ts @@ -0,0 +1,3 @@ +import createTotalsAndStatsReport from "./create-totals-and-stats-report"; + +export default [createTotalsAndStatsReport]; diff --git a/packages/backend/src/apps/strava/index.ts b/packages/backend/src/apps/strava/index.ts index 2a38b3b9..83706fbb 100644 --- a/packages/backend/src/apps/strava/index.ts +++ b/packages/backend/src/apps/strava/index.ts @@ -1,5 +1,6 @@ import defineApp from '../../helpers/define-app'; import addAuthHeader from './common/add-auth-header'; +import actions from './actions'; import auth from './auth'; export default defineApp({ @@ -13,4 +14,5 @@ export default defineApp({ primaryColor: 'fc4c01', beforeRequest: [addAuthHeader], auth, + actions, }); From d503dbc103f5f6465c2ec921c7614ceff9600ac0 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 6 Apr 2023 19:59:15 +0000 Subject: [PATCH 7/7] docs(strava): describe connection and actions --- packages/docs/pages/.vitepress/config.js | 9 +++++++++ packages/docs/pages/apps/strava/actions.md | 12 ++++++++++++ packages/docs/pages/apps/strava/connection.md | 14 ++++++++++++++ packages/docs/pages/guide/available-apps.md | 1 + packages/docs/pages/public/favicons/strava.svg | 6 ++++++ 5 files changed, 42 insertions(+) create mode 100644 packages/docs/pages/apps/strava/actions.md create mode 100644 packages/docs/pages/apps/strava/connection.md create mode 100644 packages/docs/pages/public/favicons/strava.svg diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 85915902..6e204aca 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -206,6 +206,15 @@ export default defineConfig({ { text: 'Connection', link: '/apps/spotify/connection' }, ], }, + { + text: 'Strava', + collapsible: true, + collapsed: true, + items: [ + { text: 'Actions', link: '/apps/strava/actions' }, + { text: 'Connection', link: '/apps/strava/connection' }, + ], + }, { text: 'Stripe', collapsible: true, diff --git a/packages/docs/pages/apps/strava/actions.md b/packages/docs/pages/apps/strava/actions.md new file mode 100644 index 00000000..1bd2ff94 --- /dev/null +++ b/packages/docs/pages/apps/strava/actions.md @@ -0,0 +1,12 @@ +--- +favicon: /favicons/strava.svg +items: + - name: Create Totals and Stats Report + desc: Creates a report with recent, year to date, and all time stats of your activities. +--- + + + + diff --git a/packages/docs/pages/apps/strava/connection.md b/packages/docs/pages/apps/strava/connection.md new file mode 100644 index 00000000..0060fc06 --- /dev/null +++ b/packages/docs/pages/apps/strava/connection.md @@ -0,0 +1,14 @@ +# Strava + +:::info +This page explains the steps you need to follow to set up the Strava connection in Automatisch. If any of the steps are outdated, please let us know! +::: + +1. Go to the [link](https://www.strava.com/settings/api) to create an app on Strava API. +1. Click on **Upload** button to upload your APP icon. +1. Click on **Edit** button. +1. Copy **OAuth Redirect URL** from Automatisch and paste it in **Authorization Callback Domain** +1. Click on **Save** button. +1. Copy **Client ID** from Strava and paste it in **Client ID** field on Automatisch. +1. Copy **Client Secret** from Strava and paste it in **Client Secret** field on Automatisch. +1. Now, you can start using the Strava connection with Automatisch. diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md index 1909c0f4..b0127ae8 100644 --- a/packages/docs/pages/guide/available-apps.md +++ b/packages/docs/pages/guide/available-apps.md @@ -24,6 +24,7 @@ Following integrations are currently supported by Automatisch. - [Slack](/apps/slack/actions) - [SMTP](/apps/smtp/actions) - [Spotify](/apps/spotify/actions) +- [Strava](/apps/strava/actions) - [Stripe](/apps/stripe/triggers) - [Telegram](/apps/telegram-bot/actions) - [Todoist](/apps/todoist/triggers) diff --git a/packages/docs/pages/public/favicons/strava.svg b/packages/docs/pages/public/favicons/strava.svg new file mode 100644 index 00000000..ddd7c855 --- /dev/null +++ b/packages/docs/pages/public/favicons/strava.svg @@ -0,0 +1,6 @@ + \ No newline at end of file