108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
import {
|
|
ConflictException,
|
|
Injectable,
|
|
NotFoundException,
|
|
} from '@nestjs/common';
|
|
import { createHash } from 'node:crypto';
|
|
import { PrismaService } from '../../prisma/prisma.service';
|
|
import {
|
|
SupportedTbankAccount,
|
|
accessibleTbankAccount,
|
|
supportedTbankAccounts,
|
|
} from './tbank-account.policy';
|
|
import { TbankTokenService } from './tbank-token.service';
|
|
import { TbankAccount, TbankClient } from './tbank.client';
|
|
|
|
export type TbankConnectionContext = {
|
|
token: string;
|
|
generation: string;
|
|
};
|
|
|
|
export type TbankAccountContext = TbankConnectionContext & {
|
|
account: TbankAccount;
|
|
};
|
|
|
|
@Injectable()
|
|
export class TbankAccessService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly tokens: TbankTokenService,
|
|
private readonly tbank: TbankClient,
|
|
) {}
|
|
|
|
async listSupportedAccounts(
|
|
userId: string,
|
|
): Promise<SupportedTbankAccount[]> {
|
|
return this.withSupportedAccounts(userId, ({ accounts }) => accounts);
|
|
}
|
|
|
|
async withSupportedAccounts<T>(
|
|
userId: string,
|
|
handler: (context: {
|
|
token: string;
|
|
generation: string;
|
|
accounts: SupportedTbankAccount[];
|
|
}) => Promise<T> | T,
|
|
): Promise<T> {
|
|
return this.withConnection(userId, async ({ token, generation }) =>
|
|
handler({
|
|
token,
|
|
generation,
|
|
accounts: supportedTbankAccounts(
|
|
await this.tbank.getOpenAccounts(token),
|
|
),
|
|
}),
|
|
);
|
|
}
|
|
|
|
async withConnection<T>(
|
|
userId: string,
|
|
handler: (context: TbankConnectionContext) => Promise<T> | T,
|
|
): Promise<T> {
|
|
const connection = await this.connection(userId);
|
|
return this.tokens.translateInvalidToken(async () => handler(connection));
|
|
}
|
|
|
|
async withAccount<T>(
|
|
userId: string,
|
|
accountId: string,
|
|
handler: (context: TbankAccountContext) => Promise<T>,
|
|
): Promise<T> {
|
|
return this.withConnection(userId, (connection) =>
|
|
this.withAccountForConnection(connection, accountId, handler),
|
|
);
|
|
}
|
|
|
|
async withAccountForConnection<T>(
|
|
connection: TbankConnectionContext,
|
|
accountId: string,
|
|
handler: (context: TbankAccountContext) => Promise<T>,
|
|
): Promise<T> {
|
|
const { token, generation } = connection;
|
|
return this.tokens.translateInvalidToken(async () => {
|
|
const account = accessibleTbankAccount(
|
|
await this.tbank.getOpenAccounts(token),
|
|
accountId,
|
|
);
|
|
if (!account) throw new NotFoundException();
|
|
return handler({ token, generation, account });
|
|
});
|
|
}
|
|
|
|
private async connection(
|
|
userId: string,
|
|
): Promise<{ token: string; generation: string }> {
|
|
const connection = await this.prisma.tbankConnection.findUnique({
|
|
where: { userId },
|
|
});
|
|
if (!connection)
|
|
throw new ConflictException({ code: 'TBANK_NOT_CONNECTED' });
|
|
return {
|
|
token: this.tokens.decrypt(connection.encryptedToken),
|
|
generation: createHash('sha256')
|
|
.update(connection.encryptedToken)
|
|
.digest('base64url'),
|
|
};
|
|
}
|
|
}
|