import { Injectable, Logger } from '@nestjs/common'; import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { EntityManager, In, Repository } from 'typeorm'; import { RefCodeWhiteList, User } from '../../database/entities'; import { RefCode } from '../../database/entities/refcode.entity'; import { WhitelistItem } from './whitelist.dto'; @Injectable() export class WhitelistService { constructor( @InjectRepository(RefCodeWhiteList, 'funki-id') private readonly refCodeWhiteListRepo: Repository, @InjectEntityManager('funki-id') private readonly em: EntityManager, ) {} private logger = new Logger(WhitelistService.name); private generateUniqueCode(): string { const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; return Array.from({ length: 6 }, () => characters[Math.floor(Math.random() * characters.length)]).join(''); } public async addWhitelist(items: WhitelistItem[]) { await this.em.transaction(async em => { // save ref code white list // Find existing whitelist entries const existingWhitelists = await em.find(RefCodeWhiteList, { where: { telegramId: In(items.map(item => item.telegramId)), }, }); this.logger.debug(`--------------------------------`); this.logger.debug(`Existing whitelist: ${existingWhitelists.length > 0 ? 'YES' : 'NO'}`); existingWhitelists.forEach(item => { this.logger.debug(`=> Telegram: ${item.telegramId} - Code: ${item.code}`); }); this.logger.debug(`--------------------------------`); // Filter out items that already exist const newItems = items.filter( item => !existingWhitelists.some(existing => existing.telegramId == item.telegramId), ); this.logger.debug(`--------------------------------`); this.logger.debug(`New whitelist: ${newItems.length > 0 ? 'YES' : 'NO'}`); newItems.forEach(item => { this.logger.debug(`=> Telegram: ${item.telegramId} - Code: ${item.code}`); }); this.logger.debug(`--------------------------------`); let whitelistItems = []; if (newItems.length > 0) { whitelistItems = await em.save( RefCodeWhiteList, newItems.map(item => ({ telegramId: item.telegramId, code: item.code ?? this.generateUniqueCode(), })), { chunk: 100, }, ); } // need to find already registered users and add ref code to them, because whitelist codes only trigger on registration const registeredUsers = await em.find(User, { where: { telegramId: In(items.map(item => item.telegramId)) } }); this.logger.debug(`--------------------------------`); this.logger.debug(`Registered users: ${registeredUsers.length > 0 ? 'YES' : 'NO'}`); registeredUsers.forEach(user => { this.logger.debug( `=> Telegram: ${user.telegramId} - UID: ${user.uid} - Code: ${items.find(item => item.telegramId.toString() == user.telegramId)?.code}`, ); }); this.logger.debug(`--------------------------------`); // map and save permanent ref codes to registered users // Get existing ref codes for these users const existingRefCodes = await em.find(RefCode, { where: registeredUsers.map(user => ({ ownerUid: user.uid, code: items.find(item => item.telegramId.toString() == user.telegramId)?.code, })), }); const refCodes = registeredUsers .map(user => { const code = items.find(item => item.telegramId.toString() == user.telegramId)?.code ?? this.generateUniqueCode(); // Skip if this user+code combination already exists if (existingRefCodes.some(existing => existing.ownerUid === user.uid && existing.code === code)) { return null; } return { ownerUid: user.uid, code, isPermanent: true, isUsed: false, }; }) .filter(Boolean); const permanentRefCodes = await em.save(RefCode, refCodes); // set onboarded to true await em.update(User, { uid: In(registeredUsers.map(user => user.uid)) }, { onboarded: true }); // log added ref codes this.logger.debug(`--------------------------------`); this.logger.debug(`Added whitelist: ${whitelistItems.length > 0 ? 'YES' : 'NO'}`); whitelistItems.forEach(item => { this.logger.debug(`=> Telegram: ${item.telegramId} - Code: ${item.code}`); }); this.logger.debug(`Added permanent ref codes: ${permanentRefCodes.length > 0 ? 'YES' : 'NO'}`); permanentRefCodes.forEach(item => { this.logger.debug(`=> UID: ${item.ownerUid} - Code: ${item.code}`); }); this.logger.debug(`--------------------------------`); }); } public async removeWhitelist(telegramIds: number[]) { await this.em.transaction(async em => { await em.delete(RefCodeWhiteList, { telegramId: In(telegramIds) }); }); } public async getWhitelist() { return this.refCodeWhiteListRepo.find({ order: { createdAt: 'DESC' } }); } public async getWhitelistByTelegramId(telegramId: number) { return this.refCodeWhiteListRepo.find({ where: { telegramId } }); } public async updateWhitelist(id: number, code: string) { return this.refCodeWhiteListRepo.save({ id, code }); } }