- Simplified error handling in TokenBlacklistService by removing unnecessary error variable. - Enhanced type safety in SimUsageStoreService with improved type assertions and error handling. - Updated FreebititService to provide clearer error messages and consistent error handling across multiple methods. - Standardized import statements in WhmcsInvoiceService for better organization and clarity. - Added missing imports in dev-prep script to ensure proper functionality.
75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
import { Injectable, Inject } from "@nestjs/common";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { Redis } from "ioredis";
|
|
import { Logger } from "nestjs-pino";
|
|
|
|
@Injectable()
|
|
export class TokenBlacklistService {
|
|
constructor(
|
|
@Inject("REDIS_CLIENT") private readonly redis: Redis,
|
|
private readonly configService: ConfigService,
|
|
@Inject(Logger) private readonly logger: Logger
|
|
) {}
|
|
|
|
async blacklistToken(token: string, _expiresIn?: number): Promise<void> {
|
|
// Extract JWT payload to get expiry time
|
|
try {
|
|
const payload = JSON.parse(Buffer.from(token.split(".")[1] ?? "", "base64").toString()) as {
|
|
exp?: number;
|
|
};
|
|
const expiryTime = (payload.exp ?? 0) * 1000; // Convert to milliseconds
|
|
const currentTime = Date.now();
|
|
const ttl = Math.max(0, Math.floor((expiryTime - currentTime) / 1000)); // Convert to seconds
|
|
|
|
if (ttl > 0) {
|
|
await this.redis.setex(`blacklist:${token}`, ttl, "1");
|
|
}
|
|
} catch {
|
|
// If we can't parse the token, blacklist it for the default JWT expiry time
|
|
try {
|
|
const defaultTtl = this.parseJwtExpiry(this.configService.get("JWT_EXPIRES_IN", "7d"));
|
|
await this.redis.setex(`blacklist:${token}`, defaultTtl, "1");
|
|
} catch (err) {
|
|
this.logger.warn(
|
|
"Failed to write token to Redis blacklist; proceeding without persistence",
|
|
{
|
|
error: err instanceof Error ? err.message : String(err),
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
async isTokenBlacklisted(token: string): Promise<boolean> {
|
|
try {
|
|
const result = await this.redis.get(`blacklist:${token}`);
|
|
return result !== null;
|
|
} catch (err) {
|
|
// If Redis is unavailable, treat as not blacklisted to avoid blocking auth
|
|
this.logger.warn("Redis unavailable during blacklist check; allowing request", {
|
|
error: err instanceof Error ? err.message : String(err),
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private parseJwtExpiry(expiresIn: string): number {
|
|
// Convert JWT expiry string to seconds
|
|
const unit = expiresIn.slice(-1);
|
|
const value = parseInt(expiresIn.slice(0, -1), 10);
|
|
|
|
switch (unit) {
|
|
case "s":
|
|
return value;
|
|
case "m":
|
|
return value * 60;
|
|
case "h":
|
|
return value * 60 * 60;
|
|
case "d":
|
|
return value * 24 * 60 * 60;
|
|
default:
|
|
return 7 * 24 * 60 * 60; // Default 7 days in seconds
|
|
}
|
|
}
|
|
}
|