feat(dropbox): support connections
This commit is contained in:
3
packages/backend/src/apps/dropbox/assets/favicon.svg
Normal file
3
packages/backend/src/apps/dropbox/assets/favicon.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" aria-label="Dropbox" role="img" viewBox="0 0 512 512" fill="#0061ff">
|
||||||
|
<path d="M158 101l-99 63 295 188 99-63m-99-188l99 63-295 188-99-63m99 83l98 63 98-63-98-62z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 213 B |
22
packages/backend/src/apps/dropbox/auth/generate-auth-url.ts
Normal file
22
packages/backend/src/apps/dropbox/auth/generate-auth-url.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
import { IField, IGlobalVariable } from '@automatisch/types';
|
||||||
|
import scopes from '../common/scopes';
|
||||||
|
|
||||||
|
export default async function generateAuthUrl($: IGlobalVariable) {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const callbackUrl = oauthRedirectUrlField.value as string;
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
redirect_uri: callbackUrl,
|
||||||
|
response_type: 'code',
|
||||||
|
scope: scopes.join(' '),
|
||||||
|
token_access_type: 'offline',
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `${$.app.baseUrl}/oauth2/authorize?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
await $.auth.set({ url });
|
||||||
|
}
|
48
packages/backend/src/apps/dropbox/auth/index.ts
Normal file
48
packages/backend/src/apps/dropbox/auth/index.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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: [
|
||||||
|
{
|
||||||
|
key: 'oAuthRedirectUrl',
|
||||||
|
label: 'OAuth Redirect URL',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
value: '{WEB_APP_URL}/app/dropbox/connections/add',
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'When asked to input an OAuth callback or redirect URL in Dropbox OAuth, enter the URL above.',
|
||||||
|
clickToCopy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientId',
|
||||||
|
label: 'App Key',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientSecret',
|
||||||
|
label: 'App Secret',
|
||||||
|
type: 'string' as const,
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
generateAuthUrl,
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
refreshToken,
|
||||||
|
};
|
@@ -0,0 +1,9 @@
|
|||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
import getCurrentAccount from '../common/get-current-account';
|
||||||
|
|
||||||
|
const isStillVerified = async ($: IGlobalVariable) => {
|
||||||
|
const account = await getCurrentAccount($);
|
||||||
|
return !!account;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
38
packages/backend/src/apps/dropbox/auth/refresh-token.ts
Normal file
38
packages/backend/src/apps/dropbox/auth/refresh-token.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Buffer } from 'node:buffer';
|
||||||
|
import { IGlobalVariable } from '@automatisch/types';
|
||||||
|
|
||||||
|
const refreshToken = async ($: IGlobalVariable) => {
|
||||||
|
const params = {
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
refresh_token: $.auth.data.refreshToken as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const basicAuthToken = Buffer
|
||||||
|
.from(`${$.auth.data.clientId}:${$.auth.data.clientSecret}`)
|
||||||
|
.toString('base64');
|
||||||
|
|
||||||
|
const { data } = await $.http.post(
|
||||||
|
'oauth2/token',
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${basicAuthToken}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
access_token: accessToken,
|
||||||
|
expires_in: expiresIn,
|
||||||
|
token_type: tokenType,
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken,
|
||||||
|
expiresIn,
|
||||||
|
tokenType,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default refreshToken;
|
102
packages/backend/src/apps/dropbox/auth/verify-credentials.ts
Normal file
102
packages/backend/src/apps/dropbox/auth/verify-credentials.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import { IGlobalVariable, IField } from '@automatisch/types';
|
||||||
|
import getCurrentAccount from '../common/get-current-account';
|
||||||
|
|
||||||
|
type TAccount = {
|
||||||
|
account_id: string,
|
||||||
|
name: {
|
||||||
|
given_name: string,
|
||||||
|
surname: string,
|
||||||
|
familiar_name: string,
|
||||||
|
display_name: string,
|
||||||
|
abbreviated_name: string,
|
||||||
|
},
|
||||||
|
email: string,
|
||||||
|
email_verified: boolean,
|
||||||
|
disabled: boolean,
|
||||||
|
country: string,
|
||||||
|
locale: string,
|
||||||
|
referral_link: string,
|
||||||
|
is_paired: boolean,
|
||||||
|
account_type: {
|
||||||
|
".tag": string,
|
||||||
|
},
|
||||||
|
root_info: {
|
||||||
|
".tag": string,
|
||||||
|
root_namespace_id: string,
|
||||||
|
home_namespace_id: string,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyCredentials = async ($: IGlobalVariable) => {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUrl = oauthRedirectUrlField.value as string;
|
||||||
|
const params = {
|
||||||
|
client_id: $.auth.data.clientId as string,
|
||||||
|
redirect_uri: redirectUrl,
|
||||||
|
client_secret: $.auth.data.clientSecret as string,
|
||||||
|
code: $.auth.data.code as string,
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
}
|
||||||
|
const { data: verifiedCredentials } = await $.http.post(
|
||||||
|
'/oauth2/token',
|
||||||
|
null,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
access_token: accessToken,
|
||||||
|
refresh_token: refreshToken,
|
||||||
|
expires_in: expiresIn,
|
||||||
|
scope: scope,
|
||||||
|
token_type: tokenType,
|
||||||
|
account_id: accountId,
|
||||||
|
team_id: teamId,
|
||||||
|
id_token: idToken,
|
||||||
|
uid,
|
||||||
|
} = verifiedCredentials;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken,
|
||||||
|
refreshToken,
|
||||||
|
expiresIn,
|
||||||
|
scope,
|
||||||
|
tokenType,
|
||||||
|
accountId,
|
||||||
|
teamId,
|
||||||
|
idToken,
|
||||||
|
uid
|
||||||
|
});
|
||||||
|
|
||||||
|
const account = await getCurrentAccount($) as TAccount;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accountId: account.account_id,
|
||||||
|
name: {
|
||||||
|
givenName: account.name.given_name,
|
||||||
|
surname: account.name.surname,
|
||||||
|
familiarName: account.name.familiar_name,
|
||||||
|
displayName: account.name.display_name,
|
||||||
|
abbreviatedName: account.name.abbreviated_name,
|
||||||
|
},
|
||||||
|
email: account.email,
|
||||||
|
emailVerified: account.email_verified,
|
||||||
|
disabled: account.disabled,
|
||||||
|
country: account.country,
|
||||||
|
locale: account.locale,
|
||||||
|
referralLink: account.referral_link,
|
||||||
|
isPaired: account.is_paired,
|
||||||
|
accountType: {
|
||||||
|
".tag": account.account_type['.tag'],
|
||||||
|
},
|
||||||
|
rootInfo: {
|
||||||
|
".tag": account.root_info['.tag'],
|
||||||
|
rootNamespaceId: account.root_info.root_namespace_id,
|
||||||
|
homeNamespaceId: account.root_info.home_namespace_id,
|
||||||
|
},
|
||||||
|
screenName: `${account.name.display_name} - ${account.email}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
13
packages/backend/src/apps/dropbox/common/add-auth-header.ts
Normal file
13
packages/backend/src/apps/dropbox/common/add-auth-header.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { TBeforeRequest } from '@automatisch/types';
|
||||||
|
|
||||||
|
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||||
|
requestConfig.headers['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
if (!requestConfig.headers.Authorization && $.auth.data?.accessToken) {
|
||||||
|
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
@@ -0,0 +1,8 @@
|
|||||||
|
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
const getCurrentAccount = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||||
|
const response = await $.http.post('/2/users/get_current_account', null);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentAccount;
|
8
packages/backend/src/apps/dropbox/common/scopes.ts
Normal file
8
packages/backend/src/apps/dropbox/common/scopes.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const scopes = [
|
||||||
|
'account_info.read',
|
||||||
|
'files.metadata.read',
|
||||||
|
'files.content.write',
|
||||||
|
'files.content.read',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default scopes;
|
0
packages/backend/src/apps/dropbox/index.d.ts
vendored
Normal file
0
packages/backend/src/apps/dropbox/index.d.ts
vendored
Normal file
16
packages/backend/src/apps/dropbox/index.ts
Normal file
16
packages/backend/src/apps/dropbox/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import defineApp from '../../helpers/define-app';
|
||||||
|
import addAuthHeader from './common/add-auth-header';
|
||||||
|
import auth from './auth';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'Dropbox',
|
||||||
|
key: 'dropbox',
|
||||||
|
iconUrl: '{BASE_URL}/apps/dropbox/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/dropbox/connection',
|
||||||
|
supportsConnections: true,
|
||||||
|
baseUrl: 'https://dropbox.com',
|
||||||
|
apiBaseUrl: 'https://api.dropboxapi.com',
|
||||||
|
primaryColor: '0061ff',
|
||||||
|
beforeRequest: [addAuthHeader],
|
||||||
|
auth,
|
||||||
|
});
|
Reference in New Issue
Block a user