onedrive.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Reference: https://dev.onedrive.com/README.htm
  2. import { dumpQuery, noop } from '@/common';
  3. import { FORM_URLENCODED } from '@/common/consts';
  4. import { objectGet } from '@/common/object';
  5. import {
  6. getURI, getItemFilename, BaseService, isScriptFile, register,
  7. openAuthPage,
  8. } from './base';
  9. const config = {
  10. client_id: process.env.SYNC_ONEDRIVE_CLIENT_ID,
  11. client_secret: process.env.SYNC_ONEDRIVE_CLIENT_SECRET,
  12. redirect_uri: 'https://violentmonkey.github.io/auth_onedrive.html',
  13. };
  14. const OneDrive = BaseService.extend({
  15. name: 'onedrive',
  16. displayName: 'OneDrive',
  17. urlPrefix: 'https://api.onedrive.com/v1.0',
  18. refreshToken() {
  19. const refreshToken = this.config.get('refresh_token');
  20. return this.authorized({
  21. refresh_token: refreshToken,
  22. grant_type: 'refresh_token',
  23. })
  24. .then(() => this.prepare());
  25. },
  26. user() {
  27. const requestUser = () => this.loadData({
  28. url: '/drive',
  29. responseType: 'json',
  30. });
  31. return requestUser()
  32. .catch((res) => {
  33. if (res.status === 401) {
  34. return this.refreshToken().then(requestUser);
  35. }
  36. throw res;
  37. })
  38. .catch((res) => {
  39. if (res.status === 400 && objectGet(res, 'data.error') === 'invalid_grant') {
  40. return Promise.reject({
  41. type: 'unauthorized',
  42. });
  43. }
  44. return Promise.reject({
  45. type: 'error',
  46. data: res,
  47. });
  48. });
  49. },
  50. handleMetaError(res) {
  51. if (res.status === 404) {
  52. const header = res.headers.get('WWW-Authenticate')?.[0] || '';
  53. if (/^Bearer realm="OneDriveAPI"/.test(header)) {
  54. return this.refreshToken().then(() => this.getMeta());
  55. }
  56. return;
  57. }
  58. throw res;
  59. },
  60. list() {
  61. return this.loadData({
  62. url: '/drive/special/approot/children',
  63. responseType: 'json',
  64. })
  65. .then(data => data.value.filter(item => item.file && isScriptFile(item.name)).map(normalize));
  66. },
  67. get(item) {
  68. const name = getItemFilename(item);
  69. return this.loadData({
  70. url: `/drive/special/approot:/${encodeURIComponent(name)}`,
  71. responseType: 'json',
  72. })
  73. .then(data => this.loadData({
  74. url: data['@content.downloadUrl'],
  75. delay: false,
  76. }));
  77. },
  78. put(item, data) {
  79. const name = getItemFilename(item);
  80. return this.loadData({
  81. method: 'PUT',
  82. url: `/drive/special/approot:/${encodeURIComponent(name)}:/content`,
  83. headers: {
  84. 'Content-Type': 'application/octet-stream',
  85. },
  86. body: data,
  87. responseType: 'json',
  88. })
  89. .then(normalize);
  90. },
  91. remove(item) {
  92. // return 204
  93. const name = getItemFilename(item);
  94. return this.loadData({
  95. method: 'DELETE',
  96. url: `/drive/special/approot:/${encodeURIComponent(name)}`,
  97. })
  98. .catch(noop);
  99. },
  100. authorize() {
  101. const params = {
  102. client_id: config.client_id,
  103. scope: 'onedrive.appfolder wl.offline_access',
  104. response_type: 'code',
  105. redirect_uri: config.redirect_uri,
  106. };
  107. const url = `https://login.live.com/oauth20_authorize.srf?${dumpQuery(params)}`;
  108. openAuthPage(url, config.redirect_uri);
  109. },
  110. checkAuth(url) {
  111. const redirectUri = `${config.redirect_uri}?code=`;
  112. if (url.startsWith(redirectUri)) {
  113. this.authState.set('authorizing');
  114. this.checkSync(this.authorized({
  115. code: url.slice(redirectUri.length),
  116. }));
  117. return true;
  118. }
  119. },
  120. revoke() {
  121. this.config.set({
  122. uid: null,
  123. token: null,
  124. refresh_token: null,
  125. });
  126. return this.prepare();
  127. },
  128. authorized(params) {
  129. return this.loadData({
  130. method: 'POST',
  131. url: 'https://login.live.com/oauth20_token.srf',
  132. prefix: '',
  133. headers: {
  134. 'Content-Type': FORM_URLENCODED,
  135. },
  136. body: dumpQuery(Object.assign({}, {
  137. client_id: config.client_id,
  138. client_secret: config.client_secret,
  139. redirect_uri: config.redirect_uri,
  140. grant_type: 'authorization_code',
  141. }, params)),
  142. responseType: 'json',
  143. })
  144. .then((data) => {
  145. if (data.access_token) {
  146. this.config.set({
  147. uid: data.user_id,
  148. token: data.access_token,
  149. refresh_token: data.refresh_token,
  150. });
  151. } else {
  152. throw data;
  153. }
  154. });
  155. },
  156. });
  157. register(OneDrive);
  158. function normalize(item) {
  159. return {
  160. name: item.name,
  161. size: item.size,
  162. uri: getURI(item.name),
  163. // modified: new Date(item.lastModifiedDateTime).getTime(),
  164. };
  165. }