diff options
author | Sam Wilkins <samwilkins333@gmail.com> | 2019-10-29 20:00:54 -0400 |
---|---|---|
committer | Sam Wilkins <samwilkins333@gmail.com> | 2019-10-29 20:00:54 -0400 |
commit | af25eaf2a848278a58f0993cba2e68c05da0760c (patch) | |
tree | c8065f52b1fadd7f8c18a08bfec4289380e2e29e /src | |
parent | ba7568e4fe2e9323a66a91876305f829487bffb9 (diff) |
comments and fixes for google photos server sid
Diffstat (limited to 'src')
-rw-r--r-- | src/client/Network.ts | 12 | ||||
-rw-r--r-- | src/client/apis/GoogleAuthenticationManager.tsx | 39 | ||||
-rw-r--r-- | src/client/apis/google_docs/GoogleApiClientUtils.ts | 8 | ||||
-rw-r--r-- | src/client/apis/google_docs/GooglePhotosClientUtils.ts | 10 | ||||
-rw-r--r-- | src/client/util/Import & Export/DirectoryImportBox.tsx | 5 | ||||
-rw-r--r-- | src/client/util/Import & Export/ImageUtils.ts | 4 | ||||
-rw-r--r-- | src/new_fields/RichTextUtils.ts | 4 | ||||
-rw-r--r-- | src/server/RouteManager.ts | 4 | ||||
-rw-r--r-- | src/server/RouteStore.ts | 3 | ||||
-rw-r--r-- | src/server/apis/google/GoogleApiServerUtils.ts | 171 | ||||
-rw-r--r-- | src/server/index.ts | 14 |
11 files changed, 147 insertions, 127 deletions
diff --git a/src/client/Network.ts b/src/client/Network.ts index 75ccb5e99..f9ef27267 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -1,18 +1,16 @@ import { Utils } from "../Utils"; -import { CurrentUserUtils } from "../server/authentication/models/current_user_utils"; import requestPromise = require('request-promise'); -export namespace Identified { +export namespace Networking { export async function FetchFromServer(relativeRoute: string) { - return (await fetch(relativeRoute, { headers: { userId: CurrentUserUtils.id } })).text(); + return (await fetch(relativeRoute)).text(); } export async function PostToServer(relativeRoute: string, body?: any) { let options = { uri: Utils.prepend(relativeRoute), method: "POST", - headers: { userId: CurrentUserUtils.id }, body, json: true }; @@ -22,12 +20,10 @@ export namespace Identified { export async function PostFormDataToServer(relativeRoute: string, formData: FormData) { const parameters = { method: 'POST', - headers: { userId: CurrentUserUtils.id }, - body: formData, + body: formData }; const response = await fetch(relativeRoute, parameters); - const text = await response.json(); - return text; + return response.json(); } }
\ No newline at end of file diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index 01dac3996..1ec9d8412 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; import { Opt } from "../../new_fields/Doc"; -import { Identified } from "../Network"; +import { Networking } from "../Network"; import { RouteStore } from "../../server/RouteStore"; import "./GoogleAuthenticationManager.scss"; @@ -31,7 +31,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> { } public fetchOrGenerateAccessToken = async () => { - let response = await Identified.FetchFromServer(RouteStore.readGoogleAccessToken); + let response = await Networking.FetchFromServer(RouteStore.readGoogleAccessToken); // if this is an authentication url, activate the UI to register the new access token if (new RegExp(AuthenticationUrl).test(response)) { this.isOpen = true; @@ -39,24 +39,25 @@ export default class GoogleAuthenticationManager extends React.Component<{}> { return new Promise<string>(async resolve => { const disposer = reaction( () => this.authenticationCode, - authenticationCode => { - if (authenticationCode) { - Identified.PostToServer(RouteStore.writeGoogleAccessToken, { authenticationCode }).then( - ({ access_token, avatar, name }) => { - runInAction(() => { - this.avatar = avatar; - this.username = name; - }); - this.beginFadeout(); - disposer(); - resolve(access_token); - }, - action(() => { - this.hasBeenClicked = false; - this.success = false; - }) - ); + async authenticationCode => { + if (!authenticationCode) { + return; } + const { access_token, avatar, name } = await Networking.PostToServer( + RouteStore.writeGoogleAccessToken, + { authenticationCode } + ); + runInAction(() => { + this.avatar = avatar; + this.username = name; + }); + this.beginFadeout(); + disposer(); + resolve(access_token); + action(() => { + this.hasBeenClicked = false; + this.success = false; + }); } ); }); diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index 1cf01fc3d..183679317 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -3,7 +3,7 @@ import { RouteStore } from "../../../server/RouteStore"; import { Opt } from "../../../new_fields/Doc"; import { isArray } from "util"; import { EditorState } from "prosemirror-state"; -import { Identified } from "../../Network"; +import { Networking } from "../../Network"; export const Pulls = "googleDocsPullCount"; export const Pushes = "googleDocsPushCount"; @@ -84,7 +84,7 @@ export namespace GoogleApiClientUtils { } }; try { - const schema: docs_v1.Schema$Document = await Identified.PostToServer(path, parameters); + const schema: docs_v1.Schema$Document = await Networking.PostToServer(path, parameters); return schema.documentId; } catch { return undefined; @@ -157,7 +157,7 @@ export namespace GoogleApiClientUtils { const path = `${RouteStore.googleDocs}/Documents/${Actions.Retrieve}`; try { const parameters = { documentId: options.documentId }; - const schema: RetrievalResult = await Identified.PostToServer(path, parameters); + const schema: RetrievalResult = await Networking.PostToServer(path, parameters); return schema; } catch { return undefined; @@ -173,7 +173,7 @@ export namespace GoogleApiClientUtils { } }; try { - const replies: UpdateResult = await Identified.PostToServer(path, parameters); + const replies: UpdateResult = await Networking.PostToServer(path, parameters); return replies; } catch { return undefined; diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index e93fa6eb4..402fc64b5 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -13,7 +13,7 @@ import { Docs, DocumentOptions } from "../../documents/Documents"; import { NewMediaItemResult, MediaItem } from "../../../server/apis/google/SharedTypes"; import { AssertionError } from "assert"; import { DocumentView } from "../../views/nodes/DocumentView"; -import { Identified } from "../../Network"; +import { Networking } from "../../Network"; import GoogleAuthenticationManager from "../GoogleAuthenticationManager"; export namespace GooglePhotos { @@ -78,6 +78,7 @@ export namespace GooglePhotos { } export const CollectionToAlbum = async (options: AlbumCreationOptions): Promise<Opt<AlbumCreationResult>> => { + await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); const { collection, title, descriptionKey, tag } = options; const dataDocument = Doc.GetProto(collection); const images = ((await DocListCastAsync(dataDocument.data)) || []).filter(doc => Cast(doc.data, ImageField)); @@ -127,6 +128,7 @@ export namespace GooglePhotos { export type CollectionConstructor = (data: Array<Doc>, options: DocumentOptions, ...args: any) => Doc; export const CollectionFromSearch = async (constructor: CollectionConstructor, requested: Opt<Partial<Query.SearchOptions>>): Promise<Doc> => { + await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); let response = await Query.ContentSearch(requested); let uploads = await Transactions.WriteMediaItemsToServer(response); const children = uploads.map((upload: Transactions.UploadInformation) => { @@ -147,6 +149,7 @@ export namespace GooglePhotos { const comparator = (a: string, b: string) => (a < b) ? -1 : (a > b ? 1 : 0); export const TagChildImages = async (collection: Doc) => { + await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); const idMapping = await Cast(collection.googlePhotosIdMapping, Doc); if (!idMapping) { throw new Error("Appending image metadata requires that the targeted collection have already been mapped to an album!"); @@ -304,7 +307,7 @@ export namespace GooglePhotos { }; export const WriteMediaItemsToServer = async (body: { mediaItems: any[] }): Promise<UploadInformation[]> => { - const uploads = await Identified.PostToServer(RouteStore.googlePhotosMediaDownload, body); + const uploads = await Networking.PostToServer(RouteStore.googlePhotosMediaDownload, body); return uploads; }; @@ -325,6 +328,7 @@ export namespace GooglePhotos { } export const UploadImages = async (sources: Doc[], album?: AlbumReference, descriptionKey = "caption"): Promise<Opt<ImageUploadResults>> => { + await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); if (album && "title" in album) { album = await Create.Album(album.title); } @@ -341,7 +345,7 @@ export namespace GooglePhotos { media.push({ url, description }); } if (media.length) { - const results = await Identified.PostToServer(RouteStore.googlePhotosMediaUpload, { media, album }); + const results = await Networking.PostToServer(RouteStore.googlePhotosMediaUpload, { media, album }); return results; } }; diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index d74b51993..2d1b6fe20 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -20,9 +20,8 @@ import { listSpec } from "../../../new_fields/Schema"; import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import "./DirectoryImportBox.scss"; -import { Identified } from "../../Network"; +import { Networking } from "../../Network"; import { BatchedArray } from "array-batcher"; -import { ExifData } from "exif"; const unsupported = ["text/html", "text/plain"]; @@ -117,7 +116,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> formData.append(Utils.GenerateGuid(), file); }); - const responses = await Identified.PostFormDataToServer(RouteStore.upload, formData); + const responses = await Networking.PostFormDataToServer(RouteStore.upload, formData); runInAction(() => this.completed += batch.length); return responses as ImageUploadResponse[]; }); diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index c9abf38fa..914f4870a 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -3,7 +3,7 @@ import { ImageField } from "../../../new_fields/URLField"; import { Cast, StrCast } from "../../../new_fields/Types"; import { RouteStore } from "../../../server/RouteStore"; import { Docs } from "../../documents/Documents"; -import { Identified } from "../../Network"; +import { Networking } from "../../Network"; import { Id } from "../../../new_fields/FieldSymbols"; import { Utils } from "../../../Utils"; @@ -15,7 +15,7 @@ export namespace ImageUtils { return false; } const source = field.url.href; - const response = await Identified.PostToServer(RouteStore.inspectImage, { source }); + const response = await Networking.PostToServer(RouteStore.inspectImage, { source }); const { error, data } = response.exifData; document.exif = error || Docs.Get.DocumentHierarchyFromJson(data); return data !== undefined; diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts index 601939ed2..63d718ce8 100644 --- a/src/new_fields/RichTextUtils.ts +++ b/src/new_fields/RichTextUtils.ts @@ -17,7 +17,7 @@ import { Cast, StrCast } from "./Types"; import { Id } from "./FieldSymbols"; import { DocumentView } from "../client/views/nodes/DocumentView"; import { AssertionError } from "assert"; -import { Identified } from "../client/Network"; +import { Networking } from "../client/Network"; export namespace RichTextUtils { @@ -129,7 +129,7 @@ export namespace RichTextUtils { return { baseUrl, filename }; }); - const uploads = await Identified.PostToServer(RouteStore.googlePhotosMediaDownload, { mediaItems }); + const uploads = await Networking.PostToServer(RouteStore.googlePhotosMediaDownload, { mediaItems }); if (uploads.length !== mediaItems.length) { throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" }); diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts index 21ce9c9e4..eda2a49d2 100644 --- a/src/server/RouteManager.ts +++ b/src/server/RouteManager.ts @@ -49,9 +49,9 @@ export default class RouteManager { let supervised = async (req: express.Request, res: express.Response) => { const { user, originalUrl: target } = req; const core = { req, res, isRelease }; - const tryExecute = async (target: (args: any) => any | Promise<any>, args: any) => { + const tryExecute = async (toExecute: (args: any) => any | Promise<any>, args: any) => { try { - await target(args); + await toExecute(args); } catch (e) { if (onError) { onError({ ...core, error: e }); diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts index de2553b2f..a310d0c95 100644 --- a/src/server/RouteStore.ts +++ b/src/server/RouteStore.ts @@ -39,6 +39,7 @@ export enum RouteStore { writeGoogleAccessToken = "/writeGoogleAccessToken", googlePhotosMediaUpload = "/googlePhotosMediaUpload", googlePhotosMediaDownload = "/googlePhotosMediaDownload", - googleDocsGet = "/googleDocsGet" + googleDocsGet = "/googleDocsGet", + checkGoogle = "/checkGoogleAuthentication" }
\ No newline at end of file diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts index b9984649e..ff5dc7081 100644 --- a/src/server/apis/google/GoogleApiServerUtils.ts +++ b/src/server/apis/google/GoogleApiServerUtils.ts @@ -7,7 +7,7 @@ import { GaxiosResponse } from "gaxios"; import request = require('request-promise'); import * as qs from 'query-string'; import { Database } from "../../database"; -import path from "path"; +import * as path from "path"; /** * @@ -45,7 +45,7 @@ export namespace GoogleApiServerUtils { * */ export interface CredentialsResult { - credentials: Credentials; + credentials: Opt<Credentials>; refreshed: boolean; } @@ -135,8 +135,8 @@ export namespace GoogleApiServerUtils { /** * */ - export const loadClientSecret = async () => { - return new Promise<void>((resolve, reject) => { + export async function loadClientSecret(): Promise<void> { + return new Promise((resolve, reject) => { readFile(path.join(__dirname, "../../credentials/google_docs_credentials.json"), async (err, projectCredentials) => { if (err) { reject(err); @@ -153,7 +153,7 @@ export namespace GoogleApiServerUtils { resolve(); }); }); - }; + } /** * @@ -165,9 +165,12 @@ export namespace GoogleApiServerUtils { * @param sector * @param userId */ - export const GetEndpoint = (sector: string, userId: string) => { - return new Promise<Opt<Endpoint>>(resolve => { + export async function GetEndpoint(sector: string, userId: string): Promise<Opt<Endpoint>> { + return new Promise(resolve => { retrieveOAuthClient(userId).then(auth => { + if (!auth) { + return resolve(); + } let routed: Opt<Endpoint>; let parameters: EndpointParameters = { auth, version: "v1" }; switch (sector) { @@ -181,29 +184,38 @@ export namespace GoogleApiServerUtils { resolve(routed); }); }); - }; + } /** * * @param userId */ - export const retrieveAccessToken = (userId: string): Promise<string> => { - return new Promise<string>((resolve, reject) => { + export async function retrieveAccessToken(userId: string): Promise<string> { + return new Promise(resolve => { retrieveCredentials(userId).then( - ({ credentials }) => resolve(credentials.access_token!), - error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`) + ({ credentials }) => { + if (credentials) { + return resolve(credentials.access_token!); + } + resolve(); + } ); }); - }; + } /** - * - * @param userId + * Returns an initialized OAuth2 client instance, likely to be passed into Google's + * npm-installed API wrappers that use authenticated client instances rather than access codes for + * security. + * @param userId the Dash user id of the user requesting account integration */ - export const retrieveOAuthClient = (userId: string): Promise<OAuth2Client> => { - return new Promise<OAuth2Client>((resolve, reject) => { + export async function retrieveOAuthClient(userId: string): Promise<OAuth2Client> { + return new Promise((resolve, reject) => { retrieveCredentials(userId).then( ({ credentials, refreshed }) => { + if (!credentials) { + return resolve(); + } let client = authenticationClients.get(userId); if (!client) { authenticationClients.set(userId, client = generateClient(credentials)); @@ -211,31 +223,34 @@ export namespace GoogleApiServerUtils { client.setCredentials(credentials); } resolve(client); - }, - error => reject(`Error: unable to instantiate and certify a new OAuth2 client.\n${error}`) + } ); }); - }; + } /** - * - * @param credentials + * Creates a new OAuth2Client instance, and if provided, sets + * the specific credentials on the client + * @param credentials if you have access to the credentials that you'll eventually set on + * the client, just pass them in at initialization */ - function generateClient(credentials?: Credentials) { + function generateClient(credentials?: Credentials): OAuth2Client { const client = new google.auth.OAuth2(installed); credentials && client.setCredentials(credentials); return client; } /** - * + * Calls on the worker (which does not have and does not need + * any credentials) to produce a url to which the user can + * navigate to give Dash the necessary Google permissions. */ - export const generateAuthenticationUrl = async () => { + export function generateAuthenticationUrl(): string { return worker.generateAuthUrl({ access_type: 'offline', scope: SCOPES.map(relative => prefix + relative), }); - }; + } /** * This is what we return to the server in processNewUser(), after the @@ -255,7 +270,7 @@ export namespace GoogleApiServerUtils { * and the authentication code to fetch the full set of credentials that * we'll store in the database for each user. This is called once per * new account integration. - * @param userId The Dash user id of the user requesting account integration, used to associate the new credentials + * @param userId the Dash user id of the user requesting account integration, used to associate the new credentials * with a Dash user in the googleAuthentication table of the database. * @param authenticationCode the Google-provided authentication code that the user copied * from Google's permissions UI and pasted into the overlay. @@ -263,24 +278,25 @@ export namespace GoogleApiServerUtils { * and display basic user information in the overlay on successful authentication. * This can be expanded as needed by adding properties to the interface GoogleAuthenticationResult. */ - export const processNewUser = async (userId: string, authenticationCode: string): Promise<GoogleAuthenticationResult> => { - return new Promise<GoogleAuthenticationResult>((resolve, reject) => { + export async function processNewUser(userId: string, authenticationCode: string): Promise<GoogleAuthenticationResult> { + const credentials = await new Promise<Credentials>((resolve, reject) => { worker.getToken(authenticationCode, async (err, credentials) => { if (err || !credentials) { reject(err); - return console.error('Error retrieving access token', err); + return; } - const enriched = injectUserInfo(credentials); - await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, enriched); - const { given_name, picture } = enriched.userInfo; - resolve({ - access_token: enriched.access_token!, - avatar: picture, - name: given_name - }); + resolve(credentials); }); }); - }; + const enriched = injectUserInfo(credentials); + await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, enriched); + const { given_name, picture } = enriched.userInfo; + return { + access_token: enriched.access_token!, + avatar: picture, + name: given_name + }; + } /** * This type represents the union of the full set of OAuth2 credentials @@ -299,34 +315,31 @@ export namespace GoogleApiServerUtils { * @returns the full set of credentials in the structure in which they'll be stored * in the database. */ - const injectUserInfo = (credentials: Credentials): EnrichedCredentials => { + function injectUserInfo(credentials: Credentials): EnrichedCredentials { const userInfo = JSON.parse(atob(credentials.id_token!.split(".")[1])); return { ...credentials, userInfo }; - }; + } /** * Looks in the database for any credentials object with the given user id, * and returns them. If the credentials are found but expired, the function will * automatically refresh the credentials and then resolve with the updated values. - * @param userId the id of the Dash user requesting his/her credentials. Eventually - * might have multiple. - * @returns the credentials and whether or not they were updated in the process + * @param userId the id of the Dash user requesting his/her credentials. Eventually, each user might + * be associated with multiple different sets of Google credentials. + * @returns the credentials and a flag indicating whether or not they were refreshed during retrieval */ - const retrieveCredentials = async (userId: string): Promise<CredentialsResult> => { - return new Promise<CredentialsResult>((resolve, reject) => { - Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(credentials => { - if (!credentials) { - return reject(); - } - if (credentials.expiry_date! < new Date().getTime()) { - // Token has expired, so submitting a request for a refreshed access token - return refreshAccessToken(credentials, userId).then(resolve, reject); - } - // Authentication successful! - resolve({ credentials, refreshed: false }); - }); - }); - }; + async function retrieveCredentials(userId: string): Promise<CredentialsResult> { + let credentials: Opt<Credentials> = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId); + let refreshed = false; + if (!credentials) { + return { credentials: undefined, refreshed }; + } + // if the token has expired, submit a request for a refreshed access token + if (credentials.expiry_date! <= new Date().getTime()) { + credentials = await refreshAccessToken(credentials, userId); + } + return { credentials, refreshed }; + } /** * This function submits a request to OAuth with the local refresh token @@ -334,26 +347,28 @@ export namespace GoogleApiServerUtils { * the Dash user id passed in. In addition to returning the credentials, it * writes the diff to the database. * @param credentials the credentials - * @param userId + * @param userId the id of the Dash user implicitly requesting that + * his/her credentials be refreshed + * @returns the updated credentials */ - const refreshAccessToken = (credentials: Credentials, userId: string) => { - return new Promise<CredentialsResult>(resolve => { - let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; - let queryParameters = { - refreshToken: credentials.refresh_token, - grant_type: "refresh_token", - ...installed - }; - let url = `${refreshEndpoint}?${qs.stringify(queryParameters)}`; - request.post(url, headerParameters).then(async response => { - let { access_token, expires_in } = JSON.parse(response); - const expiry_date = new Date().getTime() + (expires_in * 1000); - await Database.Auxiliary.GoogleAuthenticationToken.Update(userId, access_token, expiry_date); - credentials.access_token = access_token; - credentials.expiry_date = expiry_date; - resolve({ credentials, refreshed: true }); - }); + async function refreshAccessToken(credentials: Credentials, userId: string): Promise<Credentials> { + let headerParameters = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; + let url = `${refreshEndpoint}?${qs.stringify({ + refreshToken: credentials.refresh_token, + grant_type: "refresh_token", + ...installed + })}`; + const { access_token, expires_in } = await new Promise<any>(async resolve => { + const response = await request.post(url, headerParameters); + resolve(JSON.parse(response)); }); - }; + // expires_in is in seconds, but we're building the new expiry date in milliseconds + const expiry_date = new Date().getTime() + (expires_in * 1000); + await Database.Auxiliary.GoogleAuthenticationToken.Update(userId, access_token, expiry_date); + // update the relevant properties + credentials.access_token = access_token; + credentials.expiry_date = expiry_date; + return credentials; + } }
\ No newline at end of file diff --git a/src/server/index.ts b/src/server/index.ts index eb19c71a9..860cde3b5 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -593,11 +593,11 @@ function routeSetter(router: RouteManager) { subscription: RouteStore.readGoogleAccessToken, onValidation: async ({ user, res }) => { const userId = user.id; - const token = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId); + const token = await GoogleApiServerUtils.retrieveAccessToken(userId); if (!token) { - return res.send(await GoogleApiServerUtils.generateAuthenticationUrl()); + return res.send(GoogleApiServerUtils.generateAuthenticationUrl()); } - return GoogleApiServerUtils.retrieveAccessToken(userId).then(token => res.send(token)); + return res.send(token); } }); @@ -609,7 +609,7 @@ function routeSetter(router: RouteManager) { } }); - const tokenError = "Unable to successfully upload bytes for all images!"; + const authenticationError = "Unable to authenticate Google credentials before uploading to Google Photos!"; const mediaError = "Unable to convert all uploaded bytes to media items!"; router.addSupervisedRoute({ @@ -618,8 +618,12 @@ function routeSetter(router: RouteManager) { onValidation: async ({ user, req, res }) => { const { media } = req.body; - let failed: number[] = []; const token = await GoogleApiServerUtils.retrieveAccessToken(user.id); + if (!token) { + return _error(res, authenticationError); + } + + let failed: number[] = []; const newMediaItems = await BatchedArray.from<GooglePhotosUploadUtils.MediaInput>(media, { batchSize: 25 }).batchedMapPatientInterval( { magnitude: 100, unit: TimeUnit.Milliseconds }, async (batch: GooglePhotosUploadUtils.MediaInput[]) => { |