aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts73
-rw-r--r--src/client/views/MainView.tsx17
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx1
-rw-r--r--src/server/RouteStore.ts2
-rw-r--r--src/server/apis/google/GoogleApiServerUtils.ts28
-rw-r--r--src/server/apis/google/GooglePhotosServerUtils.ts68
-rw-r--r--src/server/apis/google/GooglePhotosUploadUtils.ts122
-rw-r--r--src/server/apis/google/typings/albums.ts150
-rw-r--r--src/server/credentials/google_docs_token.json2
-rw-r--r--src/server/index.ts75
11 files changed, 206 insertions, 334 deletions
diff --git a/package.json b/package.json
index f0f2b467e..f56e34ce0 100644
--- a/package.json
+++ b/package.json
@@ -224,4 +224,4 @@
"xoauth2": "^1.2.0",
"youtube": "^0.1.0"
}
-}
+} \ No newline at end of file
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index b95cc98c9..2b72800a9 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -1,53 +1,42 @@
-import { Album } from "../../../server/apis/google/typings/albums";
-import { PostToServer } from "../../../Utils";
+import { PostToServer, Utils } from "../../../Utils";
import { RouteStore } from "../../../server/RouteStore";
import { ImageField } from "../../../new_fields/URLField";
+import { StrCast, Cast } from "../../../new_fields/Types";
+import { Doc, Opt } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import requestImageSize = require('../../util/request-image-size');
+import Photos = require('googlephotos');
export namespace GooglePhotosClientUtils {
- export const Create = async (title: string) => {
- let parameters = {
- action: Album.Action.Create,
- body: { album: { title } }
- } as Album.Create;
- return PostToServer(RouteStore.googlePhotosQuery, parameters);
- };
+ export type AlbumReference = { id: string } | { title: string };
+ export const endpoint = () => fetch(Utils.prepend(RouteStore.googlePhotosAccessToken)).then(async response => new Photos(await response.text()));
- export const List = async (options?: Partial<Album.ListOptions>) => {
- let parameters = {
- action: Album.Action.List,
- parameters: {
- pageSize: (options ? options.pageSize : 20) || 20,
- pageToken: (options ? options.pageToken : undefined) || undefined,
- excludeNonAppCreatedData: (options ? options.excludeNonAppCreatedData : false) || false,
- } as Album.ListOptions
- } as Album.List;
- return PostToServer(RouteStore.googlePhotosQuery, parameters);
- };
+ export interface MediaInput {
+ description: string;
+ source: string;
+ }
- export const Get = async (albumId: string) => {
- let parameters = {
- action: Album.Action.Get,
- albumId
- } as Album.Get;
- return PostToServer(RouteStore.googlePhotosQuery, parameters);
- };
-
- export const toDataURL = (field: ImageField | undefined) => {
- if (!field) {
- return undefined;
+ export const UploadMedia = async (sources: Doc[], album?: AlbumReference) => {
+ if (album && "title" in album) {
+ album = (await endpoint()).albums.create(album.title);
+ }
+ const media: MediaInput[] = [];
+ sources.forEach(document => {
+ const data = Cast(Doc.GetProto(document).data, ImageField);
+ const description = StrCast(document.caption);
+ if (!data) {
+ return undefined;
+ }
+ media.push({
+ source: data.url.href,
+ description,
+ } as MediaInput);
+ });
+ if (media.length) {
+ return PostToServer(RouteStore.googlePhotosMediaUpload, { media, album });
}
- const image = document.createElement("img");
- image.src = field.url.href;
- image.width = 200;
- image.height = 200;
- const canvas = document.createElement("canvas");
- canvas.width = image.width;
- canvas.height = image.height;
- const ctx = canvas.getContext("2d")!;
- ctx.drawImage(image, 0, 0);
- const dataUrl = canvas.toDataURL("image/png");
- return dataUrl.replace(/^data:image\/(png|jpg);base64,/, "");
+ return undefined;
};
} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c1c95fc88..6d366216e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,11 +40,10 @@ import { PreviewCursor } from './PreviewCursor';
import { FilterBox } from './search/FilterBox';
import PresModeMenu from './presentationview/PresentationModeMenu';
import { PresBox } from './nodes/PresBox';
-import { docs_v1 } from 'googleapis';
-import { Album } from '../../server/apis/google/typings/albums';
import { GooglePhotosClientUtils } from '../apis/google_docs/GooglePhotosClientUtils';
import { ImageField } from '../../new_fields/URLField';
import { LinkFollowBox } from './linking/LinkFollowBox';
+import { DocumentManager } from '../util/DocumentManager';
@observer
export class MainView extends React.Component {
@@ -131,10 +130,7 @@ export class MainView extends React.Component {
window.removeEventListener("keydown", KeyManager.Instance.handle);
window.addEventListener("keydown", KeyManager.Instance.handle);
- let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
- let image = Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" });
- let parameters = { title: StrCast(image.title), MEDIA_BINARY_DATA: GooglePhotosClientUtils.toDataURL(Cast(image.data, ImageField)) };
- // PostToServer(RouteStore.googlePhotosMediaUpload, parameters).then(console.log);
+ this.executeGooglePhotosRoutine();
reaction(() => {
let workspaces = CurrentUserUtils.UserDocument.workspaces;
@@ -153,6 +149,15 @@ export class MainView extends React.Component {
}, { fireImmediately: true });
}
+ executeGooglePhotosRoutine = async () => {
+ let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
+ let doc = Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" });
+ doc.caption = "Well isn't this a nice cat image!";
+ let photos = await GooglePhotosClientUtils.endpoint();
+ let albumId = (await photos.albums.list(50)).albums.filter((album: any) => album.title === "This is a generically created album!")[0].id;
+ console.log(await GooglePhotosClientUtils.UploadMedia([doc], { id: albumId }));
+ }
+
componentWillUnMount() {
window.removeEventListener("keydown", KeyManager.Instance.handle);
//close presentation
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index b671d06ea..fda9ea33f 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -133,6 +133,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this.props.isOverlay) {
DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
}
+ FormattedTextBox.Instance = this;
}
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts
index 3b3fd9b4a..b221b71bc 100644
--- a/src/server/RouteStore.ts
+++ b/src/server/RouteStore.ts
@@ -32,7 +32,7 @@ export enum RouteStore {
// APIS
cognitiveServices = "/cognitiveservices",
googleDocs = "/googleDocs",
- googlePhotosQuery = "/googlePhotosQuery",
+ googlePhotosAccessToken = "/googlePhotosAccessToken",
googlePhotosMediaUpload = "/googlePhotosMediaUpload"
} \ No newline at end of file
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index b6330a609..ac8023ce1 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -7,6 +7,7 @@ import { GlobalOptions } from "googleapis-common";
import { GaxiosResponse } from "gaxios";
import request = require('request-promise');
import * as qs from 'query-string';
+import Photos = require('googlephotos');
/**
* Server side authentication for Google Api queries.
@@ -35,19 +36,19 @@ export namespace GoogleApiServerUtils {
}
export interface CredentialPaths {
- credentials: string;
- token: string;
+ credentialsPath: string;
+ tokenPath: string;
}
export type ApiResponse = Promise<GaxiosResponse>;
- export type ApiRouter = (endpoint: Endpoint, paramters: any) => ApiResponse;
+ export type ApiRouter = (endpoint: Endpoint, parameters: any) => ApiResponse;
export type ApiHandler = (parameters: any) => ApiResponse;
export type Action = "create" | "retrieve" | "update";
export type Endpoint = { get: ApiHandler, create: ApiHandler, batchUpdate: ApiHandler };
export type EndpointParameters = GlobalOptions & { version: "v1" };
- export const GetEndpoint = async (sector: string, paths: CredentialPaths) => {
+ export const GetEndpoint = (sector: string, paths: CredentialPaths) => {
return new Promise<Opt<Endpoint>>(resolve => {
RetrieveCredentials(paths).then(authentication => {
let routed: Opt<Endpoint>;
@@ -65,19 +66,19 @@ export namespace GoogleApiServerUtils {
});
};
- export const RetrieveCredentials = async (paths: CredentialPaths) => {
+ export const RetrieveCredentials = (paths: CredentialPaths) => {
return new Promise<TokenResult>((resolve, reject) => {
- readFile(paths.credentials, async (err, credentials) => {
+ readFile(paths.credentialsPath, async (err, credentials) => {
if (err) {
reject(err);
return console.log('Error loading client secret file:', err);
}
- authorize(parseBuffer(credentials), paths.token).then(resolve, reject);
+ authorize(parseBuffer(credentials), paths.tokenPath).then(resolve, reject);
});
});
};
- export const RetrieveAccessToken = async (paths: CredentialPaths) => {
+ export const RetrieveAccessToken = (paths: CredentialPaths) => {
return new Promise<string>((resolve, reject) => {
RetrieveCredentials(paths).then(
credentials => resolve(credentials.token.access_token!),
@@ -86,6 +87,15 @@ export namespace GoogleApiServerUtils {
});
};
+ export const RetrievePhotosEndpoint = (paths: CredentialPaths) => {
+ 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
@@ -126,7 +136,7 @@ export namespace GoogleApiServerUtils {
request.post(url, headerParameters).then(response => {
let parsed = JSON.parse(response);
credentials.access_token = parsed.access_token;
- credentials.expiry_date = new Date().getTime() + parsed.expires_in * 1000;
+ credentials.expiry_date = new Date().getTime() + (parsed.expires_in * 1000);
writeFile(token_path, JSON.stringify(credentials), (err) => {
if (err) {
console.error(err);
diff --git a/src/server/apis/google/GooglePhotosServerUtils.ts b/src/server/apis/google/GooglePhotosServerUtils.ts
deleted file mode 100644
index cb5464abc..000000000
--- a/src/server/apis/google/GooglePhotosServerUtils.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import request = require('request-promise');
-import { Album } from './typings/albums';
-import * as qs from 'query-string';
-
-const apiEndpoint = "https://photoslibrary.googleapis.com/v1/";
-
-export interface Authorization {
- token: string;
-}
-
-export namespace GooglePhotos {
-
- export type Query = Album.Query;
- export type QueryParameters = { query: GooglePhotos.Query };
- interface DispatchParameters {
- required: boolean;
- method: "GET" | "POST";
- ignore?: boolean;
- }
-
- export const ExecuteQuery = async (parameters: Authorization & QueryParameters): Promise<any> => {
- let action = parameters.query.action;
- let dispatch = SuffixMap.get(action)!;
- let suffix = Suffix(parameters, dispatch, action);
- if (suffix) {
- let query: any = parameters.query;
- let options: any = {
- headers: { 'Content-Type': 'application/json' },
- auth: { 'bearer': parameters.token },
- };
- if (query.body) {
- options.body = query.body;
- options.json = true;
- }
- let queryParameters = query.parameters;
- if (queryParameters) {
- suffix += `?${qs.stringify(queryParameters)}`;
- }
- let dispatcher = dispatch.method === "POST" ? request.post : request.get;
- return dispatcher(apiEndpoint + suffix, options);
- }
- };
-
- const Suffix = (parameters: QueryParameters, dispatch: DispatchParameters, action: Album.Action) => {
- let query: any = parameters.query;
- let id = query.albumId;
- let suffix = 'albums';
- if (dispatch.required) {
- if (!id) {
- return undefined;
- }
- suffix += `/${id}${dispatch.ignore ? "" : `:${action}`}`;
- }
- return suffix;
- };
-
- const SuffixMap = new Map<Album.Action, DispatchParameters>([
- [Album.Action.AddEnrichment, { required: true, method: "POST" }],
- [Album.Action.BatchAddMediaItems, { required: true, method: "POST" }],
- [Album.Action.BatchRemoveMediaItems, { required: true, method: "POST" }],
- [Album.Action.Create, { required: false, method: "POST" }],
- [Album.Action.Get, { required: true, ignore: true, method: "GET" }],
- [Album.Action.List, { required: false, method: "GET" }],
- [Album.Action.Share, { required: true, method: "POST" }],
- [Album.Action.Unshare, { required: true, method: "POST" }]
- ]);
-
-}
diff --git a/src/server/apis/google/GooglePhotosUploadUtils.ts b/src/server/apis/google/GooglePhotosUploadUtils.ts
index cd2a586eb..3b513aaf1 100644
--- a/src/server/apis/google/GooglePhotosUploadUtils.ts
+++ b/src/server/apis/google/GooglePhotosUploadUtils.ts
@@ -1,28 +1,122 @@
import request = require('request-promise');
-import { Authorization } from './GooglePhotosServerUtils';
+import { GoogleApiServerUtils } from './GoogleApiServerUtils';
+import * as fs from 'fs';
+import { Utils } from '../../../Utils';
+import * as path from 'path';
+import { Opt } from '../../../new_fields/Doc';
export namespace GooglePhotosUploadUtils {
- interface UploadInformation {
- title: string;
- MEDIA_BINARY_DATA: string;
+ export interface Paths {
+ uploadDirectory: string;
+ credentialsPath: string;
+ tokenPath: string;
}
- const apiEndpoint = "https://photoslibrary.googleapis.com/v1/uploads";
+ export interface MediaInput {
+ description: string;
+ source: string;
+ }
+
+ export interface DownloadInformation {
+ mediaPath: string;
+ contentType?: string;
+ contentSize?: string;
+ }
+
+ const prepend = (extension: string) => `https://photoslibrary.googleapis.com/v1/${extension}`;
+ const headers = (type: string) => ({
+ 'Content-Type': `application/${type}`,
+ 'Authorization': Bearer,
+ });
+
+ let Bearer: string;
+ let Paths: Paths;
- export const SubmitUpload = async (parameters: Authorization & UploadInformation) => {
- let options = {
+ export const initialize = async (paths: Paths) => {
+ Paths = paths;
+ const { tokenPath, credentialsPath } = paths;
+ const token = await GoogleApiServerUtils.RetrieveAccessToken({ tokenPath, credentialsPath });
+ Bearer = `Bearer ${token}`;
+ };
+
+ export const DispatchGooglePhotosUpload = async (filename: string) => {
+ let body: Buffer;
+ if (filename.includes('upload_')) {
+ const mediaPath = Paths.uploadDirectory + filename;
+ body = await new Promise<Buffer>((resolve, reject) => {
+ fs.readFile(mediaPath, (error, data) => error ? reject(error) : resolve(data));
+ });
+ } else {
+ body = await request(filename, { encoding: null });
+ }
+ const parameters = {
+ method: 'POST',
headers: {
- 'Content-Type': 'application/octet-stream',
- Authorization: `Bearer ${parameters.token}`,
- 'X-Goog-Upload-File-Name': parameters.title,
+ ...headers('octet-stream'),
+ 'X-Goog-Upload-File-Name': filename,
'X-Goog-Upload-Protocol': 'raw'
},
- body: { MEDIA_BINARY_DATA: parameters.MEDIA_BINARY_DATA },
- json: true
+ uri: prepend('uploads'),
+ body
};
- const result = await request.post(apiEndpoint, options);
- return result;
+ return new Promise<any>(resolve => request(parameters, (error, _response, body) => resolve(error ? undefined : body)));
+ };
+
+ export const CreateMediaItems = (newMediaItems: any[], album?: { id: string }) => {
+ return new Promise<any>((resolve, reject) => {
+ const parameters = {
+ method: 'POST',
+ headers: headers('json'),
+ uri: prepend('mediaItems:batchCreate'),
+ body: { newMediaItems } as any,
+ json: true
+ };
+ album && (parameters.body.albumId = album.id);
+ request(parameters, (error, _response, body) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(body);
+ }
+ });
+ });
};
+ export namespace IOUtils {
+
+ export const Download = async (url: string): Promise<Opt<DownloadInformation>> => {
+ const filename = `temporary_upload_${Utils.GenerateGuid()}${path.extname(url).toLowerCase()}`;
+ const temporaryDirectory = Paths.uploadDirectory + "temporary/";
+ const mediaPath = temporaryDirectory + filename;
+
+ if (!(await createIfNotExists(temporaryDirectory))) {
+ return undefined;
+ }
+
+ return new Promise<DownloadInformation>((resolve, reject) => {
+ request.head(url, (error, res) => {
+ if (error) {
+ return reject(error);
+ }
+ const information: DownloadInformation = {
+ mediaPath,
+ contentType: res.headers['content-type'],
+ contentSize: res.headers['content-length'],
+ };
+ request(url).pipe(fs.createWriteStream(mediaPath)).on('close', () => resolve(information));
+ });
+ });
+ };
+
+ export const createIfNotExists = async (path: string) => {
+ if (await new Promise<boolean>(resolve => fs.exists(path, resolve))) {
+ return true;
+ }
+ return new Promise<boolean>(resolve => fs.mkdir(path, error => resolve(error === null)));
+ };
+
+ export const Destroy = (mediaPath: string) => new Promise<boolean>(resolve => fs.unlink(mediaPath, error => resolve(error === null)));
+ }
+
} \ No newline at end of file
diff --git a/src/server/apis/google/typings/albums.ts b/src/server/apis/google/typings/albums.ts
deleted file mode 100644
index f3025567d..000000000
--- a/src/server/apis/google/typings/albums.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-export namespace Album {
-
- export type Query = (AddEnrichment | BatchAddMediaItems | BatchRemoveMediaItems | Create | Get | List | Share | Unshare);
-
- export enum Action {
- AddEnrichment = "addEnrichment",
- BatchAddMediaItems = "batchAddMediaItems",
- BatchRemoveMediaItems = "batchRemoveMediaItems",
- Create = "create",
- Get = "get",
- List = "list",
- Share = "share",
- Unshare = "unshare"
- }
-
- export interface AddEnrichment {
- action: Action.AddEnrichment;
- albumId: string;
- body: {
- newEnrichmentItem: NewEnrichmentItem;
- albumPosition: MediaRelativeAlbumPosition;
- };
- }
-
- export interface BatchAddMediaItems {
- action: Action.BatchAddMediaItems;
- albumId: string;
- body: {
- mediaItemIds: string[];
- };
- }
-
- export interface BatchRemoveMediaItems {
- action: Action.BatchRemoveMediaItems;
- albumId: string;
- body: {
- mediaItemIds: string[];
- };
- }
-
- export interface Create {
- action: Action.Create;
- body: {
- album: Template;
- };
- }
-
- export interface Get {
- action: Action.Get;
- albumId: string;
- }
-
- export interface List {
- action: Action.List;
- parameters: ListOptions;
- }
-
- export interface ListOptions {
- pageSize: number;
- pageToken: string;
- excludeNonAppCreatedData: boolean;
- }
-
- export interface Share {
- action: Action.Share;
- albumId: string;
- body: {
- sharedAlbumOptions: SharedOptions;
- };
- }
-
- export interface Unshare {
- action: Action.Unshare;
- albumId: string;
- }
-
- export interface Template {
- title: string;
- }
-
- export interface Model {
- id: string;
- title: string;
- productUrl: string;
- isWriteable: boolean;
- shareInfo: ShareInfo;
- mediaItemsCount: string;
- coverPhotoBaseUrl: string;
- coverPhotoMediaItemId: string;
- }
-
- export interface ShareInfo {
- sharedAlbumOptions: SharedOptions;
- shareableUrl: string;
- shareToken: string;
- isJoined: boolean;
- isOwned: boolean;
- }
-
- export interface SharedOptions {
- isCollaborative: boolean;
- isCommentable: boolean;
- }
-
- export enum PositionType {
- POSITION_TYPE_UNSPECIFIED,
- FIRST_IN_ALBUM,
- LAST_IN_ALBUM,
- AFTER_MEDIA_ITEM,
- AFTER_ENRICHMENT_ITEM
- }
-
- export type Position = GeneralAlbumPosition | MediaRelativeAlbumPosition | EnrichmentRelativeAlbumPosition;
-
- interface GeneralAlbumPosition {
- position: PositionType.FIRST_IN_ALBUM | PositionType.LAST_IN_ALBUM | PositionType.POSITION_TYPE_UNSPECIFIED;
- }
-
- interface MediaRelativeAlbumPosition {
- position: PositionType.AFTER_MEDIA_ITEM;
- relativeMediaItemId: string;
- }
-
- interface EnrichmentRelativeAlbumPosition {
- position: PositionType.AFTER_ENRICHMENT_ITEM;
- relativeEnrichmentItemId: string;
- }
-
- export interface Location {
- locationName: string;
- latlng: {
- latitude: number,
- longitude: number
- };
- }
-
- export interface NewEnrichmentItem {
- textEnrichment: {
- text: string;
- };
- locationEnrichment: {
- location: Location
- };
- mapEnrichment: {
- origin: { location: Location },
- destination: { location: Location }
- };
- }
-
-} \ No newline at end of file
diff --git a/src/server/credentials/google_docs_token.json b/src/server/credentials/google_docs_token.json
index 2ac972ed8..f3c8cf82a 100644
--- a/src/server/credentials/google_docs_token.json
+++ b/src/server/credentials/google_docs_token.json
@@ -1 +1 @@
-{"access_token":"ya29.Glt5ByifP30HMz6a1fEG77qZlz9fBEOCz4PQ1VA8t_Ck2ZTPJKoyr6xc3-GFZISAqrrw2U8XpMyZv02_URfPwUX0Z_tMdlIFqsygowR-uClukbgQPNtgxp2LS1oW","refresh_token":"1/wK1cUVLnt581ba_pYGPdlTvAa-OS64nB5m5XOXEBJ8Q","scope":"https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/photoslibrary.sharing https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/photoslibrary","token_type":"Bearer","expiry_date":1567556163894} \ No newline at end of file
+{"access_token":"ya29.Glx7B9S6zCKDE0EgYk9xX9-RhcN8j4IwG9ONopTl1NkPX9FUOw0GI_81mY9bhaouuyOTnrc6FrZD5SDHolWwp3ABNT6l7TmhTLDILgGXIixZkWFRBPpF-xHC8lUd8A","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1567765465073} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 3940bbd58..fab00a02d 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -42,7 +42,6 @@ var AdmZip = require('adm-zip');
import * as YoutubeApi from "./apis/youtube/youtubeApiSample";
import { Response } from 'express-serve-static-core';
import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
-import { GooglePhotos } from './apis/google/GooglePhotosServerUtils';
import { GooglePhotosUploadUtils } from './apis/google/GooglePhotosUploadUtils';
const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
@@ -418,10 +417,10 @@ app.get("/thumbnail/:filename", (req, res) => {
let filename = req.params.filename;
let noExt = filename.substring(0, filename.length - ".png".length);
let pagenumber = parseInt(noExt.split('-')[1]);
- fs.exists(uploadDir + filename, (exists: boolean) => {
- console.log(`${uploadDir + filename} ${exists ? "exists" : "does not exist"}`);
+ fs.exists(uploadDirectory + filename, (exists: boolean) => {
+ console.log(`${uploadDirectory + filename} ${exists ? "exists" : "does not exist"}`);
if (exists) {
- let input = fs.createReadStream(uploadDir + filename);
+ let input = fs.createReadStream(uploadDirectory + filename);
probe(input, (err: any, result: any) => {
if (err) {
console.log(err);
@@ -432,7 +431,7 @@ app.get("/thumbnail/:filename", (req, res) => {
});
}
else {
- LoadPage(uploadDir + filename.substring(0, filename.length - noExt.split('-')[1].length - ".PNG".length - 1) + ".pdf", pagenumber, res);
+ LoadPage(uploadDirectory + filename.substring(0, filename.length - noExt.split('-')[1].length - ".PNG".length - 1) + ".pdf", pagenumber, res);
}
});
});
@@ -556,13 +555,13 @@ class NodeCanvasFactory {
const pngTypes = [".png", ".PNG"];
const pdfTypes = [".pdf", ".PDF"];
const jpgTypes = [".jpg", ".JPG", ".jpeg", ".JPEG"];
-const uploadDir = __dirname + "/public/files/";
+const uploadDirectory = __dirname + "/public/files/";
// SETTERS
app.post(
RouteStore.upload,
(req, res) => {
let form = new formidable.IncomingForm();
- form.uploadDir = uploadDir;
+ form.uploadDir = uploadDirectory;
form.keepExtensions = true;
// let path = req.body.path;
console.log("upload");
@@ -592,7 +591,7 @@ app.post(
}
if (isImage) {
resizers.forEach(resizer => {
- fs.createReadStream(uploadDir + file).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDir + file.substring(0, file.length - ext.length) + resizer.suffix + ext));
+ fs.createReadStream(uploadDirectory + file).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDirectory + file.substring(0, file.length - ext.length) + resizer.suffix + ext));
});
}
names.push(`/files/` + file);
@@ -611,7 +610,7 @@ addSecureRoute(
res.status(401).send("incorrect parameters specified");
return;
}
- imageDataUri.outputFile(uri, uploadDir + filename).then((savedName: string) => {
+ imageDataUri.outputFile(uri, uploadDirectory + filename).then((savedName: string) => {
const ext = path.extname(savedName);
let resizers = [
{ resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" },
@@ -632,7 +631,7 @@ addSecureRoute(
}
if (isImage) {
resizers.forEach(resizer => {
- fs.createReadStream(savedName).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDir + filename + resizer.suffix + ext));
+ fs.createReadStream(savedName).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDirectory + filename + resizer.suffix + ext));
});
}
res.send("/files/" + filename + ext);
@@ -799,8 +798,8 @@ function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any
}
}
-const credentials = path.join(__dirname, "./credentials/google_docs_credentials.json");
-const token = path.join(__dirname, "./credentials/google_docs_token.json");
+const credentialsPath = path.join(__dirname, "./credentials/google_docs_credentials.json");
+const tokenPath = path.join(__dirname, "./credentials/google_docs_token.json");
const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerUtils.ApiRouter>([
["create", (api, params) => api.create(params)],
@@ -811,7 +810,7 @@ const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerU
app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
let sector: any = req.params.sector;
let action: any = req.params.action;
- GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentials, token }).then(endpoint => {
+ GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentialsPath, tokenPath }).then(endpoint => {
let handler = EndpointHandlerMap.get(action);
if (endpoint && handler) {
let execute = handler(endpoint, req.body).then(
@@ -825,36 +824,28 @@ app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
});
});
-app.post(RouteStore.googlePhotosQuery, (req, res) => {
- GoogleApiServerUtils.RetrieveAccessToken({ credentials, token }).then(
- token => {
- GooglePhotos.ExecuteQuery({ token, query: req.body })
- .then(response => {
- if (response === undefined) {
- res.send("Error: unable to build suffix for Google Photos API request");
- return;
- }
- res.send(response);
- })
- .catch(error => {
- res.send(`Error: an exception occurred in the execution of this Google Photos API request\n${error}`);
- });
- },
- error => res.send(error)
- );
-});
+app.get(RouteStore.googlePhotosAccessToken, (req, res) => GoogleApiServerUtils.RetrieveAccessToken({ credentialsPath, tokenPath }).then(token => res.send(token)));
-app.post(RouteStore.googlePhotosMediaUpload, (req, res) => {
- GoogleApiServerUtils.RetrieveAccessToken({ credentials, token }).then(
- token => {
- GooglePhotosUploadUtils.SubmitUpload({ token, ...req.body })
- .then(response => {
- res.send(response);
- }).catch(error => {
- res.send(`Error: an exception occurred in uploading the given media\n${error}`);
- });
- },
- error => res.send(error));
+const tokenError = "Unable to successfully upload bytes for all images!";
+const mediaError = "Unable to convert all uploaded bytes to media items!";
+
+app.post(RouteStore.googlePhotosMediaUpload, async (req, res) => {
+ const media: GooglePhotosUploadUtils.MediaInput[] = req.body.media;
+ await GooglePhotosUploadUtils.initialize({ uploadDirectory, credentialsPath, tokenPath });
+ const newMediaItems = await Promise.all(media.map(async element => {
+ const uploadToken = await GooglePhotosUploadUtils.DispatchGooglePhotosUpload(element.source);
+ return !uploadToken ? undefined : {
+ description: element.description,
+ simpleMediaItem: { uploadToken }
+ };
+ }));
+ if (!newMediaItems.every(item => item)) {
+ return res.send(tokenError);
+ }
+ GooglePhotosUploadUtils.CreateMediaItems(newMediaItems, req.body.album).then(
+ success => res.send(success),
+ () => res.send(mediaError)
+ );
});
const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {