Files
backend/src/tbank/tbank-connection.service.ts
T

71 lines
2.1 KiB
TypeScript

import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Environment } from '../config/env.validation';
import { PrismaService } from '../prisma/prisma.service';
import { TbankClient } from './tbank.client';
import { TbankCrypto } from './tbank.crypto';
import { PortfolioService } from './portfolio.service';
type TbankTokenValidator = Pick<TbankClient, 'validateToken'>;
@Injectable()
export class TbankConnectionService {
private readonly crypto: TbankCrypto;
constructor(
private readonly prisma: PrismaService,
config: ConfigService<Environment, true>,
@Inject(TbankClient) private readonly tbank: TbankTokenValidator,
private readonly portfolio: PortfolioService,
) {
this.crypto = TbankCrypto.fromBase64(
config.getOrThrow('TOKEN_ENCRYPTION_KEY'),
);
}
async status(userId: string) {
const connection = await this.prisma.tbankConnection.findUnique({
where: { userId },
});
return {
connected: Boolean(connection),
connectedAt: connection?.connectedAt.toISOString() ?? null,
};
}
async put(userId: string, value: string) {
const token = value.trim();
if (!token) throw new BadRequestException({ code: 'VALIDATION_ERROR' });
try {
await this.tbank.validateToken(token);
} catch (error) {
if (error instanceof Error && error.message === 'INVALID_TBANK_TOKEN') {
throw new BadRequestException({ code: 'INVALID_TBANK_TOKEN' });
}
throw error;
}
const connection = await this.prisma.tbankConnection.upsert({
where: { userId },
create: {
userId,
encryptedToken: this.crypto.encrypt(token),
connectedAt: new Date(),
},
update: {
encryptedToken: this.crypto.encrypt(token),
connectedAt: new Date(),
},
});
this.portfolio.invalidate(userId);
return {
connected: true,
connectedAt: connection.connectedAt.toISOString(),
};
}
async delete(userId: string): Promise<void> {
await this.prisma.tbankConnection.deleteMany({ where: { userId } });
this.portfolio.invalidate(userId);
}
}