All files / src/utils tokenStorage.ts

100% Statements 21/21
75% Branches 3/4
100% Functions 7/7
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 691x 1x                 1x       18x       8x 8x 6x     3x         5x         5x 5x     4x   4x             3x 3x       2x 2x 2x       2x 2x             3x      
import * as fs from 'fs/promises';
import * as path from 'path';
 
export interface StoredTokens {
  github?: string;
  gitlab?: string;
  azure?: string;
  lastUpdated?: string;
}
 
export class TokenStorage {
  private readonly tokenFilePath: string;
 
  constructor(tokenFilePath?: string) {
    this.tokenFilePath = tokenFilePath ?? path.join(process.cwd(), '.tokens.json');
  }
 
  async loadTokens(): Promise<StoredTokens> {
    try {
      const data = await fs.readFile(this.tokenFilePath, 'utf-8');
      return JSON.parse(data) as StoredTokens;
    } catch {
      // File doesn't exist or is invalid, return empty tokens
      return {};
    }
  }
 
  async saveTokens(tokens: StoredTokens): Promise<void> {
    const tokensWithTimestamp: StoredTokens = {
      ...tokens,
      lastUpdated: new Date().toISOString(),
    };
 
    try {
      await fs.writeFile(this.tokenFilePath, JSON.stringify(tokensWithTimestamp, null, 2), 'utf-8');
 
      // Set restrictive permissions (readable only by owner)
      await fs.chmod(this.tokenFilePath, 0o600);
    } catch (error) {
      process.stderr.write(
        `Warning: Could not save tokens to ${this.tokenFilePath}: ${error instanceof Error ? error.message : String(error)}\n`,
      );
    }
  }
 
  async getToken(provider: 'github' | 'gitlab' | 'azure'): Promise<string | undefined> {
    const tokens = await this.loadTokens();
    return tokens[provider];
  }
 
  async setToken(provider: 'github' | 'gitlab' | 'azure', token: string): Promise<void> {
    const tokens = await this.loadTokens();
    tokens[provider] = token;
    await this.saveTokens(tokens);
  }
 
  async clearTokens(): Promise<void> {
    try {
      await fs.unlink(this.tokenFilePath);
    } catch {
      // File doesn't exist, which is fine
    }
  }
 
  getTokensFilePath(): string {
    return this.tokenFilePath;
  }
}