usw-api.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /* global API msg */// msg.js
  2. /* global URLS */ // toolbox.js
  3. /* global tokenMan */
  4. 'use strict';
  5. const uswApi = (() => {
  6. //#region Internals
  7. class TokenHooks {
  8. constructor(id) {
  9. this.id = id;
  10. }
  11. keyName(name) {
  12. return `${name}/${this.id}`;
  13. }
  14. query(query) {
  15. return Object.assign(query, {vendor_data: this.id});
  16. }
  17. }
  18. function fakeUsercssHeader(style) {
  19. const {name, _usw: u = {}} = style;
  20. const meta = Object.entries({
  21. '@name': u.name || name || '?',
  22. '@version': // Same as USO-archive version: YYYYMMDD.hh.mm
  23. new Date().toISOString().replace(/^(\d+)-(\d+)-(\d+)T(\d+):(\d+).+/, '$1$2$3.$4.$5'),
  24. '@namespace': u.namespace !== '?' && u.namespace ||
  25. u.username && `userstyles.world/user/${u.username}` ||
  26. '?',
  27. '@description': u.description,
  28. '@author': u.username,
  29. '@license': u.license,
  30. });
  31. const maxKeyLen = meta.reduce((res, [k]) => Math.max(res, k.length), 0);
  32. return [
  33. '/* ==UserStyle==',
  34. ...meta.map(([k, v]) => `${k}${' '.repeat(maxKeyLen - k.length + 2)}${v || ''}`),
  35. '==/UserStyle== */',
  36. ].join('\n') + '\n\n';
  37. }
  38. async function linkStyle(style, sourceCode) {
  39. const {id} = style;
  40. const metadata = await API.worker.parseUsercssMeta(sourceCode).catch(console.warn) || {};
  41. const uswData = Object.assign({}, style, {metadata, sourceCode});
  42. API.data.set('usw' + id, uswData);
  43. const token = await tokenMan.getToken('userstylesworld', true, new TokenHooks(id));
  44. const info = await uswFetch('style', token);
  45. const data = style._usw = Object.assign({token}, info);
  46. style.url = style.url || data.homepage || `${URLS.usw}style/${data.id}`;
  47. await uswSave(style);
  48. return data;
  49. }
  50. async function uswFetch(path, token, opts) {
  51. opts = Object.assign({credentials: 'omit'}, opts);
  52. opts.headers = Object.assign({Authorization: `Bearer ${token}`}, opts.headers);
  53. return (await (await fetch(`${URLS.usw}api/${path}`, opts)).json()).data;
  54. }
  55. /** Uses a custom method when broadcasting and avoids needlessly sending the entire style */
  56. async function uswSave(style) {
  57. const {id, _usw} = style;
  58. await API.styles.save(style, {broadcast: false});
  59. msg.broadcastExtension({method: 'uswData', style: {id, _usw}});
  60. }
  61. //#endregion
  62. //#region Exports
  63. return {
  64. /**
  65. * @param {number} id
  66. * @param {string} sourceCode
  67. * @return {Promise<string>}
  68. */
  69. async publish(id, sourceCode) {
  70. const style = await API.styles.get(id);
  71. const data = (style._usw || {}).token
  72. ? style._usw
  73. : await linkStyle(style, sourceCode);
  74. const header = style.usercssData ? '' : fakeUsercssHeader(style);
  75. return uswFetch(`style/${data.id}`, data.token, {
  76. method: 'POST',
  77. headers: {'Content-Type': 'application/json'},
  78. body: JSON.stringify({code: header + sourceCode}),
  79. });
  80. },
  81. /**
  82. * @param {number} id
  83. * @return {Promise<void>}
  84. */
  85. async revoke(id) {
  86. await tokenMan.revokeToken('userstylesworld', new TokenHooks(id));
  87. const style = await API.styles.get(id);
  88. if (style) {
  89. style._usw = {};
  90. await uswSave(style);
  91. }
  92. },
  93. };
  94. //#endregion
  95. })();
  96. /* Doing this outside so we don't break IDE's recognition of the exported methods in IIFE */
  97. for (const [k, fn] of Object.entries(uswApi)) {
  98. uswApi[k] = async (id, ...args) => {
  99. API.data.set('usw' + id, true);
  100. try {
  101. /* Awaiting inside `try` so that `finally` runs when done */
  102. return await fn(id, ...args);
  103. } finally {
  104. API.data.del('usw' + id);
  105. }
  106. };
  107. }