aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/ApiManagers/ExportManager.ts4
-rw-r--r--src/server/ApiManagers/UtilManager.ts36
-rw-r--r--src/server/RouteManager.ts1
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts353
-rw-r--r--src/server/apis/google/GooglePhotosUploadUtils.ts19
-rw-r--r--src/server/database.ts2
-rw-r--r--src/server/index.ts224
7 files changed, 385 insertions, 254 deletions
diff --git a/src/server/ApiManagers/ExportManager.ts b/src/server/ApiManagers/ExportManager.ts
index 261acbbe0..14ac7dd5b 100644
--- a/src/server/ApiManagers/ExportManager.ts
+++ b/src/server/ApiManagers/ExportManager.ts
@@ -26,7 +26,7 @@ export default class ExportManager extends ApiManager {
const id = req.params.docId;
const hierarchy: Hierarchy = {};
await buildHierarchyRecursive(id, hierarchy);
- BuildAndDispatchZip(res, zip => writeHierarchyRecursive(zip, hierarchy));
+ return BuildAndDispatchZip(res, zip => writeHierarchyRecursive(zip, hierarchy));
}
});
}
@@ -48,7 +48,7 @@ export async function BuildAndDispatchZip(res: express.Response, mutator: ZipMut
const zip = Archiver('zip');
zip.pipe(res);
await mutator(zip);
- zip.finalize();
+ return zip.finalize();
}
/**
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index a3f802b20..61cda2e9b 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -10,13 +10,16 @@ export default class UtilManager extends ApiManager {
register({
method: Method.GET,
subscription: "/pull",
- onValidation: ({ res }) => {
- exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', err => {
- if (err) {
- res.send(err.message);
- return;
- }
- res.redirect("/");
+ onValidation: async ({ res }) => {
+ return new Promise<void>(resolve => {
+ exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', err => {
+ if (err) {
+ res.send(err.message);
+ return;
+ }
+ res.redirect("/");
+ resolve();
+ });
});
}
});
@@ -24,14 +27,14 @@ export default class UtilManager extends ApiManager {
register({
method: Method.GET,
subscription: "/buxton",
- onValidation: ({ res }) => {
+ onValidation: async ({ res }) => {
let cwd = '../scraping/buxton';
let onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); };
let onRejected = (err: any) => { console.error(err.message); res.send(err); };
let tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
- command_line('python scraper.py', cwd).then(onResolved, tryPython3);
+ return command_line('python scraper.py', cwd).then(onResolved, tryPython3);
},
});
@@ -39,12 +42,15 @@ export default class UtilManager extends ApiManager {
method: Method.GET,
subscription: "/version",
onValidation: ({ res }) => {
- exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout) => {
- if (err) {
- res.send(err.message);
- return;
- }
- res.send(stdout);
+ return new Promise<void>(resolve => {
+ exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout) => {
+ if (err) {
+ res.send(err.message);
+ return;
+ }
+ res.send(stdout);
+ });
+ resolve();
});
}
});
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index ef083a88a..21ce9c9e4 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -75,6 +75,7 @@ export default class RouteManager {
}
setTimeout(() => {
if (!res.headersSent) {
+ console.log("Initiating fallback for ", target);
const warning = `request to ${target} fell through - this is a fallback response`;
res.send({ warning });
}
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 6093197f1..b9984649e 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -1,61 +1,175 @@
import { google } from "googleapis";
-import { createInterface } from "readline";
-import { readFile, writeFile } from "fs";
-import { OAuth2Client, Credentials } from "google-auth-library";
+import { readFile } from "fs";
+import { OAuth2Client, Credentials, OAuth2ClientOptions } from "google-auth-library";
import { Opt } from "../../../new_fields/Doc";
import { GlobalOptions } from "googleapis-common";
import { GaxiosResponse } from "gaxios";
import request = require('request-promise');
import * as qs from 'query-string';
-import Photos = require('googlephotos');
import { Database } from "../../database";
-const path = require("path");
+import path from "path";
/**
- * Server side authentication for Google Api queries.
+ *
+ */
+const prefix = 'https://www.googleapis.com/auth/';
+
+/**
+ *
+ */
+const refreshEndpoint = "https://oauth2.googleapis.com/token";
+
+/**
+ *
+ */
+const SCOPES = [
+ 'documents.readonly',
+ 'documents',
+ 'presentations',
+ 'presentations.readonly',
+ 'drive',
+ 'drive.file',
+ 'photoslibrary',
+ 'photoslibrary.appendonly',
+ 'photoslibrary.sharing',
+ 'userinfo.profile'
+];
+
+/**
+ * This namespace manages server side authentication for Google API queries, either
+ * from the standard v1 APIs or the Google Photos REST API.
*/
export namespace GoogleApiServerUtils {
- // If modifying these scopes, delete token.json.
- const prefix = 'https://www.googleapis.com/auth/';
- const SCOPES = [
- 'documents.readonly',
- 'documents',
- 'presentations',
- 'presentations.readonly',
- 'drive',
- 'drive.file',
- 'photoslibrary',
- 'photoslibrary.appendonly',
- 'photoslibrary.sharing',
- 'userinfo.profile'
- ];
-
- export const parseBuffer = (data: Buffer) => JSON.parse(data.toString());
+ /**
+ *
+ */
+ export interface CredentialsResult {
+ credentials: Credentials;
+ refreshed: boolean;
+ }
+
+ /**
+ *
+ */
+ export interface UserInfo {
+ at_hash: string;
+ aud: string;
+ azp: string;
+ exp: number;
+ family_name: string;
+ given_name: string;
+ iat: number;
+ iss: string;
+ locale: string;
+ name: string;
+ picture: string;
+ sub: string;
+ }
+ /**
+ *
+ */
export enum Service {
Documents = "Documents",
Slides = "Slides"
}
+ /**
+ *
+ */
export interface CredentialInformation {
credentialsPath: string;
userId: string;
}
+ /**
+ *
+ */
+ let installed: OAuth2ClientOptions;
+
+ /**
+ * This is a global authorization client that is never
+ * passed around, and whose credentials are never set.
+ * Its job is purely to generate new authentication urls
+ * (users will follow to get to Google's permissions GUI)
+ * and to use the codes returned from that process to generate the
+ * initial credentials.
+ */
+ let worker: OAuth2Client;
+
+ /**
+ *
+ */
export type ApiResponse = Promise<GaxiosResponse>;
+
+ /**
+ *
+ */
export type ApiRouter = (endpoint: Endpoint, parameters: any) => ApiResponse;
+
+ /**
+ *
+ */
export type ApiHandler = (parameters: any, methodOptions?: any) => ApiResponse;
+
+ /**
+ *
+ */
export type Action = "create" | "retrieve" | "update";
- export type Endpoint = { get: ApiHandler, create: ApiHandler, batchUpdate: ApiHandler };
+ /**
+ *
+ */
+ export interface Endpoint {
+ get: ApiHandler;
+ create: ApiHandler;
+ batchUpdate: ApiHandler;
+ }
+
+ /**
+ *
+ */
export type EndpointParameters = GlobalOptions & { version: "v1" };
- export const GetEndpoint = (sector: string, paths: CredentialInformation) => {
+ /**
+ *
+ */
+ export const loadClientSecret = async () => {
+ return new Promise<void>((resolve, reject) => {
+ readFile(path.join(__dirname, "../../credentials/google_docs_credentials.json"), async (err, projectCredentials) => {
+ if (err) {
+ reject(err);
+ return console.log('Error loading client secret file:', err);
+ }
+ const { client_secret, client_id, redirect_uris } = JSON.parse(projectCredentials.toString()).installed;
+ // initialize the global authorization client
+ installed = {
+ clientId: client_id,
+ clientSecret: client_secret,
+ redirectUri: redirect_uris[0]
+ };
+ worker = generateClient();
+ resolve();
+ });
+ });
+ };
+
+ /**
+ *
+ */
+ const authenticationClients = new Map<String, OAuth2Client>();
+
+ /**
+ *
+ * @param sector
+ * @param userId
+ */
+ export const GetEndpoint = (sector: string, userId: string) => {
return new Promise<Opt<Endpoint>>(resolve => {
- RetrieveCredentials(paths).then(authentication => {
+ retrieveOAuthClient(userId).then(auth => {
let routed: Opt<Endpoint>;
- let parameters: EndpointParameters = { auth: authentication.client, version: "v1" };
+ let parameters: EndpointParameters = { auth, version: "v1" };
switch (sector) {
case Service.Documents:
routed = google.docs(parameters).documents;
@@ -69,53 +183,95 @@ export namespace GoogleApiServerUtils {
});
};
- export const RetrieveAccessToken = (information: CredentialInformation) => {
+ /**
+ *
+ * @param userId
+ */
+ export const retrieveAccessToken = (userId: string): Promise<string> => {
return new Promise<string>((resolve, reject) => {
- RetrieveCredentials(information).then(
- credentials => resolve(credentials.token.access_token!),
+ retrieveCredentials(userId).then(
+ ({ credentials }) => resolve(credentials.access_token!),
error => reject(`Error: unable to authenticate Google Photos API request.\n${error}`)
);
});
};
- let AuthorizationManager: OAuth2Client;
-
- export const LoadOAuthClient = async () => {
- return new Promise<void>((resolve, reject) => {
- readFile(path.join(__dirname, "../../credentials/google_docs_credentials.json"), async (err, credentials) => {
- if (err) {
- reject(err);
- return console.log('Error loading client secret file:', err);
- }
- const { client_secret, client_id, redirect_uris } = parseBuffer(credentials).installed;
- AuthorizationManager = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
- resolve();
- });
+ /**
+ *
+ * @param userId
+ */
+ export const retrieveOAuthClient = (userId: string): Promise<OAuth2Client> => {
+ return new Promise<OAuth2Client>((resolve, reject) => {
+ retrieveCredentials(userId).then(
+ ({ credentials, refreshed }) => {
+ let client = authenticationClients.get(userId);
+ if (!client) {
+ authenticationClients.set(userId, client = generateClient(credentials));
+ } else if (refreshed) {
+ client.setCredentials(credentials);
+ }
+ resolve(client);
+ },
+ error => reject(`Error: unable to instantiate and certify a new OAuth2 client.\n${error}`)
+ );
});
};
- export const GenerateAuthenticationUrl = async (information: CredentialInformation) => {
- return AuthorizationManager.generateAuthUrl({
+ /**
+ *
+ * @param credentials
+ */
+ function generateClient(credentials?: Credentials) {
+ const client = new google.auth.OAuth2(installed);
+ credentials && client.setCredentials(credentials);
+ return client;
+ }
+
+ /**
+ *
+ */
+ export const generateAuthenticationUrl = async () => {
+ return worker.generateAuthUrl({
access_type: 'offline',
scope: SCOPES.map(relative => prefix + relative),
});
};
+ /**
+ * This is what we return to the server in processNewUser(), after the
+ * worker OAuth2Client has used the user-pasted authentication code
+ * to retrieve an access token and an info token. The avatar is the
+ * URL to the Google-hosted mono-color, single white letter profile 'image'.
+ */
export interface GoogleAuthenticationResult {
access_token: string;
avatar: string;
name: string;
}
- export const ProcessClientSideCode = async (information: CredentialInformation, authenticationCode: string): Promise<GoogleAuthenticationResult> => {
+
+ /**
+ * This method receives the authentication code that the
+ * user pasted into the overlay in the client side and uses the worker
+ * 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
+ * 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.
+ * @returns the information necessary to authenticate a client side google photos request
+ * 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) => {
- AuthorizationManager.getToken(authenticationCode, async (err, token) => {
- if (err || !token) {
+ worker.getToken(authenticationCode, async (err, credentials) => {
+ if (err || !credentials) {
reject(err);
return console.error('Error retrieving access token', err);
}
- AuthorizationManager.setCredentials(token);
- const enriched = injectUserInfo(token);
- await Database.Auxiliary.GoogleAuthenticationToken.Write(information.userId, enriched);
+ const enriched = injectUserInfo(credentials);
+ await Database.Auxiliary.GoogleAuthenticationToken.Write(userId, enriched);
const { given_name, picture } = enriched.userInfo;
resolve({
access_token: enriched.access_token!,
@@ -127,86 +283,66 @@ export namespace GoogleApiServerUtils {
};
/**
+ * This type represents the union of the full set of OAuth2 credentials
+ * and all of a Google user's publically available information. This is the strucure
+ * of the JSON object we ultimately store in the googleAuthentication table of the database.
+ */
+ export type EnrichedCredentials = Credentials & { userInfo: UserInfo };
+
+ /**
* It's pretty cool: the credentials id_token is split into thirds by periods.
* The middle third contains a base64-encoded JSON string with all the
* user info contained in the interface below. So, we isolate that middle third,
* base64 decode with atob and parse the JSON.
* @param credentials the client credentials returned from OAuth after the user
* has executed the authentication routine
+ * @returns the full set of credentials in the structure in which they'll be stored
+ * in the database.
*/
const injectUserInfo = (credentials: Credentials): EnrichedCredentials => {
const userInfo = JSON.parse(atob(credentials.id_token!.split(".")[1]));
return { ...credentials, userInfo };
};
- export type EnrichedCredentials = Credentials & { userInfo: UserInfo };
- export interface UserInfo {
- at_hash: string;
- aud: string;
- azp: string;
- exp: number;
- family_name: string;
- given_name: string;
- iat: number;
- iss: string;
- locale: string;
- name: string;
- picture: string;
- sub: string;
- }
-
- export const RetrieveCredentials = (information: CredentialInformation) => {
- return new Promise<TokenResult>((resolve, reject) => {
- readFile(information.credentialsPath, async (err, credentials) => {
- if (err) {
- reject(err);
- return console.log('Error loading client secret file:', err);
- }
- authorize(parseBuffer(credentials), information.userId).then(resolve, reject);
- });
- });
- };
-
- export const RetrievePhotosEndpoint = (paths: CredentialInformation) => {
- return new Promise<any>((resolve, reject) => {
- RetrieveAccessToken(paths).then(
- token => resolve(new Photos(token)),
- reject
- );
- });
- };
-
- type TokenResult = { token: Credentials, client: OAuth2Client };
/**
- * Create an OAuth2 client with the given credentials, and returns the promise resolving to the authenticated client
- * @param {Object} credentials The authorization client credentials.
+ * 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
*/
- export function authorize(credentials: any, userId: string): Promise<TokenResult> {
- const { client_secret, client_id, redirect_uris } = credentials.installed;
- const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
- return new Promise<TokenResult>((resolve, reject) => {
- // Attempting to authorize user (${userId})
- Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId).then(token => {
- if (token!.expiry_date! < new Date().getTime()) {
+ 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 refreshToken(token!, client_id, client_secret, oAuth2Client, userId).then(resolve, reject);
+ return refreshAccessToken(credentials, userId).then(resolve, reject);
}
// Authentication successful!
- oAuth2Client.setCredentials(token!);
- resolve({ token: token!, client: oAuth2Client });
+ resolve({ credentials, refreshed: false });
});
});
- }
+ };
- const refreshEndpoint = "https://oauth2.googleapis.com/token";
- const refreshToken = (credentials: Credentials, client_id: string, client_secret: string, oAuth2Client: OAuth2Client, userId: string) => {
- return new Promise<TokenResult>(resolve => {
+ /**
+ * This function submits a request to OAuth with the local refresh token
+ * to revalidate the credentials for a given Google user associated with
+ * 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
+ */
+ 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,
- client_id,
- client_secret,
- grant_type: "refresh_token"
+ grant_type: "refresh_token",
+ ...installed
};
let url = `${refreshEndpoint}?${qs.stringify(queryParameters)}`;
request.post(url, headerParameters).then(async response => {
@@ -215,8 +351,7 @@ export namespace GoogleApiServerUtils {
await Database.Auxiliary.GoogleAuthenticationToken.Update(userId, access_token, expiry_date);
credentials.access_token = access_token;
credentials.expiry_date = expiry_date;
- oAuth2Client.setCredentials(credentials);
- resolve({ token: credentials, client: oAuth2Client });
+ resolve({ credentials, refreshed: true });
});
});
};
diff --git a/src/server/apis/google/GooglePhotosUploadUtils.ts b/src/server/apis/google/GooglePhotosUploadUtils.ts
index 4a67e57cc..172fa8d46 100644
--- a/src/server/apis/google/GooglePhotosUploadUtils.ts
+++ b/src/server/apis/google/GooglePhotosUploadUtils.ts
@@ -20,19 +20,12 @@ export namespace GooglePhotosUploadUtils {
}
const prepend = (extension: string) => `https://photoslibrary.googleapis.com/v1/${extension}`;
- const headers = (type: string) => ({
+ const headers = (type: string, token: string) => ({
'Content-Type': `application/${type}`,
- 'Authorization': Bearer,
+ 'Authorization': `Bearer ${token}`,
});
- let Bearer: string;
-
- export const initialize = async (information: GoogleApiServerUtils.CredentialInformation) => {
- const token = await GoogleApiServerUtils.RetrieveAccessToken(information);
- Bearer = `Bearer ${token}`;
- };
-
- export const DispatchGooglePhotosUpload = async (url: string) => {
+ export const DispatchGooglePhotosUpload = async (bearerToken: string, url: string) => {
if (!DashUploadUtils.imageFormats.includes(path.extname(url))) {
return undefined;
}
@@ -40,7 +33,7 @@ export namespace GooglePhotosUploadUtils {
const parameters = {
method: 'POST',
headers: {
- ...headers('octet-stream'),
+ ...headers('octet-stream', bearerToken),
'X-Goog-Upload-File-Name': path.basename(url),
'X-Goog-Upload-Protocol': 'raw'
},
@@ -56,13 +49,13 @@ export namespace GooglePhotosUploadUtils {
}));
};
- export const CreateMediaItems = async (newMediaItems: NewMediaItem[], album?: { id: string }): Promise<MediaItemCreationResult> => {
+ export const CreateMediaItems = async (bearerToken: string, newMediaItems: NewMediaItem[], album?: { id: string }): Promise<MediaItemCreationResult> => {
const newMediaItemResults = await BatchedArray.from(newMediaItems, { batchSize: 50 }).batchedMapPatientInterval(
{ magnitude: 100, unit: TimeUnit.Milliseconds },
async (batch: NewMediaItem[]) => {
const parameters = {
method: 'POST',
- headers: headers('json'),
+ headers: headers('json', bearerToken),
uri: prepend('mediaItems:batchCreate'),
body: { newMediaItems: batch } as any,
json: true
diff --git a/src/server/database.ts b/src/server/database.ts
index 12626e594..79dd26b7d 100644
--- a/src/server/database.ts
+++ b/src/server/database.ts
@@ -298,7 +298,7 @@ export namespace Database {
export type StoredCredentials = Credentials & { _id: string };
- export const Fetch = async (userId: string, removeId = true) => {
+ export const Fetch = async (userId: string, removeId = true): Promise<Opt<StoredCredentials>> => {
return SanitizedSingletonQuery<StoredCredentials>({ userId }, GoogleAuthentication, removeId);
};
diff --git a/src/server/index.ts b/src/server/index.ts
index 384800f23..eb19c71a9 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -65,7 +65,7 @@ async function PreliminaryFunctions() {
resolve();
});
});
- await GoogleApiServerUtils.LoadOAuthClient();
+ await GoogleApiServerUtils.loadClientSecret();
await DashUploadUtils.createIfNotExists(pdfDirectory);
await Database.tryInitializeConnection();
}
@@ -225,55 +225,58 @@ function routeSetter(router: RouteManager) {
}
}
};
- form.parse(req, async (err, fields, files) => {
- remap = fields.remap !== "false";
- let id: string = "";
- try {
- for (const name in files) {
- const path_2 = files[name].path;
- const zip = new AdmZip(path_2);
- zip.getEntries().forEach((entry: any) => {
- if (!entry.entryName.startsWith("files/")) return;
- let dirname = path.dirname(entry.entryName) + "/";
- let extname = path.extname(entry.entryName);
- let basename = path.basename(entry.entryName).split(".")[0];
- // zip.extractEntryTo(dirname + basename + "_o" + extname, __dirname + RouteStore.public, true, false);
- // zip.extractEntryTo(dirname + basename + "_s" + extname, __dirname + RouteStore.public, true, false);
- // zip.extractEntryTo(dirname + basename + "_m" + extname, __dirname + RouteStore.public, true, false);
- // zip.extractEntryTo(dirname + basename + "_l" + extname, __dirname + RouteStore.public, true, false);
+ return new Promise<void>(resolve => {
+ form.parse(req, async (_err, fields, files) => {
+ remap = fields.remap !== "false";
+ let id: string = "";
+ try {
+ for (const name in files) {
+ const path_2 = files[name].path;
+ const zip = new AdmZip(path_2);
+ zip.getEntries().forEach((entry: any) => {
+ if (!entry.entryName.startsWith("files/")) return;
+ let dirname = path.dirname(entry.entryName) + "/";
+ let extname = path.extname(entry.entryName);
+ let basename = path.basename(entry.entryName).split(".")[0];
+ // zip.extractEntryTo(dirname + basename + "_o" + extname, __dirname + RouteStore.public, true, false);
+ // zip.extractEntryTo(dirname + basename + "_s" + extname, __dirname + RouteStore.public, true, false);
+ // zip.extractEntryTo(dirname + basename + "_m" + extname, __dirname + RouteStore.public, true, false);
+ // zip.extractEntryTo(dirname + basename + "_l" + extname, __dirname + RouteStore.public, true, false);
+ try {
+ zip.extractEntryTo(entry.entryName, __dirname + RouteStore.public, true, false);
+ dirname = "/" + dirname;
+
+ fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_o" + extname));
+ fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_s" + extname));
+ fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_m" + extname));
+ fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_l" + extname));
+ } catch (e) {
+ console.log(e);
+ }
+ });
+ const json = zip.getEntry("doc.json");
+ let docs: any;
try {
- zip.extractEntryTo(entry.entryName, __dirname + RouteStore.public, true, false);
- dirname = "/" + dirname;
-
- fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_o" + extname));
- fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_s" + extname));
- fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_m" + extname));
- fs.createReadStream(__dirname + RouteStore.public + dirname + basename + extname).pipe(fs.createWriteStream(__dirname + RouteStore.public + dirname + basename + "_l" + extname));
- } catch (e) {
- console.log(e);
- }
- });
- const json = zip.getEntry("doc.json");
- let docs: any;
- try {
- let data = JSON.parse(json.getData().toString("utf8"));
- docs = data.docs;
- id = data.id;
- docs = Object.keys(docs).map(key => docs[key]);
- docs.forEach(mapFn);
- await Promise.all(docs.map((doc: any) => new Promise(res => Database.Instance.replace(doc.id, doc, (err, r) => {
- err && console.log(err);
- res();
- }, true, "newDocuments"))));
- } catch (e) { console.log(e); }
- fs.unlink(path_2, () => { });
- }
- if (id) {
- res.send(JSON.stringify(getId(id)));
- } else {
- res.send(JSON.stringify("error"));
- }
- } catch (e) { console.log(e); }
+ let data = JSON.parse(json.getData().toString("utf8"));
+ docs = data.docs;
+ id = data.id;
+ docs = Object.keys(docs).map(key => docs[key]);
+ docs.forEach(mapFn);
+ await Promise.all(docs.map((doc: any) => new Promise(res => Database.Instance.replace(doc.id, doc, (err, r) => {
+ err && console.log(err);
+ res();
+ }, true, "newDocuments"))));
+ } catch (e) { console.log(e); }
+ fs.unlink(path_2, () => { });
+ }
+ if (id) {
+ res.send(JSON.stringify(getId(id)));
+ } else {
+ res.send(JSON.stringify("error"));
+ }
+ } catch (e) { console.log(e); }
+ resolve();
+ });
});
}
});
@@ -285,22 +288,25 @@ function routeSetter(router: RouteManager) {
let filename = req.params.filename;
let noExt = filename.substring(0, filename.length - ".png".length);
let pagenumber = parseInt(noExt.split('-')[1]);
- fs.exists(uploadDirectory + filename, (exists: boolean) => {
- console.log(`${uploadDirectory + filename} ${exists ? "exists" : "does not exist"}`);
- if (exists) {
- let input = fs.createReadStream(uploadDirectory + filename);
- probe(input, (err: any, result: any) => {
- if (err) {
- console.log(err);
- console.log(`error on ${filename}`);
- return;
- }
- res.send({ path: "/files/" + filename, width: result.width, height: result.height });
- });
- }
- else {
- LoadPage(uploadDirectory + filename.substring(0, filename.length - noExt.split('-')[1].length - ".PNG".length - 1) + ".pdf", pagenumber, res);
- }
+ return new Promise<void>(resolve => {
+ fs.exists(uploadDirectory + filename, (exists: boolean) => {
+ console.log(`${uploadDirectory + filename} ${exists ? "exists" : "does not exist"}`);
+ if (exists) {
+ let input = fs.createReadStream(uploadDirectory + filename);
+ probe(input, (err: any, result: any) => {
+ if (err) {
+ console.log(err);
+ console.log(`error on ${filename}`);
+ return;
+ }
+ res.send({ path: "/files/" + filename, width: result.width, height: result.height });
+ });
+ }
+ else {
+ LoadPage(uploadDirectory + filename.substring(0, filename.length - noExt.split('-')[1].length - ".PNG".length - 1) + ".pdf", pagenumber, res);
+ }
+ resolve();
+ });
});
}
});
@@ -414,8 +420,8 @@ function routeSetter(router: RouteManager) {
var canvas = createCanvas(width, height);
var context = canvas.getContext('2d');
return {
- canvas: canvas,
- context: context,
+ canvas,
+ context
};
}
@@ -442,37 +448,39 @@ function routeSetter(router: RouteManager) {
router.addSupervisedRoute({
method: Method.POST,
subscription: RouteStore.upload,
- onValidation: ({ req, res }) => {
+ onValidation: async ({ req, res }) => {
let form = new formidable.IncomingForm();
form.uploadDir = uploadDirectory;
form.keepExtensions = true;
- form.parse(req, async (_err, _fields, files) => {
- let results: ImageFileResponse[] = [];
- for (const key in files) {
- const { type, path: location, name } = files[key];
- const filename = path.basename(location);
- let uploadInformation: Opt<DashUploadUtils.UploadInformation>;
- if (filename.endsWith(".pdf")) {
- let dataBuffer = fs.readFileSync(uploadDirectory + filename);
- const result: ParsedPDF = await pdf(dataBuffer);
- await new Promise<void>(resolve => {
- const path = pdfDirectory + "/" + filename.substring(0, filename.length - ".pdf".length) + ".txt";
- fs.createWriteStream(path).write(result.text, error => {
- if (!error) {
- resolve();
- } else {
- reject(error);
- }
+ return new Promise<void>(resolve => {
+ form.parse(req, async (_err, _fields, files) => {
+ let results: ImageFileResponse[] = [];
+ for (const key in files) {
+ const { type, path: location, name } = files[key];
+ const filename = path.basename(location);
+ let uploadInformation: Opt<DashUploadUtils.UploadInformation>;
+ if (filename.endsWith(".pdf")) {
+ let dataBuffer = fs.readFileSync(uploadDirectory + filename);
+ const result: ParsedPDF = await pdf(dataBuffer);
+ await new Promise<void>(resolve => {
+ const path = pdfDirectory + "/" + filename.substring(0, filename.length - ".pdf".length) + ".txt";
+ fs.createWriteStream(path).write(result.text, error => {
+ if (!error) {
+ resolve();
+ } else {
+ reject(error);
+ }
+ });
});
- });
- } else {
- uploadInformation = await DashUploadUtils.UploadImage(uploadDirectory + filename, filename);
+ } else {
+ uploadInformation = await DashUploadUtils.UploadImage(uploadDirectory + filename, filename);
+ }
+ const exif = uploadInformation ? uploadInformation.exifData : undefined;
+ results.push({ name, type, path: `/files/${filename}`, exif });
}
- const exif = uploadInformation ? uploadInformation.exifData : undefined;
- results.push({ name, type, path: `/files/${filename}`, exif });
-
- }
- _success(res, results);
+ _success(res, results);
+ resolve();
+ });
});
}
});
@@ -500,7 +508,7 @@ function routeSetter(router: RouteManager) {
res.status(401).send("incorrect parameters specified");
return;
}
- imageDataUri.outputFile(uri, uploadDirectory + filename).then((savedName: string) => {
+ return imageDataUri.outputFile(uri, uploadDirectory + filename).then((savedName: string) => {
const ext = path.extname(savedName);
let resizers = [
{ resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
@@ -553,8 +561,6 @@ function routeSetter(router: RouteManager) {
}
});
- const credentialsPath = path.join(__dirname, "./credentials/google_docs_credentials.json");
-
const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
["create", (api, params) => api.create(params)],
["retrieve", (api, params) => api.get(params)],
@@ -564,10 +570,10 @@ function routeSetter(router: RouteManager) {
router.addSupervisedRoute({
method: Method.POST,
subscription: new RouteSubscriber(RouteStore.googleDocs).add("sector", "action"),
- onValidation: ({ req, res }) => {
+ onValidation: async ({ req, res, user }) => {
let sector: GoogleApiServerUtils.Service = req.params.sector as GoogleApiServerUtils.Service;
let action: GoogleApiServerUtils.Action = req.params.action as GoogleApiServerUtils.Action;
- GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentialsPath, userId: req.headers.userId as string }).then(endpoint => {
+ return GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], user.id).then(endpoint => {
let handler = EndpointHandlerMap.get(action);
if (endpoint && handler) {
let execute = handler(endpoint, req.body).then(
@@ -588,11 +594,10 @@ function routeSetter(router: RouteManager) {
onValidation: async ({ user, res }) => {
const userId = user.id;
const token = await Database.Auxiliary.GoogleAuthenticationToken.Fetch(userId);
- const information = { credentialsPath, userId };
if (!token) {
- return res.send(await GoogleApiServerUtils.GenerateAuthenticationUrl(information));
+ return res.send(await GoogleApiServerUtils.generateAuthenticationUrl());
}
- GoogleApiServerUtils.RetrieveAccessToken(information).then(token => res.send(token));
+ return GoogleApiServerUtils.retrieveAccessToken(userId).then(token => res.send(token));
}
});
@@ -600,37 +605,28 @@ function routeSetter(router: RouteManager) {
method: Method.POST,
subscription: RouteStore.writeGoogleAccessToken,
onValidation: async ({ user, req, res }) => {
- const userId = user.id;
- const information = { credentialsPath, userId };
- res.send(await GoogleApiServerUtils.ProcessClientSideCode(information, req.body.authenticationCode));
+ res.send(await GoogleApiServerUtils.processNewUser(user.id, req.body.authenticationCode));
}
});
const tokenError = "Unable to successfully upload bytes for all images!";
const mediaError = "Unable to convert all uploaded bytes to media items!";
- const userIdError = "Unable to parse the identification of the user!";
router.addSupervisedRoute({
method: Method.POST,
subscription: RouteStore.googlePhotosMediaUpload,
onValidation: async ({ user, req, res }) => {
const { media } = req.body;
- const userId = user.id;
- if (!userId) {
- return _error(res, userIdError);
- }
-
- await GooglePhotosUploadUtils.initialize({ credentialsPath, userId });
let failed: number[] = [];
-
+ const token = await GoogleApiServerUtils.retrieveAccessToken(user.id);
const newMediaItems = await BatchedArray.from<GooglePhotosUploadUtils.MediaInput>(media, { batchSize: 25 }).batchedMapPatientInterval(
{ magnitude: 100, unit: TimeUnit.Milliseconds },
async (batch: GooglePhotosUploadUtils.MediaInput[]) => {
const newMediaItems: NewMediaItem[] = [];
for (let index = 0; index < batch.length; index++) {
const element = batch[index];
- const uploadToken = await GooglePhotosUploadUtils.DispatchGooglePhotosUpload(element.url);
+ const uploadToken = await GooglePhotosUploadUtils.DispatchGooglePhotosUpload(token, element.url);
if (!uploadToken) {
failed.push(index);
} else {
@@ -649,7 +645,7 @@ function routeSetter(router: RouteManager) {
console.error(`Unable to upload ${failedCount} image${failedCount === 1 ? "" : "s"} to Google's servers`);
}
- GooglePhotosUploadUtils.CreateMediaItems(newMediaItems, req.body.album).then(
+ return GooglePhotosUploadUtils.CreateMediaItems(token, newMediaItems, req.body.album).then(
result => _success(res, { results: result.newMediaItemResults, failed }),
error => _error(res, mediaError, error)
);