Files
edr-platform/apps/edr-freight-api/src/modules/otp/otp.controller.ts
2026-07-20 12:10:49 +00:00

78 lines
2.2 KiB
TypeScript

// otp.controller.ts
import {
BadRequestException,
Body,
Controller,
Post,
} from "@nestjs/common";
import { OtpService, OtpTarget } from "./otp.service";
import { Public } from "@edr/api-common";
// At least one of phone/email must be present. When BOTH are given the code is
// sent to both and either one verifies it — the caller no longer picks a single
// channel, it just states every address it knows for the account.
function toTarget(phone?: string, email?: string): OtpTarget {
const target: OtpTarget = {};
if (email?.trim()) target.email = email;
if (phone?.trim()) target.phone = phone;
if (!target.email && !target.phone) {
throw new BadRequestException("phone or email is required");
}
return target;
}
// TODO: these public routes need per-target + per-IP rate limiting (a NestJS
// ThrottlerGuard / @Throttle on /otp/send and /otp/verify). No Throttler is
// wired into the app yet; add @nestjs/throttler and apply it here.
@Controller("otp")
@Public()
export class OtpController {
constructor(
private readonly otpService: OtpService
) {}
// ---------------------------------------------------------------------------
// Send OTP
// ---------------------------------------------------------------------------
@Post("send")
async sendOtp(
@Body("phone")
phone?: string,
@Body("email")
email?: string
) {
// `delivered` stays server-side: this route is @Public(), and whether our
// broker accepted the publish is infrastructure state an anonymous caller has
// no need for. It is on the `otp.dispatch` log line instead.
const { success, message } = await this.otpService.sendOtp(
toTarget(phone, email)
);
return { success, message };
}
// ---------------------------------------------------------------------------
// Verify OTP
// ---------------------------------------------------------------------------
@Post("verify")
async verifyOtp(
@Body("phone")
phone: string | undefined,
@Body("email")
email: string | undefined,
@Body("otp")
otp: string
) {
return this.otpService.verifyOtp(
toTarget(phone, email),
otp
);
}
}