Cuu.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
2.4 KiB

  1. import {
  2. createWriteStream,
  3. createReadStream,
  4. promises,
  5. ReadStream,
  6. } from 'fs';
  7. import readline from 'readline';
  8. export type Item = {
  9. url: string,
  10. reactions?: string[],
  11. }
  12. export const save = async (userId: string, item: Item): Promise<void> => {
  13. try {
  14. await promises.stat('.data');
  15. } catch {
  16. await promises.mkdir('.data');
  17. }
  18. return new Promise((resolve, reject) => {
  19. const ws = createWriteStream(`.data/${userId}.txt`, { flags: 'a' });
  20. ws.on('error', reject);
  21. ws.on('finish', resolve);
  22. ws.end(`${[item.url, ...(item.reactions || [])].join(' ')}\n`);
  23. });
  24. };
  25. export const load = async (userId: string, count = 1): Promise<Item[]> => {
  26. try {
  27. await promises.stat('.data');
  28. } catch {
  29. await promises.mkdir('.data');
  30. }
  31. return new Promise<Item[]>((resolve, reject) => {
  32. let rs: ReadStream;
  33. const items: Item[] = [];
  34. try {
  35. rs = createReadStream(`.data/${userId}.txt`, { flags: 'r' });
  36. rs.on('error', reject);
  37. const rl = readline.createInterface(rs);
  38. rl.on('line', (line) => {
  39. if (items.length >= count) {
  40. return;
  41. }
  42. const [url, ...reactions] = line.split(' ');
  43. items.push({
  44. url,
  45. reactions,
  46. });
  47. });
  48. rl.on('close', () => {
  49. resolve(items);
  50. });
  51. } catch {
  52. // noop
  53. }
  54. });
  55. };
  56. export const remove = async (userId: string, count = 1): Promise<void> => {
  57. try {
  58. await promises.stat('.data');
  59. } catch {
  60. await promises.mkdir('.data');
  61. }
  62. return new Promise((resolve, reject) => {
  63. let rs: ReadStream;
  64. let lines = 0;
  65. try {
  66. rs = createReadStream(`.data/${userId}.txt`, { flags: 'r' });
  67. rs.on('error', reject);
  68. const rl = readline.createInterface(rs);
  69. const ws = createWriteStream(`.data/${userId}.txt.tmp`, { flags: 'w' });
  70. // eslint-disable-next-line @typescript-eslint/no-misused-promises
  71. ws.on('close', async () => {
  72. await promises.unlink(`.data/${userId}.txt`);
  73. await promises.rename(`.data/${userId}.txt.tmp`, `.data/${userId}.txt`);
  74. resolve();
  75. });
  76. rl.on('line', (line) => {
  77. lines += 1;
  78. if (lines <= count) {
  79. return;
  80. }
  81. ws.write(`${line}\n`);
  82. });
  83. rl.on('close', () => {
  84. ws.close();
  85. });
  86. } catch (e) {
  87. // noop
  88. reject(e);
  89. }
  90. });
  91. };