diff --git a/.env.example b/.env.example index dd481681..5c18ebec 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,9 @@ NODE_ENV=production API_PORT=2785 LOG_LEVEL=info # error | warn | info | debug +# Auto-start previously authenticated sessions on server boot +AUTO_START_SESSIONS=false + # Domain Configuration DOMAIN=localhost DASHBOARD_PORT=2886 diff --git a/.env.minimal b/.env.minimal index a536ed94..934e9786 100644 --- a/.env.minimal +++ b/.env.minimal @@ -4,6 +4,9 @@ # This is a minimal configuration for development # and single-session personal bots using SQLite. +# Auto-start previously authenticated sessions on server boot +AUTO_START_SESSIONS=false + # Server PORT=2785 NODE_ENV=development diff --git a/src/modules/session/session.service.ts b/src/modules/session/session.service.ts index 671599db..f6d5f18e 100644 --- a/src/modules/session/session.service.ts +++ b/src/modules/session/session.service.ts @@ -5,9 +5,10 @@ import { BadRequestException, OnModuleDestroy, OnModuleInit, + OnApplicationBootstrap, } from '@nestjs/common'; import { InjectRepository, InjectDataSource } from '@nestjs/typeorm'; -import { Repository, In, DataSource } from 'typeorm'; +import { Repository, In, Not, IsNull, DataSource } from 'typeorm'; import { Session, SessionStatus } from './entities/session.entity'; import { CreateSessionDto } from './dto'; import { EngineFactory } from '../../engine/engine.factory'; @@ -25,7 +26,7 @@ interface ReconnectState { } @Injectable() -export class SessionService implements OnModuleDestroy, OnModuleInit { +export class SessionService implements OnModuleDestroy, OnModuleInit, OnApplicationBootstrap { private readonly logger = createLogger('SessionService'); // In-memory map of active engine instances @@ -70,6 +71,38 @@ export class SessionService implements OnModuleDestroy, OnModuleInit { } } + async onApplicationBootstrap(): Promise { + if (process.env.AUTO_START_SESSIONS !== 'true') return; + + const sessions = await this.sessionRepository.find({ + where: { phone: Not(IsNull()), status: SessionStatus.DISCONNECTED }, + }); + + if (sessions.length === 0) return; + + this.logger.log(`Auto-starting ${sessions.length} previously authenticated session(s)`, { + action: 'auto_start', + count: sessions.length, + }); + + for (const session of sessions) { + try { + await this.start(session.id); + this.logger.log(`Auto-started session: ${session.name}`, { + sessionId: session.id, + action: 'auto_start_success', + }); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.logger.error(`Auto-start failed for session: ${session.name}`, errorMessage, { + sessionId: session.id, + action: 'auto_start_failed', + }); + } + await this.delay(2000); + } + } + async onModuleDestroy(): Promise { // Clean up all engines on shutdown for (const [sessionId, engine] of this.engines) { @@ -537,4 +570,8 @@ export class SessionService implements OnModuleDestroy, OnModuleInit { isActive(id: string): boolean { return this.engines.has(id); } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } }