1- import { Injectable , Logger , OnModuleInit } from '@nestjs/common' ;
1+ import { Injectable , Logger } from '@nestjs/common' ;
22import { randomBytes , timingSafeEqual } from 'crypto' ;
3- import { chmod , mkdir , readFile , writeFile } from 'fs/promises' ;
3+ import { chmod , mkdir , readFile , unlink , writeFile } from 'fs/promises' ;
44import { dirname } from 'path' ;
55
66/**
77 * Service that manages a local session file for internal CLI/system authentication.
88 * Creates a secure token on startup that can be used for local system operations.
99 */
1010@Injectable ( )
11- export class LocalSessionService implements OnModuleInit {
11+ export class LocalSessionService {
1212 private readonly logger = new Logger ( LocalSessionService . name ) ;
1313 private sessionToken : string | null = null ;
1414 private static readonly SESSION_FILE_PATH = '/var/run/unraid-api/local-session' ;
1515
16- // NOTE: do NOT cleanup the session file upon eg. module/application shutdown.
17- // That would invalidate the session after each cli invocation, which is incorrect.
18- // Instead, rely on the startup logic to invalidate/overwrite any obsolete session.
19- async onModuleInit ( ) {
20- try {
21- await this . generateLocalSession ( ) ;
22- this . logger . verbose ( 'Local session initialized' ) ;
23- } catch ( error ) {
24- this . logger . error ( 'Failed to initialize local session:' , error ) ;
25- }
26- }
27-
2816 /**
2917 * Generate a secure local session token and write it to file
3018 */
31- private async generateLocalSession ( ) : Promise < void > {
19+ async generateLocalSession ( ) : Promise < void > {
3220 // Generate a cryptographically secure random token
3321 this . sessionToken = randomBytes ( 32 ) . toString ( 'hex' ) ;
3422
3523 try {
3624 // Ensure directory exists
37- await mkdir ( dirname ( LocalSessionService . SESSION_FILE_PATH ) , { recursive : true } ) ;
25+ await mkdir ( dirname ( LocalSessionService . getSessionFilePath ( ) ) , { recursive : true } ) ;
3826
3927 // Write token to file
40- await writeFile ( LocalSessionService . SESSION_FILE_PATH , this . sessionToken , {
28+ await writeFile ( LocalSessionService . getSessionFilePath ( ) , this . sessionToken , {
4129 encoding : 'utf-8' ,
4230 mode : 0o600 , // Owner read/write only
4331 } ) ;
4432
4533 // Ensure proper permissions (redundant but explicit)
46- await chmod ( LocalSessionService . SESSION_FILE_PATH , 0o600 ) ;
34+ await chmod ( LocalSessionService . getSessionFilePath ( ) , 0o600 ) ;
4735
48- this . logger . debug ( `Local session written to ${ LocalSessionService . SESSION_FILE_PATH } ` ) ;
36+ this . logger . debug ( `Local session written to ${ LocalSessionService . getSessionFilePath ( ) } ` ) ;
4937 } catch ( error ) {
5038 this . logger . error ( `Failed to write local session: ${ error } ` ) ;
5139 throw error ;
@@ -57,7 +45,7 @@ export class LocalSessionService implements OnModuleInit {
5745 */
5846 public async getLocalSession ( ) : Promise < string | null > {
5947 try {
60- return await readFile ( LocalSessionService . SESSION_FILE_PATH , 'utf-8' ) ;
48+ return await readFile ( LocalSessionService . getSessionFilePath ( ) , 'utf-8' ) ;
6149 } catch ( error ) {
6250 this . logger . warn ( error , 'Local session file not found or not readable' ) ;
6351 return null ;
@@ -77,6 +65,14 @@ export class LocalSessionService implements OnModuleInit {
7765 return timingSafeEqual ( Buffer . from ( token , 'utf-8' ) , Buffer . from ( currentToken , 'utf-8' ) ) ;
7866 }
7967
68+ public async deleteLocalSession ( ) : Promise < void > {
69+ try {
70+ await unlink ( LocalSessionService . getSessionFilePath ( ) ) ;
71+ } catch ( error ) {
72+ this . logger . error ( error , 'Error deleting local session file' ) ;
73+ }
74+ }
75+
8076 /**
8177 * Get the file path for the local session (useful for external readers)
8278 */
0 commit comments