feat: wire sidecar to platform — auth proxy with workspace subdomain, GraphQL proxy, health check

This commit is contained in:
2026-03-18 07:15:47 +05:30
parent d488d551ed
commit a42d479f06
9 changed files with 281 additions and 25 deletions

View File

@@ -6,9 +6,13 @@ import axios from 'axios';
export class AuthController {
private readonly logger = new Logger(AuthController.name);
private readonly graphqlUrl: string;
private readonly workspaceSubdomain: string;
private readonly origin: string;
constructor(private config: ConfigService) {
this.graphqlUrl = config.get<string>('platform.graphqlUrl')!;
this.workspaceSubdomain = process.env.PLATFORM_WORKSPACE_SUBDOMAIN ?? 'fortytwo-dev';
this.origin = process.env.PLATFORM_ORIGIN ?? 'http://fortytwo-dev.localhost:4010';
}
@Post('login')
@@ -16,46 +20,40 @@ export class AuthController {
this.logger.log(`Login attempt for ${body.email}`);
try {
const response = await axios.post(this.graphqlUrl, {
// Step 1: Get login token
const loginRes = await axios.post(this.graphqlUrl, {
query: `mutation GetLoginToken($email: String!, $password: String!) {
getLoginTokenFromCredentials(
email: $email
password: $password
origin: "http://localhost:5173"
origin: "${this.origin}"
) {
loginToken { token }
}
}`,
variables: { email: body.email, password: body.password },
}, {
headers: { 'Content-Type': 'application/json' },
headers: {
'Content-Type': 'application/json',
'X-Workspace-Subdomain': this.workspaceSubdomain,
},
});
if (response.data.errors) {
if (loginRes.data.errors) {
throw new HttpException(
response.data.errors[0]?.message ?? 'Login failed',
loginRes.data.errors[0]?.message ?? 'Login failed',
401,
);
}
return response.data.data.getLoginTokenFromCredentials;
} catch (error) {
if (error instanceof HttpException) throw error;
this.logger.error(`Login proxy failed: ${error}`);
throw new HttpException('Authentication service unavailable', 503);
}
}
const loginToken = loginRes.data.data.getLoginTokenFromCredentials.loginToken.token;
@Post('tokens')
async getTokens(@Body() body: { loginToken: string }) {
this.logger.log('Exchanging login token for access token');
try {
const response = await axios.post(this.graphqlUrl, {
// Step 2: Exchange for access + refresh tokens
const tokenRes = await axios.post(this.graphqlUrl, {
query: `mutation GetAuthTokens($loginToken: String!) {
getAuthTokensFromLoginToken(
loginToken: $loginToken
origin: "http://localhost:5173"
origin: "${this.origin}"
) {
tokens {
accessOrWorkspaceAgnosticToken { token }
@@ -63,22 +61,30 @@ export class AuthController {
}
}
}`,
variables: { loginToken: body.loginToken },
variables: { loginToken },
}, {
headers: { 'Content-Type': 'application/json' },
headers: {
'Content-Type': 'application/json',
'X-Workspace-Subdomain': this.workspaceSubdomain,
},
});
if (response.data.errors) {
if (tokenRes.data.errors) {
throw new HttpException(
response.data.errors[0]?.message ?? 'Token exchange failed',
tokenRes.data.errors[0]?.message ?? 'Token exchange failed',
401,
);
}
return response.data.data.getAuthTokensFromLoginToken;
const tokens = tokenRes.data.data.getAuthTokensFromLoginToken.tokens;
return {
accessToken: tokens.accessOrWorkspaceAgnosticToken.token,
refreshToken: tokens.refreshToken.token,
};
} catch (error) {
if (error instanceof HttpException) throw error;
this.logger.error(`Token exchange proxy failed: ${error}`);
this.logger.error(`Login proxy failed: ${error}`);
throw new HttpException('Authentication service unavailable', 503);
}
}