File size: 4,761 Bytes
b39afbe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
 * Copyright (c) 2023 MERCENARIES.AI PTE. LTD.
 * All rights reserved.
 */

//---------------------------------------------------------
// Endpoints for uploading and grabbing files from storage
//---------------------------------------------------------

import { type FastifyRequest, type FastifyReply } from 'fastify';
import { type CdnIntegration, type ICDNFidServeOpts } from '../CdnIntegration';
import type MercsServer from '../../../core/Server';

const fidClientExport = function () {
  return {
    description: 'Retrieve a workflow artifact',
    params: [{ name: 'fid', required: true, type: 'string' }]
  };
};

const uploadClientExport = function () {
  return {
    method: 'POST',
    description: 'Retrieve a workflow artifact',
    params: [{ name: 'fid', required: true, type: 'string' }]
  };
};

// Simple Upload Hander
// TODO: Stream based version that can handle large files like video
const createUploadHandler = function (integration: CdnIntegration, config: any) {
  return {
    schema: {
      headers: {
        type: 'object',
        properties: {
          'Content-Type': {
            type: 'string',
            pattern: '.*multipart/form-data.*' // Ensures the request has this content-type. Right now this suffice, might need a custom validation function instead for more complex validation
          }
        },
        required: ['content-type']
      }
    },
    handler: async function (request: FastifyRequest, reply: FastifyReply) {
      if (!request.user) {
        throw new Error('User not logged in');
      }

      const parts = request.parts();
      integration.info('upload', parts);

      const files = [];
      let storageType = 'temporary'; // Default storage type

      for await (const part of parts) {
        if (!(part as any).file) {
          // This is a non-file field
          const value = await (part as any).value;
          if (part.fieldname === 'storageType' && ['permanent', 'temporary'].includes(value)) {
            storageType = value;
          }
        } else {
          // This is a file
          const buffer = await (part as any).toBuffer();
          const fileName = (part as any).filename;

          let res;
          if (storageType === 'permanent') {
            res = await integration.put(buffer, { fileName, userId: request.user.id, tags: ['upload'] });
          } else {
            res = await integration.putTemp(buffer, { fileName, userId: request.user.id, tags: ['upload'] });
          }

          files.push(res);
        }
      }

      return await reply.send(files);
    }
  };
};

const createFidHandler = function (integration: CdnIntegration, config: any) {
  return {
    schema: {
      params: {
        type: 'object',
        properties: {
          fid: { type: 'string' }
        },
        required: ['fid']
      },
      querystring: {
        type: 'object',
        properties: {
          obj: { type: 'boolean' },
          test: { type: 'boolean' }
        }
      }
      // TODO: Validate response
    },
    handler: async function (request: FastifyRequest, reply: FastifyReply) {
      // const start = performance.now()
      //@ts-expect-error
      const fid = request.params.fid as string;

      if (fid == null) {
        return await reply.status(422).send({ error: 'Missing fid' });
      }

      const cdn = (integration.app as MercsServer).cdn;
      //@ts-expect-error
      if (request.query.obj) {
        const fo = await cdn.find(fid);
        if (fo == null) {
          return await reply
            .status(404)
            .header('Cache-Control', 'no-cache, no-store, must-revalidate')
            .send({ error: 'File not found' });
        } else {
          return await reply.status(200).send(fo);
        }
      }

      if ((request.query as any).test === 'true') {
        if (await cdn.checkFileExists(fid)) {
          return await reply.status(200).send({ exists: true });
        } else {
          return await reply
            .status(410)
            .header('Cache-Control', 'no-cache, no-store, must-revalidate')
            .send({ exists: false });
        }
      }

      const defaults = { download: false };
      const opts = Object.assign({}, defaults, { ...(request.query as ICDNFidServeOpts) });

      omnilog.log(opts);
      try {
        const servedFile = await cdn.serveFile(fid, opts, reply);
        return servedFile;
      } catch (ex: any) {
        integration.error(ex);
        const status = ex.response?.status ?? 500;
        const replied = reply.status(status).send({ error: `${status} : An error occurred` });
        return await replied;
      }
    }
  };
};

export { createFidHandler, fidClientExport, createUploadHandler, uploadClientExport };