File

src/database/database.service.ts

Index

Properties
Methods

Constructor

constructor(configService: ConfigService)
Parameters :
Name Type Optional
configService ConfigService No

Methods

Async enableShutdownHooks
enableShutdownHooks(app: INestApplication)
Parameters :
Name Type Optional
app INestApplication No
Returns : any
Async onModuleInit
onModuleInit()
Returns : any
Public Async prepareTestQueries
prepareTestQueries()
Returns : any
Public Async removeTestData
removeTestData()
Returns : any

Properties

Private Static connected
Default value : false
Private Static instances
Type : number
Default value : 0
Private Readonly logger
Default value : new Logger(DatabaseService.name)
Private Static mwareBound
Default value : false
Public Readonly read
Type : PrismaClient
Private Static replicated
Default value : false
Private Static sRead
Type : PrismaClient
Private Static sWrite
Type : PrismaClient
Public Readonly write
Type : PrismaClient
import { Injectable, INestApplication, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PrismaClient } from '@prisma/client';
import { Logger } from '@nestjs/common';
import * as Sentry from "@sentry/node";
import "@sentry/tracing";
import { MiddlewareParams } from 'prisma';

function createSentryMiddleware(type: 'read' | 'write') {
  return async (
    params: MiddlewareParams,
    next: (params: MiddlewareParams) => Promise<any>,
  ) => {
    const { model, action, runInTransaction, args } = params;
    const description = [model, action].filter(Boolean).join('.');
    const data = {
      model,
      action,
      runInTransaction,
      args,
    };

    const hub = Sentry.getCurrentHub();
    const transaction = hub.startTransaction({
      name: `database ${type} query`,
    });
    const span = transaction.startChild({
      op: 'db',
      description,
      data,
    });
    hub.configureScope((scope) => scope.setSpan(span));

    hub?.addBreadcrumb({
      category: 'db',
      message: description,
      data,
    });

    const result = await next(params);
    span?.finish();
    transaction.finish();
    return result;
  };
}

@Injectable()
export class DatabaseService implements OnModuleInit {
  public readonly read: PrismaClient;
  public readonly write: PrismaClient;
  private static sRead: PrismaClient; // dont create 23023 db clients
  private static sWrite: PrismaClient; // --||--
  private readonly logger = new Logger(DatabaseService.name);
  private static instances = 0;
  private static mwareBound = false;
  private static connected = false;
  private static replicated = false;
  constructor(private configService: ConfigService) {
    if (DatabaseService.sRead && DatabaseService.sWrite) {
      this.read = DatabaseService.sRead;
      this.write = DatabaseService.sWrite;
    } else {
      DatabaseService.instances++;
      this.logger.debug(
        `Creating new database driver (static read: ${!!DatabaseService.sRead}, static write: ${!!DatabaseService.sWrite})`,
      );
      DatabaseService.sWrite = new PrismaClient({
        datasources: { db: { url: configService.get<string>('DATABASE_URL') } },
      });

      if (this.configService.get('READ_REPLICA') === '1') {
        this.logger.log(`Connecting to replica`);
        DatabaseService.replicated = true;
        DatabaseService.sRead = new PrismaClient({
          datasources: {
            db: { url: configService.get<string>('READ_DATABASE_URL') },
          },
        });
      } else {
        DatabaseService.sRead = DatabaseService.sWrite;
      }

      if (!DatabaseService.mwareBound) {
        DatabaseService.mwareBound = true;
        if (DatabaseService.replicated) {
          DatabaseService.sRead.$use(createSentryMiddleware('read'));
        }

        DatabaseService.sWrite.$use(createSentryMiddleware('write'));
      }

      this.read = DatabaseService.sRead;
      this.write = DatabaseService.sWrite;
      this.logger.debug(`Created db instances: ${DatabaseService.instances}`);
    }
  }

  async onModuleInit() {
    if (!DatabaseService.connected) {
      DatabaseService.connected = true;
      try {
        await Promise.all([this.read.$connect(), this.write.$connect()]).catch(
          (err) => {
            throw err;
          },
        );
      } catch (err) {
        this.logger.error(`Prisma failed to connect to database`, err);
        throw err;
      }
    } else {
      this.logger.debug(
        'Not connecting to the database again, connection was already estabilished',
      );
    }
  }

  public async prepareTestQueries() {
    await this.removeTestData();
    await this.write.users.create({
      data: {
        snowflake: (Math.random() + 1).toString(36).substring(7),
        username: 'sex bear',
        email_hash:
          '54abacfd35a6a6e342d3e19e13908ddf83ae12899ee735ccf1ad862ca591c14b',
        email:
          'c37d8650f63631ca5513b67a6d42aa2b2d33b4b15b8a1ec98ff62155448cc07a',
        password:
          '$2b$10$mu031q8qKUZAofFOzov.IeHFnbv5fy2d8Tv9su1UW71975Sn8rB9W',
        blocked: false,
        admin: false,
        twostep: false,
        date: Date.now().toString(),
        init_ip: '127.0.0.1',
        sus: false,
        mock: true,
      },
    });
    this.logger.log('Added mock data to database');
  }

  public async removeTestData() {
    await this.write.users.deleteMany({
      where: {
        mock: true,
      },
    });
    this.logger.log('Removed mock data from database');
  }

  async enableShutdownHooks(app: INestApplication) {
    this.write.$on('beforeExit', async () => {
      await this.write.$disconnect();
      await this.read.$disconnect();
      await app.close();
    });
  }
}

results matching ""

    No results matching ""