import { All, Controller, Get, Headers, Inject, Req, Res } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; import { MountService } from './mount.service'; @ApiTags('Mount') @Controller() export class MountController { // NOTE: explicit @Inject — tsx/esbuild strips design:paramtypes metadata, // so implicit constructor injection resolves to undefined under `tsx watch`. constructor(@Inject(MountService) private readonly mount: MountService) {} @Get('health') @ApiOperation({ summary: 'Health + upstream targets' }) @ApiResponse({ status: 200, description: 'Mount status' }) health() { return { ok: true, service: 'mindmount', routes: { '/ips/{*path}': this.mount.gapmindBase() + '/*', '/socials/{*path}': this.mount.socmindBase() + '/*', }, gapmindTokenProvisioned: Boolean(this.mount.serviceToken()), }; } @Get('api/routes') @ApiOperation({ summary: 'List proxied routes' }) @ApiResponse({ status: 200, description: 'Route table' }) routes() { return [ { prefix: '/ips/{*path}', target: this.mount.gapmindBase(), auth: 'mount token, gapmind service token injected' }, { prefix: '/socials/{*path}', target: this.mount.socmindBase(), auth: 'mount token' }, ]; } @All('ips/{*path}') @ApiOperation({ summary: 'Proxy to gapmind (IP intelligence)' }) async ips(@Req() req: any, @Res() res: any, @Headers('authorization') auth?: string) { return this.forward(req, res, auth, this.mount.gapmindBase(), '/ips', true); } @All('socials/{*path}') @ApiOperation({ summary: 'Proxy to socmind (social intelligence)' }) async socials(@Req() req: any, @Res() res: any, @Headers('authorization') auth?: string) { return this.forward(req, res, auth, this.mount.socmindBase(), '/socials', false); } private async forward( req: any, res: any, auth: string | undefined, base: string, prefix: string, injectToken: boolean, ) { try { this.mount.requireMountAuth(auth); } catch (e: any) { return res.status(e.status || 401).send({ message: e.message || 'Unauthorized' }); } const rawUrl: string = req.raw?.url || req.url || '/'; const [path, query] = rawUrl.split('?'); const subpath = path.slice(prefix.length) || '/'; try { const out = await this.mount.proxy( base, subpath, req.method, query || '', (req.headers || {}) as Record, req.body, injectToken, ); if (out.contentType) res.header('Content-Type', out.contentType); return res.status(out.status).send(out.buffer); } catch (e) { return res .status(502) .send({ message: 'Upstream unreachable', detail: e instanceof Error ? e.message : String(e) }); } } }