-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[v4.x backport] @uppy/google-photos: add plugin #5265
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Backport of #5061
Diff output filesdiff --git a/packages/@uppy/companion/lib/config/grant.d.ts b/packages/@uppy/companion/lib/config/grant.d.ts
index f7df73c..97c46b1 100644
--- a/packages/@uppy/companion/lib/config/grant.d.ts
+++ b/packages/@uppy/companion/lib/config/grant.d.ts
@@ -1,12 +1,29 @@
declare function _exports(): {
- google: {
- transport: string;
+ googledrive: {
+ callback: string;
scope: string[];
+ transport: string;
+ custom_params: {
+ access_type: string;
+ prompt: string;
+ };
+ authorize_url: string;
+ access_url: string;
+ oauth: number;
+ scope_delimiter: string;
+ };
+ googlephotos: {
callback: string;
+ scope: string[];
+ transport: string;
custom_params: {
access_type: string;
prompt: string;
};
+ authorize_url: string;
+ access_url: string;
+ oauth: number;
+ scope_delimiter: string;
};
dropbox: {
transport: string;
diff --git a/packages/@uppy/companion/lib/config/grant.js b/packages/@uppy/companion/lib/config/grant.js
index 861adf7..6f113e3 100644
--- a/packages/@uppy/companion/lib/config/grant.js
+++ b/packages/@uppy/companion/lib/config/grant.js
@@ -1,21 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
+const google = {
+ transport: "session",
+ // access_type: offline is needed in order to get refresh tokens.
+ // prompt: 'consent' is needed because sometimes a user will get stuck in an authenticated state where we will
+ // receive no refresh tokens from them. This seems to be happen when running on different subdomains.
+ // therefore to be safe that we always get refresh tokens, we set this.
+ // https://stackoverflow.com/questions/10827920/not-receiving-google-oauth-refresh-token/65108513#65108513
+ custom_params: { access_type: "offline", prompt: "consent" },
+ // copied from https://github.com/simov/grant/blob/master/config/oauth.json
+ "authorize_url": "https://accounts.google.com/o/oauth2/v2/auth",
+ "access_url": "https://oauth2.googleapis.com/token",
+ "oauth": 2,
+ "scope_delimiter": " ",
+};
// oauth configuration for provider services that are used.
module.exports = () => {
return {
- // for drive
- google: {
- transport: "session",
- scope: [
- "https://www.googleapis.com/auth/drive.readonly",
- ],
+ // we need separate auth providers because scopes are different,
+ // and because it would be a too big rewrite to allow reuse of the same provider.
+ googledrive: {
+ ...google,
callback: "/drive/callback",
- // access_type: offline is needed in order to get refresh tokens.
- // prompt: 'consent' is needed because sometimes a user will get stuck in an authenticated state where we will
- // receive no refresh tokens from them. This seems to be happen when running on different subdomains.
- // therefore to be safe that we always get refresh tokens, we set this.
- // https://stackoverflow.com/questions/10827920/not-receiving-google-oauth-refresh-token/65108513#65108513
- custom_params: { access_type: "offline", prompt: "consent" },
+ scope: ["https://www.googleapis.com/auth/drive.readonly"],
+ },
+ googlephotos: {
+ ...google,
+ callback: "/googlephotos/callback",
+ scope: [
+ "https://www.googleapis.com/auth/photoslibrary.readonly",
+ "https://www.googleapis.com/auth/userinfo.email",
+ ], // if name is needed, then add https://www.googleapis.com/auth/userinfo.profile too
},
dropbox: {
transport: "session",
diff --git a/packages/@uppy/companion/lib/server/controllers/get.js b/packages/@uppy/companion/lib/server/controllers/get.js
index c3b5129..44c2d07 100644
--- a/packages/@uppy/companion/lib/server/controllers/get.js
+++ b/packages/@uppy/companion/lib/server/controllers/get.js
@@ -10,10 +10,7 @@ async function get(req, res) {
async function getSize() {
return provider.size({ id, token: accessToken, query: req.query });
}
- async function download() {
- const { stream } = await provider.download({ id, token: accessToken, providerUserSession, query: req.query });
- return stream;
- }
+ const download = () => provider.download({ id, token: accessToken, providerUserSession, query: req.query });
try {
await startDownUpload({ req, res, getSize, download });
} catch (err) {
diff --git a/packages/@uppy/companion/lib/server/controllers/url.js b/packages/@uppy/companion/lib/server/controllers/url.js
index 67a67aa..3e0eff3 100644
--- a/packages/@uppy/companion/lib/server/controllers/url.js
+++ b/packages/@uppy/companion/lib/server/controllers/url.js
@@ -24,8 +24,8 @@ const downloadURL = async (url, allowLocalIPs, traceId) => {
try {
const protectedGot = await getProtectedGot({ allowLocalIPs });
const stream = protectedGot.stream.get(url, { responseType: "json" });
- await prepareStream(stream);
- return stream;
+ const { size } = await prepareStream(stream);
+ return { stream, size };
} catch (err) {
logger.error(err, "controller.url.download.error", traceId);
throw err;
@@ -71,9 +71,7 @@ const get = async (req, res) => {
const { size } = await getURLMeta(req.body.url, allowLocalUrls);
return size;
}
- async function download() {
- return downloadURL(req.body.url, allowLocalUrls, req.id);
- }
+ const download = () => downloadURL(req.body.url, allowLocalUrls, req.id);
try {
await startDownUpload({ req, res, getSize, download });
} catch (err) {
diff --git a/packages/@uppy/companion/lib/server/helpers/oauth-state.d.ts b/packages/@uppy/companion/lib/server/helpers/oauth-state.d.ts
index f1c2709..f2b204d 100644
--- a/packages/@uppy/companion/lib/server/helpers/oauth-state.d.ts
+++ b/packages/@uppy/companion/lib/server/helpers/oauth-state.d.ts
@@ -1,4 +1,5 @@
export function encodeState(state: any, secret: any): string;
+export function decodeState(state: any, secret: any): any;
export function generateState(): {
id: string;
};
diff --git a/packages/@uppy/companion/lib/server/helpers/oauth-state.js b/packages/@uppy/companion/lib/server/helpers/oauth-state.js
index e7704f4..ebcae43 100644
--- a/packages/@uppy/companion/lib/server/helpers/oauth-state.js
+++ b/packages/@uppy/companion/lib/server/helpers/oauth-state.js
@@ -6,7 +6,7 @@ module.exports.encodeState = (state, secret) => {
const encodedState = Buffer.from(JSON.stringify(state)).toString("base64");
return encrypt(encodedState, secret);
};
-const decodeState = (state, secret) => {
+module.exports.decodeState = (state, secret) => {
const encodedState = decrypt(state, secret);
return JSON.parse(atob(encodedState));
};
@@ -16,7 +16,7 @@ module.exports.generateState = () => {
};
};
module.exports.getFromState = (state, name, secret) => {
- return decodeState(state, secret)[name];
+ return module.exports.decodeState(state, secret)[name];
};
module.exports.getGrantDynamicFromRequest = (req) => {
return req.session.grant?.dynamic ?? {};
diff --git a/packages/@uppy/companion/lib/server/helpers/upload.js b/packages/@uppy/companion/lib/server/helpers/upload.js
index 92991a8..4d60174 100644
--- a/packages/@uppy/companion/lib/server/helpers/upload.js
+++ b/packages/@uppy/companion/lib/server/helpers/upload.js
@@ -5,12 +5,20 @@ const logger = require("../logger");
const { respondWithError } = require("../provider/error");
async function startDownUpload({ req, res, getSize, download }) {
try {
- const size = await getSize();
+ logger.debug("Starting download stream.", null, req.id);
+ const { stream, size: maybeSize } = await download();
+ let size;
+ // if the provider already knows the size, we can use that
+ if (typeof maybeSize === "number" && !Number.isNaN(maybeSize) && maybeSize > 0) {
+ size = maybeSize;
+ }
+ // if not we need to get the size
+ if (size == null) {
+ size = await getSize();
+ }
const { clientSocketConnectTimeout } = req.companion.options;
logger.debug("Instantiating uploader.", null, req.id);
const uploader = new Uploader(Uploader.reqToOptions(req, size));
- logger.debug("Starting download stream.", null, req.id);
- const stream = await download();
(async () => {
// wait till the client has connected to the socket, before starting
// the download, so that the client can receive all download/upload progress.
diff --git a/packages/@uppy/companion/lib/server/helpers/utils.js b/packages/@uppy/companion/lib/server/helpers/utils.js
index b998ab3..5985bbb 100644
--- a/packages/@uppy/companion/lib/server/helpers/utils.js
+++ b/packages/@uppy/companion/lib/server/helpers/utils.js
@@ -142,11 +142,14 @@ module.exports.StreamHttpJsonError = StreamHttpJsonError;
module.exports.prepareStream = async (stream) =>
new Promise((resolve, reject) => {
stream
- .on("response", () => {
+ .on("response", (response) => {
+ const contentLengthStr = response.headers["content-length"];
+ const contentLength = parseInt(contentLengthStr, 10);
+ const size = !Number.isNaN(contentLength) && contentLength >= 0 ? contentLength : undefined;
// Don't allow any more data to flow yet.
// https://github.com/request/request/issues/1990#issuecomment-184712275
stream.pause();
- resolve();
+ resolve({ size });
})
.on("error", (err) => {
// In this case the error object is not a normal GOT HTTPError where json is already parsed,
diff --git a/packages/@uppy/companion/lib/server/provider/google/drive/index.d.ts b/packages/@uppy/companion/lib/server/provider/google/drive/index.d.ts
index 753b95d..a13c57b 100644
--- a/packages/@uppy/companion/lib/server/provider/google/drive/index.d.ts
+++ b/packages/@uppy/companion/lib/server/provider/google/drive/index.d.ts
@@ -3,7 +3,6 @@ export = Drive;
* Adapter for API https://developers.google.com/drive/api/v3/
*/
declare class Drive extends Provider {
- static get authProvider(): string;
list(options: any): Promise<any>;
download({ id: idIn, token }: {
id: any;
@@ -13,8 +12,9 @@ declare class Drive extends Provider {
id: any;
token: any;
}): Promise<any>;
- logout: (...args: any[]) => Promise<any>;
+ logout: typeof logout;
refreshToken: typeof refreshToken;
}
import Provider = require("../../Provider");
+import { logout } from "../index";
import { refreshToken } from "../index";
diff --git a/packages/@uppy/companion/lib/server/provider/google/drive/index.js b/packages/@uppy/companion/lib/server/provider/google/drive/index.js
index 242631d..e5bcbf2 100644
--- a/packages/@uppy/companion/lib/server/provider/google/drive/index.js
+++ b/packages/@uppy/companion/lib/server/provider/google/drive/index.js
@@ -47,7 +47,7 @@ async function getStats({ id, token }) {
* Adapter for API https://developers.google.com/drive/api/v3/
*/
class Drive extends Provider {
- static get authProvider() {
+ static get oauthProvider() {
return "googledrive";
}
static get authStateExpiry() {
@@ -55,7 +55,7 @@ class Drive extends Provider {
}
// eslint-disable-next-line class-methods-use-this
async list(options) {
- return withGoogleErrorHandling(Drive.authProvider, "provider.drive.list.error", async () => {
+ return withGoogleErrorHandling(Drive.oauthProvider, "provider.drive.list.error", async () => {
const directory = options.directory || "root";
const query = options.query || {};
const { token } = options;
@@ -123,7 +123,7 @@ class Drive extends Provider {
throw new ProviderAuthError();
}
}
- return withGoogleErrorHandling(Drive.authProvider, "provider.drive.download.error", async () => {
+ return withGoogleErrorHandling(Drive.oauthProvider, "provider.drive.download.error", async () => {
const client = await getClient({ token });
const { mimeType, id, exportLinks } = await getStats({ id: idIn, token });
let stream;
@@ -160,7 +160,7 @@ class Drive extends Provider {
}
// eslint-disable-next-line class-methods-use-this
async size({ id, token }) {
- return withGoogleErrorHandling(Drive.authProvider, "provider.drive.size.error", async () => {
+ return withGoogleErrorHandling(Drive.oauthProvider, "provider.drive.size.error", async () => {
const { mimeType, size } = await getStats({ id, token });
if (isGsuiteFile(mimeType)) {
// GSuite file sizes cannot be predetermined (but are max 10MB)
@@ -170,10 +170,6 @@ class Drive extends Provider {
return parseInt(size, 10);
});
}
- // eslint-disable-next-line class-methods-use-this
- async logout(...args) {
- return logout(...args);
- }
}
Drive.prototype.logout = logout;
Drive.prototype.refreshToken = refreshToken;
diff --git a/packages/@uppy/companion/lib/server/provider/index.js b/packages/@uppy/companion/lib/server/provider/index.js
index 3be93fc..0c8e112 100644
--- a/packages/@uppy/companion/lib/server/provider/index.js
+++ b/packages/@uppy/companion/lib/server/provider/index.js
@@ -5,7 +5,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
*/
const dropbox = require("./dropbox");
const box = require("./box");
-const drive = require("./drive");
+const drive = require("./google/drive");
+const googlephotos = require("./google/googlephotos");
const instagram = require("./instagram/graph");
const facebook = require("./facebook");
const onedrive = require("./onedrive");
@@ -63,7 +64,7 @@ module.exports.getProviderMiddleware = (providers, grantConfig) => {
* @returns {Record<string, typeof Provider>}
*/
module.exports.getDefaultProviders = () => {
- const providers = { dropbox, box, drive, facebook, onedrive, zoom, instagram, unsplash };
+ const providers = { dropbox, box, drive, googlephotos, facebook, onedrive, zoom, instagram, unsplash };
return providers;
};
/**
diff --git a/packages/@uppy/companion/lib/standalone/helper.js b/packages/@uppy/companion/lib/standalone/helper.js
index 0da1ebf..9bce6ea 100644
--- a/packages/@uppy/companion/lib/standalone/helper.js
+++ b/packages/@uppy/companion/lib/standalone/helper.js
@@ -72,6 +72,11 @@ const getConfigFromEnv = () => {
secret: getSecret("COMPANION_GOOGLE_SECRET"),
credentialsURL: process.env.COMPANION_GOOGLE_KEYS_ENDPOINT,
},
+ googlephotos: {
+ key: process.env.COMPANION_GOOGLE_KEY,
+ secret: getSecret("COMPANION_GOOGLE_SECRET"),
+ credentialsURL: process.env.COMPANION_GOOGLE_KEYS_ENDPOINT,
+ },
dropbox: {
key: process.env.COMPANION_DROPBOX_KEY,
secret: getSecret("COMPANION_DROPBOX_SECRET"),
diff --git a/packages/@uppy/google-photos/lib/GooglePhotos.js b/packages/@uppy/google-photos/lib/GooglePhotos.js
index 2292e1a..51bc8c5 100644
--- a/packages/@uppy/google-photos/lib/GooglePhotos.js
+++ b/packages/@uppy/google-photos/lib/GooglePhotos.js
@@ -9,6 +9,7 @@ import locale from "./locale.js";
export default class GooglePhotos extends UIPlugin {
constructor(uppy, opts) {
super(uppy, opts);
+ this.rootFolderId = null;
this.type = "acquirer";
this.storage = this.opts.storage || tokenStorage;
this.files = [];
@@ -69,7 +70,6 @@ export default class GooglePhotos extends UIPlugin {
this.defaultLocale = locale;
this.i18nInit();
this.title = this.i18n("pluginNameGooglePhotos");
- this.onFirstRender = this.onFirstRender.bind(this);
this.render = this.render.bind(this);
}
install() {
@@ -88,9 +88,6 @@ export default class GooglePhotos extends UIPlugin {
this.view.tearDown();
this.unmount();
}
- async onFirstRender() {
- await Promise.all([this.provider.fetchPreAuthToken(), this.view.getFolder()]);
- }
render(state) {
if (this.getPluginState().files.length && !this.getPluginState().folders.length) {
return this.view.render(state, { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Backport of #5061