storage.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import { arrayToB64, isFile } from './utils';
  2. import OwnedFile from './ownedFile';
  3. class Mem {
  4. constructor() {
  5. this.items = new Map();
  6. }
  7. get length() {
  8. return this.items.size;
  9. }
  10. getItem(key) {
  11. return this.items.get(key);
  12. }
  13. setItem(key, value) {
  14. return this.items.set(key, value);
  15. }
  16. removeItem(key) {
  17. return this.items.delete(key);
  18. }
  19. key(i) {
  20. return this.items.keys()[i];
  21. }
  22. }
  23. class Storage {
  24. constructor() {
  25. try {
  26. this.engine = localStorage || new Mem();
  27. } catch (e) {
  28. this.engine = new Mem();
  29. }
  30. this._files = this.loadFiles();
  31. this.pruneTokens();
  32. }
  33. loadFiles() {
  34. const fs = new Map();
  35. for (let i = 0; i < this.engine.length; i++) {
  36. const k = this.engine.key(i);
  37. if (isFile(k)) {
  38. try {
  39. const f = new OwnedFile(JSON.parse(this.engine.getItem(k)));
  40. if (!f.id) {
  41. f.id = f.fileId;
  42. }
  43. fs.set(f.id, f);
  44. } catch (err) {
  45. // obviously you're not a golfer
  46. this.engine.removeItem(k);
  47. }
  48. }
  49. }
  50. return fs;
  51. }
  52. get id() {
  53. let id = this.engine.getItem('device_id');
  54. if (!id) {
  55. id = arrayToB64(crypto.getRandomValues(new Uint8Array(16)));
  56. this.engine.setItem('device_id', id);
  57. }
  58. return id;
  59. }
  60. get totalDownloads() {
  61. return Number(this.engine.getItem('totalDownloads'));
  62. }
  63. set totalDownloads(n) {
  64. this.engine.setItem('totalDownloads', n);
  65. }
  66. get totalUploads() {
  67. return Number(this.engine.getItem('totalUploads'));
  68. }
  69. set totalUploads(n) {
  70. this.engine.setItem('totalUploads', n);
  71. }
  72. get referrer() {
  73. return this.engine.getItem('referrer');
  74. }
  75. set referrer(str) {
  76. this.engine.setItem('referrer', str);
  77. }
  78. get enrolled() {
  79. return JSON.parse(this.engine.getItem('ab_experiments') || '{}');
  80. }
  81. enroll(id, variant) {
  82. const enrolled = {};
  83. enrolled[id] = variant;
  84. this.engine.setItem('ab_experiments', JSON.stringify(enrolled));
  85. }
  86. get files() {
  87. return Array.from(this._files.values()).sort(
  88. (a, b) => a.createdAt - b.createdAt
  89. );
  90. }
  91. get user() {
  92. try {
  93. return JSON.parse(this.engine.getItem('user'));
  94. } catch (e) {
  95. return null;
  96. }
  97. }
  98. set user(info) {
  99. return this.engine.setItem('user', JSON.stringify(info));
  100. }
  101. getFileById(id) {
  102. return this._files.get(id);
  103. }
  104. get(id) {
  105. return this.engine.getItem(id);
  106. }
  107. set(id, value) {
  108. return this.engine.setItem(id, value);
  109. }
  110. remove(property) {
  111. if (isFile(property)) {
  112. this._files.delete(property);
  113. }
  114. this.engine.removeItem(property);
  115. }
  116. addFile(file) {
  117. this._files.set(file.id, file);
  118. this.writeFile(file);
  119. }
  120. writeFile(file) {
  121. this.engine.setItem(file.id, JSON.stringify(file));
  122. }
  123. writeFiles() {
  124. this._files.forEach(f => this.writeFile(f));
  125. }
  126. clearLocalFiles() {
  127. this._files.forEach(f => this.engine.removeItem(f.id));
  128. this._files = new Map();
  129. }
  130. async merge(files = []) {
  131. let incoming = false;
  132. let outgoing = false;
  133. let downloadCount = false;
  134. for (const f of files) {
  135. if (!this.getFileById(f.id)) {
  136. this.addFile(new OwnedFile(f));
  137. incoming = true;
  138. }
  139. }
  140. const workingFiles = this.files.slice();
  141. for (const f of workingFiles) {
  142. const cc = await f.updateDownloadCount();
  143. if (cc) {
  144. await this.writeFile(f);
  145. }
  146. downloadCount = downloadCount || cc;
  147. outgoing = outgoing || f.expired;
  148. if (f.expired) {
  149. this.remove(f.id);
  150. } else if (!files.find(x => x.id === f.id)) {
  151. outgoing = true;
  152. }
  153. }
  154. return {
  155. incoming,
  156. outgoing,
  157. downloadCount
  158. };
  159. }
  160. setDownloadToken(id, token) {
  161. let otherTokens = {};
  162. try {
  163. otherTokens = JSON.parse(this.get('dlTokens'));
  164. } catch (e) {
  165. //
  166. }
  167. if (token) {
  168. const record = { token, ts: Date.now() };
  169. this.set('dlTokens', JSON.stringify({ ...otherTokens, [id]: record }));
  170. } else {
  171. this.set('dlTokens', JSON.stringify({ ...otherTokens, [id]: undefined }));
  172. }
  173. }
  174. getDownloadToken(id) {
  175. try {
  176. return JSON.parse(this.get('dlTokens'))[id].token;
  177. } catch (e) {
  178. return undefined;
  179. }
  180. }
  181. pruneTokens() {
  182. try {
  183. const now = Date.now();
  184. const tokens = JSON.parse(this.get('dlTokens'));
  185. const keep = {};
  186. for (const id of Object.keys(tokens)) {
  187. const t = tokens[id];
  188. if (t.ts > now - 7 * 86400 * 1000) {
  189. keep[id] = t;
  190. }
  191. }
  192. if (Object.keys(keep).length < Object.keys(tokens).length) {
  193. this.set('dlTokens', JSON.stringify(keep));
  194. }
  195. } catch (e) {
  196. console.error(e);
  197. }
  198. }
  199. }
  200. export default new Storage();