aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/Network.ts12
-rw-r--r--src/client/apis/GoogleAuthenticationManager.tsx39
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts8
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts10
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx5
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts4
-rw-r--r--src/new_fields/RichTextUtils.ts4
-rw-r--r--src/server/RouteManager.ts4
-rw-r--r--src/server/RouteStore.ts3
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts171
-rw-r--r--src/server/index.ts14
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[]) => {