ekyc

Electronic KYC process with uploading ID document using OAuth 2.1 (experimental)
Log | Files | Refs | README | LICENSE

ratelimit.ts (2820B)


      1 import { RateLimitRepository } from "#core/application/oauth2/ratelimit_repository.ts";
      2 import {
      3   EntityLocked,
      4   EntityNotFound,
      5 } from "#core/application/repository_error.ts";
      6 import { RateLimit } from "#core/domain/rate_limit.ts";
      7 import { InvalidUUID } from "#core/domain/uuid.ts";
      8 import {
      9   RateLimitDto,
     10   mapFromRateLimit,
     11   mapToRateLimit,
     12 } from "#infrastructure/memory/mapper/ratelimit.ts";
     13 import { mapError } from "#infrastructure/postgres/error.ts";
     14 import { Pool } from "$postgres";
     15 
     16 export class PostgresRateLimitRepositoryAdapter
     17   implements RateLimitRepository {
     18   constructor(readonly pool: Pool) {
     19   }
     20 
     21   async findOrCreate(key: string): Promise<RateLimit> {
     22     try {
     23       const connection = await this.pool.connect();
     24       try {
     25         const result = await connection.queryObject<RateLimitDto>`
     26             select *
     27             from "ratelimit"
     28             where key = ${key.toString()}
     29             limit 1;
     30           `;
     31         if (result.rowCount !== 1) {
     32           return new RateLimit(key)
     33         }
     34         return mapToRateLimit(result.rows[0]);
     35       } finally {
     36         connection.release();
     37       }
     38     } catch (error) {
     39       if (error instanceof InvalidUUID) {
     40         throw new EntityNotFound(key.toString());
     41       }
     42       throw mapError(error);
     43     }
     44   }
     45 
     46   async store(rateLimit: RateLimit): Promise<void> {
     47     try {
     48       const dto = mapFromRateLimit(rateLimit);
     49       const connection = await this.pool.connect();
     50       const transaction = connection.createTransaction(
     51         `txn_${dto.key}_${dto.version}`,
     52       );
     53       try {
     54         await transaction.begin();
     55         dto.version++;
     56         if (dto.version === 1) {
     57           await transaction.queryArray`
     58               insert into "ratelimit" (
     59                 "key", "count", "expire", "version"
     60               ) values (
     61                 ${dto.key}, ${dto.count}, ${dto.expire}, ${dto.version}
     62               );
     63             `;
     64           await transaction.commit();
     65           rateLimit.version = dto.version;
     66           return;
     67         }
     68         const result = await transaction.queryObject<{ version: number }>`
     69             update "ratelimit" set
     70               "count" = ${dto.count},
     71               "expire" = ${dto.expire},
     72               "version" = "version" + 1
     73             where "key" = ${dto.key}
     74             returning version;
     75           `;
     76         if (result.rowCount !== 1) {
     77           transaction.rollback();
     78           throw new EntityNotFound(dto.key);
     79         }
     80         if (result.rows[0].version === dto.version) {
     81           await transaction.commit();
     82           rateLimit.version = dto.version;
     83           return;
     84         }
     85         transaction.rollback();
     86         throw new EntityLocked();
     87       } finally {
     88         connection.release();
     89       }
     90     } catch (error) {
     91       throw mapError(error);
     92     }
     93   }
     94 }