import assert from 'node:assert'; import AuditSource from '../auditsource.js'; import BoxError from '../boxerror.js'; import logger from '../logger.js'; import http from 'node:http'; import { HttpError } from '@cloudron/connect-lastmile'; import { HttpSuccess } from '@cloudron/connect-lastmile'; import mailServer from '../mailserver.js'; import safe from 'safetydance'; import services from '../services.js'; const { log } = logger('routes/mailserver'); async function proxyToMailContainer(port, pathname, req, res, next) { req.clearTimeout(); const [error, addonDetails] = await safe(services.getContainerDetails('mail', 'CLOUDRON_MAIL_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 opts = { hostname: addonDetails.ip, port, path: `/${pathname}?${searchParams.toString()}`, method: req.method, headers: req.headers }; const mailReq = http.request(opts, function (mailRes) { res.writeHead(mailRes.statusCode, mailRes.headers); mailRes.on('error', (resError) => next(new HttpError(500, `mailserver error: ${resError.message} ${resError.code}`))); mailRes.on('end', () => next()); mailRes.pipe(res); }); mailReq.on('error', (reqError) => next(new HttpError(424, `Unable to connect to mailserver: ${reqError.message} ${reqError.code}`))); if (!req.readable) { mailReq.end(); } else { req.pipe(mailReq); } } async function proxy(req, res, next) { const pathname = req.path.split('/').pop(); proxyToMailContainer(3000, pathname, req, res, function (httpError) { if (httpError) return next(httpError); // for success, the proxy already sent the response. do not proceed to connect-lastmile which will result in double headers }); } async function proxyAndRestart(req, res, next) { const pathname = req.path.split('/').pop(); proxyToMailContainer(3000, pathname, req, res, async function (httpError) { if (httpError) return next(httpError); // for success, the proxy already sent the response. do not proceed to connect-lastmile which will result in double headers await safe(mailServer.restart(), { debug: log }); }); } async function getLocation(req, res, next) { const [error, result] = await safe(mailServer.getLocation()); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, { domain: result.domain, subdomain: result.subdomain })); } async function setLocation(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.domain !== 'string') return next(new HttpError(400, 'domain must be a string')); if (typeof req.body.subdomain !== 'string') return next(new HttpError(400, 'subdomain must be a string')); const [error, taskId] = await safe(mailServer.startChangeLocation(req.body.subdomain, req.body.domain, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(202, { taskId })); } export default { proxy, proxyAndRestart, setLocation, getLocation };