225 lines
7.2 KiB
JavaScript
225 lines
7.2 KiB
JavaScript
import utils from '../utils';
|
|
import store from '../../store';
|
|
|
|
const clientId = '241271498917-t4t7d07qis7oc0ahaskbif3ft6tk63cd.apps.googleusercontent.com';
|
|
const appsDomain = null;
|
|
const tokenExpirationMargin = 10 * 60 * 1000; // 10 min
|
|
|
|
// const scopeMap = {
|
|
// profile: [
|
|
// 'https://www.googleapis.com/auth/userinfo.profile',
|
|
// ],
|
|
// gdrive: [
|
|
// 'https://www.googleapis.com/auth/drive.install',
|
|
// store.getters['data/settings'].gdriveFullAccess === true ?
|
|
// 'https://www.googleapis.com/auth/drive' :
|
|
// 'https://www.googleapis.com/auth/drive.file',
|
|
// ],
|
|
// blogger: [
|
|
// 'https://www.googleapis.com/auth/blogger',
|
|
// ],
|
|
// picasa: [
|
|
// 'https://www.googleapis.com/auth/photos',
|
|
// ],
|
|
// };
|
|
|
|
const request = (googleToken, options) => utils.request({
|
|
...options,
|
|
headers: {
|
|
...options.headers,
|
|
Authorization: `Bearer ${googleToken.accessToken}`,
|
|
},
|
|
});
|
|
|
|
const saveFile = (googleToken, data, appData) => {
|
|
const options = {
|
|
method: 'POST',
|
|
url: 'https://www.googleapis.com/upload/drive/v2/files',
|
|
headers: {},
|
|
};
|
|
if (appData) {
|
|
options.method = 'PUT';
|
|
options.url = `https://www.googleapis.com/drive/v2/files/${appData.id}`;
|
|
options.headers['if-match'] = appData.etag;
|
|
}
|
|
const metadata = {
|
|
title: data.name,
|
|
parents: [{
|
|
id: 'appDataFolder',
|
|
}],
|
|
properties: Object.keys(data)
|
|
.filter(key => key !== 'name' && key !== 'tx')
|
|
.map(key => ({
|
|
key,
|
|
value: JSON.stringify(data[key]),
|
|
visibility: 'PUBLIC',
|
|
})),
|
|
};
|
|
const media = null;
|
|
const boundary = `-------${utils.uid()}`;
|
|
const delimiter = `\r\n--${boundary}\r\n`;
|
|
const closeDelimiter = `\r\n--${boundary}--`;
|
|
if (media) {
|
|
let multipartRequestBody = '';
|
|
multipartRequestBody += delimiter;
|
|
multipartRequestBody += 'Content-Type: application/json\r\n\r\n';
|
|
multipartRequestBody += JSON.stringify(metadata);
|
|
multipartRequestBody += delimiter;
|
|
multipartRequestBody += 'Content-Type: application/json\r\n\r\n';
|
|
multipartRequestBody += JSON.stringify(media);
|
|
multipartRequestBody += closeDelimiter;
|
|
return request(googleToken, {
|
|
...options,
|
|
params: {
|
|
uploadType: 'multipart',
|
|
},
|
|
headers: {
|
|
...options.headers,
|
|
'Content-Type': `multipart/mixed; boundary="${boundary}"`,
|
|
},
|
|
body: multipartRequestBody,
|
|
});
|
|
}
|
|
return request(googleToken, {
|
|
...options,
|
|
body: metadata,
|
|
}).then(res => ({
|
|
id: res.body.id,
|
|
etag: res.body.etag,
|
|
}));
|
|
};
|
|
|
|
export default {
|
|
startOauth2(scopes, sub = null, silent = false) {
|
|
return utils.startOauth2(
|
|
'https://accounts.google.com/o/oauth2/v2/auth', {
|
|
client_id: clientId,
|
|
response_type: 'token',
|
|
scope: scopes.join(' '),
|
|
hd: appsDomain,
|
|
login_hint: sub,
|
|
prompt: silent ? 'none' : null,
|
|
}, silent)
|
|
// Call the tokeninfo endpoint
|
|
.then(data => utils.request({
|
|
method: 'POST',
|
|
url: 'https://www.googleapis.com/oauth2/v3/tokeninfo',
|
|
params: {
|
|
access_token: data.accessToken,
|
|
},
|
|
}).then((res) => {
|
|
// Check the returned client ID consistency
|
|
if (res.body.aud !== clientId) {
|
|
throw new Error('Client ID inconsistent.');
|
|
}
|
|
// Check the returned sub consistency
|
|
if (sub && res.body.sub !== sub) {
|
|
throw new Error('Google account ID not expected.');
|
|
}
|
|
// Build token object including scopes and sub
|
|
return {
|
|
scopes,
|
|
accessToken: data.accessToken,
|
|
expiresOn: Date.now() + (data.expiresIn * 1000),
|
|
sub: res.body.sub,
|
|
isLogin: !store.getters['data/loginToken'],
|
|
};
|
|
}))
|
|
// Call the tokeninfo endpoint
|
|
.then(googleToken => request(googleToken, {
|
|
method: 'GET',
|
|
url: 'https://www.googleapis.com/plus/v1/people/me',
|
|
}).then((res) => {
|
|
// Add name to googleToken
|
|
googleToken.name = res.body.displayName;
|
|
const existingToken = store.getters['data/googleTokens'][googleToken.sub];
|
|
if (existingToken) {
|
|
if (!sub) {
|
|
throw new Error('Google account already linked.');
|
|
}
|
|
// Add isLogin and lastChangeId to googleToken
|
|
googleToken.isLogin = existingToken.isLogin;
|
|
googleToken.lastChangeId = existingToken.lastChangeId;
|
|
}
|
|
// Add googleToken to googleTokens
|
|
store.dispatch('data/setGoogleToken', googleToken);
|
|
return googleToken;
|
|
}));
|
|
},
|
|
refreshToken(scopes, googleToken) {
|
|
const sub = googleToken.sub;
|
|
const lastToken = store.getters['data/googleTokens'][sub];
|
|
const mergedScopes = [...new Set([
|
|
...scopes,
|
|
...lastToken.scopes,
|
|
])];
|
|
|
|
return Promise.resolve()
|
|
.then(() => {
|
|
if (mergedScopes.length === lastToken.scopes.length) {
|
|
return lastToken;
|
|
}
|
|
// New scopes are requested, popup an authorize window
|
|
return this.startOauth2(mergedScopes, sub);
|
|
})
|
|
.then((refreshedToken) => {
|
|
if (refreshedToken.expiresOn > Date.now() + tokenExpirationMargin) {
|
|
// Token is fresh enough
|
|
return refreshedToken;
|
|
}
|
|
// Token is almost outdated, try to take one in background
|
|
return this.startOauth2(mergedScopes, sub, true)
|
|
// If it fails try to popup a window
|
|
.catch(() => this.startOauth2(mergedScopes, sub));
|
|
});
|
|
},
|
|
getChanges(googleToken) {
|
|
let changes = [];
|
|
return this.refreshToken(['https://www.googleapis.com/auth/drive.appdata'], googleToken)
|
|
.then((refreshedToken) => {
|
|
const lastChangeId = refreshedToken.lastChangeId || 0;
|
|
const getPage = pageToken => request(refreshedToken, {
|
|
method: 'GET',
|
|
url: 'https://www.googleapis.com/drive/v2/changes',
|
|
params: {
|
|
pageToken,
|
|
startChangeId: pageToken || !lastChangeId ? null : lastChangeId + 1,
|
|
spaces: 'appDataFolder',
|
|
fields: 'nextPageToken,items(deleted,file/id,file/etag,file/title,file/properties(key,value))',
|
|
},
|
|
}).then((res) => {
|
|
changes = changes.concat(res.body.items);
|
|
if (res.body.nextPageToken) {
|
|
return getPage(res.body.nextPageToken);
|
|
}
|
|
return changes;
|
|
});
|
|
|
|
return getPage();
|
|
});
|
|
},
|
|
updateLastChangeId(googleToken, changes) {
|
|
const refreshedToken = store.getters['data/googleTokens'][googleToken.sub];
|
|
let lastChangeId = refreshedToken.lastChangeId || 0;
|
|
changes.forEach((change) => {
|
|
if (change.id > lastChangeId) {
|
|
lastChangeId = change.id;
|
|
}
|
|
});
|
|
if (lastChangeId !== refreshedToken.lastChangeId) {
|
|
store.dispatch('data/setGoogleToken', {
|
|
...refreshedToken,
|
|
lastChangeId,
|
|
});
|
|
}
|
|
},
|
|
insertData(googleToken, data) {
|
|
return this.refreshToken(['https://www.googleapis.com/auth/drive.appdata'], googleToken)
|
|
.then(refreshedToken => saveFile(refreshedToken, data));
|
|
},
|
|
updateData(googleToken, data, appData) {
|
|
return this.refreshToken(['https://www.googleapis.com/auth/drive.appdata'], googleToken)
|
|
.then(refreshedToken => saveFile(refreshedToken, data, appData));
|
|
},
|
|
};
|