File

src/users/users.service.ts

Index

Properties
Methods

Constructor

constructor(databaseService: DatabaseService, utilService: UtilsService, configService: ConfigService, codesService: CodesService, emailService: EmailsService, cloudflareService: CloudflareService, bezosService: BezosService, csamService: CsamService, uploadService: UploadsService)
Parameters :
Name Type Optional
databaseService DatabaseService No
utilService UtilsService No
configService ConfigService No
codesService CodesService No
emailService EmailsService No
cloudflareService CloudflareService No
bezosService BezosService No
csamService CsamService No
uploadService UploadsService No

Methods

Public Async blockUser
blockUser(user: string, until: number, reason: string, perma: boolean)
Parameters :
Name Type Optional
user string No
until number No
reason string No
perma boolean No
Returns : any
Public Async changeUserEmail
changeUserEmail(user: string, email: string)
Parameters :
Name Type Optional
user string No
email string No
Returns : any
Public Async confirmUserEmail
confirmUserEmail(snowflake: string, code: string)
Parameters :
Name Type Optional
snowflake string No
code string No
Returns : any
Public Async create
create(username: string, password: string, email: string, customSnw?: string, sendEmail)
Parameters :
Name Type Optional Default value
username string No
password string No
email string No
customSnw string Yes
sendEmail No true
Returns : Promise<string>
Public Async createUserApikey
createUserApikey(user: string, ip: string)
Parameters :
Name Type Optional
user string No
ip string No
Returns : any
Public Async deleteUserAccount
deleteUserAccount(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Public Async deleteUserApiKey
deleteUserApiKey(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : unknown
Public Async getActiveBlocks
getActiveBlocks()
Returns : unknown
Public Async getCoreUserData
getCoreUserData(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : Promise<FileglassMainDashBoard>
Public Async getDevEmails
getDevEmails()
Returns : Promise<string[]>
Public Async getLeaderBoard
getLeaderBoard(total: number)
Parameters :
Name Type Optional Default value
total number No 10
Returns : Promise<literal type[]>
Public Async getPassword
getPassword(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : Promise<string>
Public Async getUploadMinimalDataFromApikey
getUploadMinimalDataFromApikey(apikey: string, ip: string)
Parameters :
Name Type Optional
apikey string No
ip string No
Returns : unknown
Public Async getUploadMinimalDataFromUser
getUploadMinimalDataFromUser(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Public Async getUser
getUser(data: string, dataType: "username" | "email_hash")
Parameters :
Name Type Optional
data string No
dataType "username" | "email_hash" No
Returns : Promise<literal type | undefined>
Public Async getUserApiKey
getUserApiKey(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : unknown
Public Async getUserBlockData
getUserBlockData(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Public Async getUserCname
getUserCname(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Public Async getUserDomain
getUserDomain(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Public Async getUserEmail
getUserEmail(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : Promise<string>
Public Async getUsername
getUsername(snowflake: string)
Parameters :
Name Type Optional
snowflake string No
Returns : Promise<string>
Public Async getUserPasswordHash
getUserPasswordHash(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Public Async getUserPfp
getUserPfp(user: string)
Parameters :
Name Type Optional
user string No
Returns : unknown
Private Async isCnameSavedWithDomain
isCnameSavedWithDomain(cname: string, domain: string)
Parameters :
Name Type Optional
cname string No
domain string No
Returns : unknown
Public Async isIpRegistered
isIpRegistered(ip: string)
Parameters :
Name Type Optional
ip string No
Returns : unknown
Public Async isSnowflakeValid
isSnowflakeValid(snowflake: string, excessData?: T)
Type parameters :
  • T
Parameters :
Name Type Optional
snowflake string No
excessData T Yes
Returns : Promise<literal type>
Public Async sendEmailConfirmation
sendEmailConfirmation(email: string, snowflake: string, username: string)
Parameters :
Name Type Optional
email string No
snowflake string No
username string No
Returns : any
Public Async sendEmailToUser
sendEmailToUser(user: string, emailBody: string, subject: string)

Sends an email to the user (body has to be HTML)

Parameters :
Name Type Optional
user string No
emailBody string No
subject string No
Returns : any
Public Async setFieldData
setFieldData(user: string, field: T, state)
Type parameters :
  • T
Parameters :
Name Type Optional
user string No
field T No
state No
Returns : any
Public Async setUserCname
setUserCname(user: string, cname: string, domain?: string)
Parameters :
Name Type Optional
user string No
cname string No
domain string Yes
Returns : any
Public Async setUserDomain
setUserDomain(user: string, domain: string, cname?: string)
Parameters :
Name Type Optional
user string No
domain string No
cname string Yes
Returns : any
Public Async setUserPfp
setUserPfp(type: Type, args: PfpSetterArgs<Type>)
Type parameters :
  • Type
Parameters :
Name Type Optional
type Type No
args PfpSetterArgs<Type> No
Returns : unknown
Public Async unblockUser
unblockUser(user: string)
Parameters :
Name Type Optional
user string No
Returns : any
Public Async userExists
userExists(email_hash: string, username: string)
Parameters :
Name Type Optional
email_hash string No
username string No
Returns : Promise<boolean>
Private Async writePfpNameToDb
writePfpNameToDb(user: string, name: string, type)
Parameters :
Name Type Optional
user string No
name string No
type No
Returns : any

Properties

Private Readonly logger
Default value : new Logger(UsersService.name)
import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
import { DatabaseService } from '../database/database.service';
import { UtilsService } from '../utils/utils.service';
import * as Enums from '../utils/enums/responses.enum';
import { FileglassMainDashBoard } from '../../types';
import Prisma, { PFP_TYPE } from '@prisma/client';
import { AlreadyRegisteredException } from '../exceptions/already-registered.exception';
import { ConfigService } from '@nestjs/config';
import { ApikeyException } from '../exceptions/apikey.exception';
import { BlockedException } from '../exceptions/blocked.exception';
import { CodesService } from '../codes/codes.service';
import { EmailsService } from '../emails/emails.service';
import { CloudflareService } from '../cloud/cloudflare/cloudflare.service';
import { BezosService } from '../cloud/bezos.service';
import { CsamService } from '../cloud/csam/csam.service';
import { GenericException } from '../exceptions/generic.exception';
import { GenericRespEnum } from '../utils/enums/responses.enum';
import { UploadsService } from '../uploads/uploads.service';

@Injectable()
export class UsersService {
  private readonly logger = new Logger(UsersService.name);
  constructor(
    private readonly databaseService: DatabaseService,
    private readonly utilService: UtilsService,
    private readonly configService: ConfigService,
    private readonly codesService: CodesService,
    private readonly emailService: EmailsService,
    private readonly cloudflareService: CloudflareService,
    private readonly bezosService: BezosService,
    private readonly csamService: CsamService,
    @Inject(forwardRef(() => UploadsService))
    private readonly uploadService: UploadsService,
  ) {}

  public async create(
    username: string,
    password: string,
    email: string,
    customSnw?: string,
    sendEmail = true,
  ): Promise<string> {
    const emailHash = this.utilService.sha(email);
    if (await this.userExists(emailHash, username)) {
      throw new AlreadyRegisteredException();
    }

    const snowflake = customSnw || (await this.utilService.generateSnowflake());
    await this.databaseService.write.users.create({
      data: {
        snowflake,
        username,
        email_hash: emailHash,
        email: this.utilService.encrypt(email),
        password: await this.utilService.hashPass(password),
        blocked: false, // idk jesus is lazy so imma need to do that
        admin: false,
        twostep: false,
        date: Date.now().toString(),
        init_ip: 'ip',
        sus: false,
        email_verified: !sendEmail,
      },
    });
    if (sendEmail) {
      await this.sendEmailConfirmation(email, snowflake, username);
    }

    return Enums.RegisterRespEnum.USER_REGISTERED;
  }

  public async sendEmailConfirmation(
    email: string,
    snowflake: string,
    username: string,
  ) {
    const code = await this.codesService.createCode(
      'EMAIL_CONFIRM_INIT',
      20,
      snowflake,
    );
    const body = this.emailService.bodies.confirmEmail(
      this.utilService.buildConfirmUrl(code.code),
      username,
      true,
    );
    await new this.emailService.Mailer(email, 'Confirm your email', true)
      .append(body)
      .deliver();
  }

  public async isIpRegistered(ip: string) {
    return (
      (await this.databaseService.read.users.count({
        where: { init_ip: ip },
      })) > 0
    );
  }

  public async userExists(
    email_hash: string,
    username: string,
  ): Promise<boolean> {
    const queryResult = await this.databaseService.read.users.count({
      where: {
        OR: [
          {
            email_hash,
          },
          {
            username,
          },
        ],
      },
    });

    return queryResult > 0;
  }

  public async getUser(
    data: string,
    dataType: 'username' | 'email_hash',
  ): Promise<{ snowflake: string; emailVerified: boolean } | undefined> {
    this.logger.debug(`Selecting ${dataType} with value: ${data}`);
    const retval = await this.databaseService.read.users.findFirst({
      where: {
        [dataType]: data,
      },
      select: {
        snowflake: true,
        email_verified: true,
      },
    });
    return retval
      ? { snowflake: retval.snowflake, emailVerified: retval.email_verified }
      : undefined;
  }

  public async getPassword(snowflake: string): Promise<string> {
    const r = await this.databaseService.read.users.findFirst({
      where: {
        snowflake,
      },
      select: {
        password: true,
      },
    });
    return r.password;
  }
  public async getUploadMinimalDataFromUser(user: string) {
    const akey = await this.getUserApiKey(user);
    const result = await this.databaseService.read.api_keys.findFirst({
      where: {
        hash: akey.hash,
      },
      select: {
        owner: true,
        ip: true,
        users: {
          select: {
            blocked: true,
            cname: true,
            domain: true,
            raw: true,
            embed: true,
          },
        },
      },
    });
    return {
      snowflake: result.owner,
      cname: result.users.cname!,
      ip: result.ip!,
      domain: result.users.domain,
      raw: result.users.raw,
      embed: result!.users.embed!,
    };
  }
  public async getUploadMinimalDataFromApikey(apikey: string, ip: string) {
    const result = await this.databaseService.read.api_keys.findFirst({
      where: {
        hash: this.utilService.sha(apikey),
      },
      select: {
        owner: true,
        ip: true,
        users: {
          select: {
            blocked: true,
            cname: true,
            domain: true,
            raw: true,
            embed: true,
          },
        },
      },
    });
    if (result) {
      if (!result.users.blocked) {
        if (result.ip === ip || result.ip === '*') {
          return {
            snowflake: result.owner,
            cname: result.users.cname!,
            ip: result.ip!,
            domain: result.users.domain,
            raw: result.users.raw,
            embed: result!.users.embed!,
          };
        } else {
          throw new BlockedException(true);
        }
      } else {
        this.logger.log('Resulting as false');
        throw new BlockedException();
      }
    } else {
      this.logger.log('Throwing invalid apikey exception');
      throw new ApikeyException(true);
    }
  }

  public async isSnowflakeValid<T = Prisma.Prisma.usersSelect>(
    snowflake: string,
    excessData?: T,
  ): Promise<{
    valid: boolean;
    excessResult: Prisma.Prisma.CheckSelect<T, any, any>;
  }> {
    const data = await this.databaseService.read.users.findFirst({
      where: {
        snowflake,
      },
      select: {
        snowflake: true,
        ...((excessData as Prisma.Prisma.usersSelect) || undefined),
      },
    });
    return {
      valid: data !== null,
      excessResult: excessData
        ? (data as Prisma.Prisma.CheckSelect<T, any, any>) ||
          (this.utilService.setAllPropsTo(
            excessData as unknown as { [key: string]: unknown },
            false,
          ) as Prisma.Prisma.CheckSelect<T, any, any>)
        : undefined,
    };
  }

  public async getUserEmail(snowflake: string): Promise<string> {
    const r = await this.databaseService.read.users.findFirst({
      where: { snowflake },
      select: { email: true },
    });

    return this.utilService.decrypt(r.email);
  }

  public async confirmUserEmail(snowflake: string, code: string) {
    this.logger.debug(`Confirming user email with code ${code}`);
    await this.databaseService.write.users.update({
      where: { snowflake },
      data: {
        email_verified: true,
        codes: { update: { where: { code }, data: { used: true } } },
      },
    });
  }

  public async getUserApiKey(snowflake: string) {
    return await this.databaseService.read.api_keys.findFirst({
      where: { owner: snowflake },
    });
  }
  public async deleteUserApiKey(snowflake: string) {
    return await this.databaseService.write.api_keys.deleteMany({
      where: { owner: snowflake },
    });
  }

  public async createUserApikey(user: string, ip: string) {
    const key = this.utilService.sha(
      await this.utilService.generateSnowflake(50),
    );
    await this.deleteUserApiKey(user);
    await this.databaseService.write.api_keys.create({
      data: {
        ip,
        owner: user,
        api_key: this.utilService.encrypt(key),
        hash: this.utilService.sha(key),
      },
    });
  }
  public async getDevEmails(): Promise<string[]> {
    return (
      await this.databaseService.read.users.findMany({
        where: { admin: true },
        select: { email: true },
      })
    ).map((data) => this.utilService.decrypt(data.email));
  }

  public async getUsername(snowflake: string): Promise<string> {
    return (
      await this.databaseService.read.users.findFirst({
        where: { snowflake },
        select: { username: true },
      })
    ).username;
  }
  public async getCoreUserData(
    snowflake: string,
  ): Promise<FileglassMainDashBoard> {
    const r = await this.databaseService.read.users.findFirst({
      where: { snowflake },
      select: {
        username: true,
        tier: true,
        email: true,
        pfp: true,
        email_verified: true,
        leaderboard_private: true,
        admin: true,
        domain: true,
        cname: true,
        raw: true,
        pfp_type: true,
        twostep: true,
        uploads_uploadsTousers: {
          select: {
            size: true,
            date: true,
            views: true,
            likes: true,
            path: true,
            mime: true,
          },
          orderBy: { date: 'desc' },
        },
      },
    });
    let totalSize = 0;
    let totalViews = 0;
    const dates: string[] = [];
    r.uploads_uploadsTousers.forEach((upload) => {
      totalSize += parseInt(upload.size!);
      totalViews += upload.views!;
      dates.push(upload.date!);
    });
    let newestUpload;
    try {
      newestUpload = `${r.uploads_uploadsTousers[0].path}.${
        r.uploads_uploadsTousers[0].mime!.split('/')[1]
      }`;
    } catch (err) {
      newestUpload = '0i850.png';
    }
    return {
      memUsage: totalSize,
      totalPosts: r.uploads_uploadsTousers.length,
      newestUpload,
      totalViews,
      dates,
      username: r!.username!,
      tier: r!.tier!,
      email: this.utilService.decrypt(r!.email!),
      pfp: r!.pfp!,
      emailVerified: r!.email_verified!,
      leaderboardPrivate: r!.leaderboard_private,
      isAdmin: r!.admin || false,
      domain: r!.domain,
      cname: r!.cname === 'no-cname' ? 'c' : r!.cname!,
      raw: r!.raw,
      pfpDescriptor: r!.pfp_type,
      twostep: r!.twostep,
    };
  }

  public async getLeaderBoard(
    // proudly pasted from the old src
    total = 10,
  ): Promise<{ count: number; tier: number; username: string; pfp: string }[]> {
    const r = await this.databaseService.read.users.findMany({
      select: {
        uploads: true,
        username: true,
        tier: true,
        pfp: true,
      },
      orderBy: {
        uploads: 'desc',
      },
      where: {
        leaderboard_private: false,
      },
      take: total,
    });
    return r.map((user) => {
      return {
        count: user!.uploads!,
        tier: user!.tier!,
        username: user!.username!,
        pfp: user.pfp!,
      };
    });
  }
  public async setFieldData<T extends keyof Prisma.Prisma.usersUpdateInput>(
    user: string,
    field: T,
    state: Prisma.Prisma.usersUpdateInput[T],
  ) {
    await this.databaseService.write.users.update({
      where: {
        snowflake: user,
      },
      data: {
        [field]: state,
      },
    });
  }

  public async getUserPasswordHash(user: string) {
    return (
      await this.databaseService.read.users.findFirst({
        where: {
          snowflake: user,
        },
        select: {
          password: true,
        },
      })
    ).password;
  }

  public async getUserDomain(user: string) {
    const r = await this.databaseService.read.users.findFirst({
      where: { snowflake: user },
      select: { domain: true },
    });
    return r.domain;
  }
  public async getUserCname(user: string) {
    const r = await this.databaseService.read.users.findFirst({
      where: { snowflake: user },
      select: { cname: true },
    });
    this.logger.debug(`User cname: ${r.cname}`);
    return r.cname;
  }

  private async isCnameSavedWithDomain(cname: string, domain: string) {
    const r = await this.databaseService.read.cnames.count({
      where: {
        domain,
        cname,
      },
    });
    return r > 0;
  }
  public async setUserCname(user: string, cname: string, domain?: string) {
    domain = domain || (await this.getUserDomain(user));
    const cnameId = await this.cloudflareService.createCname(cname, domain);
    this.logger.debug('Cname', cname, 'cf response', cnameId);
    if (!(await this.isCnameSavedWithDomain(cname, domain))) {
      try {
        await this.databaseService.write.cnames.create({
          data: {
            cname,
            worker_bind: cnameId.workerId,
            id: cnameId.dnsId,
            creator: user,
          },
        });
      } catch (err) {}
    }

    await this.databaseService.write.users.update({
      where: { snowflake: user },
      data: {
        cname,
        domain,
      },
    });
  }

  public async setUserDomain(user: string, domain: string, cname?: string) {
    await this.setUserCname(
      user,
      cname || (await this.getUserCname(user)),
      domain,
    );
  }
  public async getActiveBlocks() {
    return await this.databaseService.read.users.findMany({
      where: {
        blocked: true,
      },
      select: {
        blocked_until: true,
        snowflake: true,
        ban_id: true,
      },
    });
  }

  /**
   * Sends an email to the user (body has to be HTML)
   * @param user
   * @param emailBody
   * @param subject
   */
  public async sendEmailToUser(
    user: string,
    emailBody: string,
    subject: string,
  ) {
    const email = await this.getUserEmail(user);
    const mailer = new this.emailService.Mailer(email, subject, true);
    mailer.append(emailBody);
    await mailer.deliver();
    this.logger.log(`Email sent to: ${email} with subject: ${subject}`);
  }
  public async getUserBlockData(user: string) {
    return await this.databaseService.read.users.findFirst({
      where: {
        snowflake: user,
      },
      select: {
        blocked: true,
        ban_id: true,
        block_msg: true,
        blocked_until: true,
        username: true,
      },
    });
  }
  public async unblockUser(user: string) {
    const blockData = await this.getUserBlockData(user);
    const body = this.emailService.bodies.unblockMessage(
      blockData.ban_id,
      blockData.username,
    );
    await this.databaseService.write.users.update({
      where: {
        snowflake: user,
      },
      data: {
        blocked: false,
        bans: {
          update: {
            where: {
              ban_id: blockData.ban_id,
            },
            data: {
              ended: true,
            },
          },
        },
      },
    });
    await this.sendEmailToUser(user, body, 'Account Suspension');
    this.logger.log(`User ${blockData.username} unblocked`);
  }

  public async blockUser(
    user: string,
    until: number,
    reason: string,
    perma: boolean,
  ) {
    until = perma ? 95647388400000 : until; //until 5000-12-12
    const banid = await this.utilService.generateSnowflake(15);
    await this.databaseService.write.users.update({
      where: {
        snowflake: user,
      },
      data: {
        blocked: true,
        blocked_until: until.toString(),
        ban_id: banid,
        block_msg: reason,
        bans: {
          create: {
            ban_id: banid,
            ends: until.toString(),
            ended: false,
            reason,
          },
        },
      },
    });
    const username = await this.getUsername(user);
    await this.sendEmailToUser(
      user,
      this.emailService.bodies.blockMessage(
        until,
        perma,
        reason,
        banid,
        username,
      ),
      'Account Suspension',
    );
    this.logger.log(
      `User ${username} blocked ${
        perma ? 'permanently' : `for ${this.utilService.humanize(until)}`
      }`,
    );
  }

  public async changeUserEmail(user: string, email: string) {
    const code = await this.codesService.createCode(
      'EMAIL_CONFIRM_UPDATE',
      20,
      user,
      email,
    );
    const username = await this.getUsername(user);
    const body = this.emailService.bodies.confirmEmail(
      this.utilService.buildConfirmUrl(code.code),
      username,
      false,
    );
    const mailer = new this.emailService.Mailer(
      email,
      'Confirm your email',
      true,
    );
    await mailer.append(body).deliver();
  }

  public async getUserPfp(user: string) {
    const r = await this.databaseService.read.users.findFirst({
      where: {
        snowflake: user,
      },
      select: {
        pfp: true,
        pfp_type: true,
      },
    });
    return { url: r.pfp, type: r.pfp_type };
  }

  private async writePfpNameToDb(
    user: string,
    name: string,
    type: keyof typeof PFP_TYPE,
  ) {
    await this.databaseService.write.users.update({
      where: {
        snowflake: user,
      },
      data: {
        pfp: name,
        pfp_type: type,
      },
    });
  }
  public async setUserPfp<Type extends PFP_TYPE>(
    type: Type,
    args: PfpSetterArgs<Type>,
  ) {
    const curr = await this.getUserPfp(args.user);
    switch (type) {
      case 'CUSTOM':
        await this.bezosService.removeFromCdn(curr.url, 'pfps');
        const isSafe = await this.csamService.isSafe(
          args.file,
          args.name,
          args.mime,
        );
        if (!isSafe) {
          throw new GenericException(GenericRespEnum.ERR_IMAGE_FILTERED, 403);
        }
        const fName = await this.utilService.generateSnowflake();
        const ext = this.utilService.getImageExtension(args.name);
        await this.bezosService.uploadToCdn(
          args.file,
          `${fName}.${ext}`,
          args.mime,
          'pfps',
        );
        await this.writePfpNameToDb(args.user, `${fName}.${ext}`, type);
        return GenericRespEnum.PFP_UPDATED;
      case 'DISCORD':
        await this.writePfpNameToDb(args.user, args.name, type);
        return GenericRespEnum.PFP_UPDATED;
      case 'GITHUB':
        await this.writePfpNameToDb(args.user, args.name, type);
        return GenericRespEnum.PFP_UPDATED;
    }
  }

  public async deleteUserAccount(user: string) {
    const uname = await this.getUsername(user);
    const emailaddr = await this.getUserEmail(user);
    const akey = await this.getUserApiKey(user);
    const emailBod = this.emailService.bodies.accountDelete(uname);
    await new this.emailService.Mailer(
      emailaddr,
      'Account marked for deletion',
      true,
    )
      .append(emailBod)
      .deliver();
    this.uploadService.wipeUserUploads(user).then(async () => {
      await this.databaseService.write.uploads.deleteMany({
        where: {
          owner: user,
        },
      });
    });
    await this.databaseService.write.users.delete({
      where: {
        snowflake: user,
      },
    });
    if (akey?.api_key) {
      await this.databaseService.write.api_keys.delete({
        where: {
          api_key: akey.api_key,
        },
      });
    }
    return GenericRespEnum.ACCOUNT_DELETED;
  }
}

interface PfpSetterArgs<Type extends PFP_TYPE> {
  file: Type extends typeof PFP_TYPE.CUSTOM ? Buffer : undefined;
  mime: Type extends typeof PFP_TYPE.CUSTOM ? string : undefined;
  name: string;
  user: string;
}

results matching ""

    No results matching ""