Api.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import {
  2. basicAuth,
  3. encodeBase64,
  4. fetchLinesIterator,
  5. maybeWithBasicAuth,
  6. topicShortUrl,
  7. topicUrl,
  8. topicUrlAuth,
  9. topicUrlJsonPoll,
  10. topicUrlJsonPollWithSince, userStatsUrl
  11. } from "./utils";
  12. import userManager from "./UserManager";
  13. class Api {
  14. async poll(baseUrl, topic, since) {
  15. const user = await userManager.get(baseUrl);
  16. const shortUrl = topicShortUrl(baseUrl, topic);
  17. const url = (since)
  18. ? topicUrlJsonPollWithSince(baseUrl, topic, since)
  19. : topicUrlJsonPoll(baseUrl, topic);
  20. const messages = [];
  21. const headers = maybeWithBasicAuth({}, user);
  22. console.log(`[Api] Polling ${url}`);
  23. for await (let line of fetchLinesIterator(url, headers)) {
  24. console.log(`[Api, ${shortUrl}] Received message ${line}`);
  25. messages.push(JSON.parse(line));
  26. }
  27. return messages;
  28. }
  29. async publish(baseUrl, topic, message, options) {
  30. const user = await userManager.get(baseUrl);
  31. console.log(`[Api] Publishing message to ${topicUrl(baseUrl, topic)}`);
  32. const headers = {};
  33. const body = {
  34. topic: topic,
  35. message: message,
  36. ...options
  37. };
  38. await fetch(baseUrl, {
  39. method: 'PUT',
  40. body: JSON.stringify(body),
  41. headers: maybeWithBasicAuth(headers, user)
  42. });
  43. }
  44. publishXHR(baseUrl, topic, body, headers, onProgress) {
  45. const url = topicUrl(baseUrl, topic);
  46. const xhr = new XMLHttpRequest();
  47. console.log(`[Api] Publishing message to ${url}`);
  48. const send = new Promise(function (resolve, reject) {
  49. xhr.open("PUT", url);
  50. xhr.addEventListener('readystatechange', (ev) => {
  51. if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status <= 299) {
  52. console.log(`[Api] Publish successful (HTTP ${xhr.status})`, xhr.response);
  53. resolve(xhr.response);
  54. } else if (xhr.readyState === 4) {
  55. console.log(`[Api] Publish failed (HTTP ${xhr.status})`, xhr.responseText);
  56. let errorText;
  57. try {
  58. const error = JSON.parse(xhr.responseText);
  59. if (error.code && error.error) {
  60. errorText = `Error ${error.code}: ${error.error}`;
  61. }
  62. } catch (e) {
  63. // Nothing
  64. }
  65. xhr.abort();
  66. reject(errorText ?? "An error occurred");
  67. }
  68. })
  69. xhr.upload.addEventListener("progress", onProgress);
  70. if (body.type) {
  71. xhr.overrideMimeType(body.type);
  72. }
  73. for (const [key, value] of Object.entries(headers)) {
  74. xhr.setRequestHeader(key, value);
  75. }
  76. xhr.send(body);
  77. });
  78. send.abort = () => {
  79. console.log(`[Api] Publish aborted by user`);
  80. xhr.abort();
  81. }
  82. return send;
  83. }
  84. async auth(baseUrl, topic, user) {
  85. const url = topicUrlAuth(baseUrl, topic);
  86. console.log(`[Api] Checking auth for ${url}`);
  87. const response = await fetch(url, {
  88. headers: maybeWithBasicAuth({}, user)
  89. });
  90. if (response.status >= 200 && response.status <= 299) {
  91. return true;
  92. } else if (!user && response.status === 404) {
  93. return true; // Special case: Anonymous login to old servers return 404 since /<topic>/auth doesn't exist
  94. } else if (response.status === 401 || response.status === 403) { // See server/server.go
  95. return false;
  96. }
  97. throw new Error(`Unexpected server response ${response.status}`);
  98. }
  99. async userStats(baseUrl) {
  100. const url = userStatsUrl(baseUrl);
  101. console.log(`[Api] Fetching user stats ${url}`);
  102. const response = await fetch(url);
  103. if (response.status !== 200) {
  104. throw new Error(`Unexpected server response ${response.status}`);
  105. }
  106. return response.json();
  107. }
  108. }
  109. const api = new Api();
  110. export default api;