ledgerone_backend/src/plaid/plaid.controller.ts

80 lines
2.7 KiB
TypeScript

import { BadRequestException, Body, Controller, Headers, Post, Req } from "@nestjs/common";
import { Request } from "express";
import { ok } from "../common/response";
import { PlaidService } from "./plaid.service";
import { CurrentUser } from "../common/decorators/current-user.decorator";
import { Public } from "../common/decorators/public.decorator";
import { OpaqueIdService } from "../common/opaque-id.service";
import { ViewRefService } from "../common/view-ref.service";
import { PrismaService } from "../prisma/prisma.service";
@Controller("plaid")
export class PlaidController {
constructor(
private readonly plaidService: PlaidService,
private readonly opaqueIds: OpaqueIdService,
private readonly viewRefs: ViewRefService,
private readonly prisma: PrismaService,
) {}
@Post("link-token")
async createLinkToken(@CurrentUser() userId: string) {
const data = await this.plaidService.createLinkToken(userId);
return ok(data);
}
@Post("exchange")
async exchange(
@CurrentUser() userId: string,
@Body() payload: { publicToken: string },
) {
const data = await this.plaidService.exchangePublicTokenForUser(userId, payload.publicToken);
return ok(data);
}
@Post("update-link-token")
async createUpdateLinkToken(
@CurrentUser() userId: string,
@Body() payload: { accountId: string },
) {
const accountId = await this.resolveAccountHandle(userId, payload.accountId);
const data = await this.plaidService.createUpdateModeLinkToken(userId, accountId);
return ok(data);
}
@Post("repair-complete")
async repairComplete(
@CurrentUser() userId: string,
@Body() payload: { accountId: string },
) {
const accountId = await this.resolveAccountHandle(userId, payload.accountId);
const data = await this.plaidService.markItemRepairComplete(userId, accountId);
return ok(data);
}
@Public()
@Post("webhook")
async webhook(
@Body() payload: Record<string, unknown>,
@Headers("plaid-verification") verification: string | undefined,
@Req() request: Request & { rawBody?: Buffer },
) {
const data = await this.plaidService.handleWebhook(payload, verification, request.rawBody);
return ok(data);
}
private async resolveAccountHandle(userId: string, handle: string) {
try {
return this.opaqueIds.decode("account", userId, handle);
} catch {
const accounts = await this.prisma.account.findMany({
where: { userId, isActive: true },
select: { id: true },
});
const match = accounts.find((account) => this.viewRefs.matches(userId, "account", account.id, handle));
if (!match) throw new BadRequestException("Invalid resource identifier.");
return match.id;
}
}
}