import assert from 'node:assert'; import BoxError from '../boxerror.js'; import http from 'node:http'; import { HttpError } from '@cloudron/connect-lastmile'; import safe from 'safetydance'; import services from '../services.js'; function proxy(kind) { assert(kind === 'mail' || kind === 'volume' || kind === 'app'); return async function (req, res, next) { req.clearTimeout(); let id = null; switch (kind) { case 'app': id = `app-${req.params.id}`; break; case 'volume': id = `volume-${req.params.id}`; break; case 'mail': id = 'mail'; break; } const [error, addonDetails] = await safe(services.getContainerDetails('sftp', 'CLOUDRON_SFTP_TOKEN')); if (error) return next(BoxError.toHttpError(error)); const searchParams = new URLSearchParams(req.url.slice(req.url.indexOf('?')+1)); searchParams.delete('access_token'); searchParams.append('access_token', addonDetails.token); const filepath = req.params.filepath.join('/'); const opts = { hostname: addonDetails.ip, port: 3000, path: `/files/${id}/${encodeURIComponent(filepath)}?${searchParams.toString()}`, // params[0] already contains leading '/' method: req.method, headers: req.headers }; const sftpReq = http.request(opts, function (sftpRes) { res.writeHead(sftpRes.statusCode, sftpRes.headers); // note: these are intentionally not handled. response has already been written. do not forward to connect-lastmile // sftpRes.on('error', (error) => next(new HttpError(500, `filemanager error: ${error.message} ${error.code}`))); // sftpRes.on('end', () => next()); sftpRes.pipe(res); }); sftpReq.on('error', (reqError) => next(new HttpError(424, `Unable to connect to filemanager: ${reqError.message} ${reqError.code}`))); if (!req.readable) { sftpReq.end(); } else { req.pipe(sftpReq); } }; } export default { proxy, };