File size: 2,657 Bytes
4327358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { IMediaStorage, MediaData } from '@waha/core/media/IMediaStorage';
import { SECOND } from '@waha/structures/enums.dto';
import * as fsp from 'fs/promises';
import * as path from 'path';
import { Logger } from 'pino';
import fs = require('fs');
import { fileExists } from '@waha/utils/files';
import { deleteAsync } from 'del';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const writeFileAtomic = require('write-file-atomic');

/**
 * Save files locally using the filesystem
 */
export class MediaLocalStorage implements IMediaStorage {
  private readonly lifetimeMs: number;

  constructor(
    protected log: Logger,
    private filesFolder: string,
    private baseUrl: string,
    lifetimeSeconds: number,
  ) {
    this.lifetimeMs = lifetimeSeconds * SECOND;
    if (this.lifetimeMs === 0) {
      this.log.info('Files lifetime is 0, files will not be removed');
    }
  }

  async init() {
    return;
  }

  async exists(data: MediaData): Promise<boolean> {
    const filepath = this.getFullPath(data);
    return await fileExists(filepath);
  }

  public async save(buffer: Buffer, data: MediaData): Promise<boolean> {
    const filepath = this.getFullPath(data);
    const folder = path.dirname(filepath);
    await fsp.mkdir(folder, { recursive: true });
    await writeFileAtomic(filepath, buffer);
    this.postponeRemoval(filepath);
    return true;
  }

  public async getStorageData(data: MediaData) {
    const filename = this.getKey(data);
    const url = this.baseUrl + filename;
    return { url };
  }

  async purge() {
    if (this.lifetimeMs === 0) {
      this.log.info('No need to purge files with lifetime 0');
      return;
    }

    if (fs.existsSync(this.filesFolder)) {
      deleteAsync([`${this.filesFolder}/*`], { force: true }).then((paths) => {
        if (paths.length === 0) {
          return;
        }
        this.log.info('Deleted files and directories:\n', paths.join('\n'));
      });
    } else {
      fs.mkdirSync(this.filesFolder);
      this.log.info(`Directory '${this.filesFolder}' created from scratch`);
    }
  }

  private getKey(data: MediaData) {
    return `${data.session}/${data.message.id}.${data.file.extension}`;
  }

  private getFullPath(data: MediaData) {
    const filepath = this.getKey(data);
    return path.resolve(`${this.filesFolder}/${filepath}`);
  }

  private postponeRemoval(filepath: string) {
    if (this.lifetimeMs === 0) {
      return;
    }
    setTimeout(
      () =>
        fs.unlink(filepath, () => {
          this.log.info(`File ${filepath} was removed`);
        }),
      this.lifetimeMs,
    );
  }

  async close() {
    return;
  }
}