83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
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<string, string>,
|
|
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) });
|
|
}
|
|
}
|
|
}
|