Update import paths for DataTable component in InvoiceTable and SubscriptionsList to improve module structure and maintainability.
This commit is contained in:
parent
4447278b2c
commit
be3af76e01
@ -0,0 +1,153 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import type { WhmcsApiConfig } from "../types/connection.types";
|
||||
|
||||
/**
|
||||
* Service for managing WHMCS API configuration
|
||||
* Handles environment-based configuration loading with dev/prod separation
|
||||
*/
|
||||
@Injectable()
|
||||
export class WhmcsConfigService {
|
||||
private readonly config: WhmcsApiConfig;
|
||||
private readonly accessKey?: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.config = this.loadConfiguration();
|
||||
this.accessKey = this.loadAccessKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the complete WHMCS API configuration
|
||||
*/
|
||||
getConfig(): WhmcsApiConfig {
|
||||
return { ...this.config };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API access key if available
|
||||
*/
|
||||
getAccessKey(): string | undefined {
|
||||
return this.accessKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if admin authentication is available
|
||||
*/
|
||||
hasAdminAuth(): boolean {
|
||||
return Boolean(this.config.adminUsername && this.config.adminPasswordHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get admin authentication credentials
|
||||
*/
|
||||
getAdminAuth(): { username: string; passwordHash: string } | null {
|
||||
if (!this.hasAdminAuth()) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
username: this.config.adminUsername!,
|
||||
passwordHash: this.config.adminPasswordHash!,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that required configuration is present
|
||||
*/
|
||||
validateConfig(): void {
|
||||
const required = ['baseUrl', 'identifier', 'secret'];
|
||||
const missing = required.filter(key => !this.config[key as keyof WhmcsApiConfig]);
|
||||
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Missing required WHMCS configuration: ${missing.join(', ')}`);
|
||||
}
|
||||
|
||||
if (!this.config.baseUrl.startsWith('http')) {
|
||||
throw new Error('WHMCS baseUrl must start with http:// or https://');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from environment variables
|
||||
*/
|
||||
private loadConfiguration(): WhmcsApiConfig {
|
||||
const nodeEnv = this.configService.get<string>("NODE_ENV", "development");
|
||||
const isDev = nodeEnv !== "production";
|
||||
|
||||
// Resolve and normalize base URL (trim trailing slashes)
|
||||
const rawBaseUrl = this.getFirst([
|
||||
isDev ? "WHMCS_DEV_BASE_URL" : undefined,
|
||||
"WHMCS_BASE_URL"
|
||||
]) || "";
|
||||
const baseUrl = rawBaseUrl.replace(/\/+$/, "");
|
||||
|
||||
const identifier = this.getFirst([
|
||||
isDev ? "WHMCS_DEV_API_IDENTIFIER" : undefined,
|
||||
"WHMCS_API_IDENTIFIER"
|
||||
]) || "";
|
||||
|
||||
const secret = this.getFirst([
|
||||
isDev ? "WHMCS_DEV_API_SECRET" : undefined,
|
||||
"WHMCS_API_SECRET"
|
||||
]) || "";
|
||||
|
||||
const adminUsername = this.getFirst([
|
||||
isDev ? "WHMCS_DEV_ADMIN_USERNAME" : undefined,
|
||||
"WHMCS_ADMIN_USERNAME",
|
||||
]);
|
||||
|
||||
const adminPasswordHash = this.getFirst([
|
||||
isDev ? "WHMCS_DEV_ADMIN_PASSWORD_MD5" : undefined,
|
||||
"WHMCS_ADMIN_PASSWORD_MD5",
|
||||
"WHMCS_ADMIN_PASSWORD_HASH",
|
||||
]);
|
||||
|
||||
return {
|
||||
baseUrl,
|
||||
identifier,
|
||||
secret,
|
||||
timeout: this.getNumberConfig("WHMCS_API_TIMEOUT", 30000),
|
||||
retryAttempts: this.getNumberConfig("WHMCS_API_RETRY_ATTEMPTS", 3),
|
||||
retryDelay: this.getNumberConfig("WHMCS_API_RETRY_DELAY", 1000),
|
||||
adminUsername,
|
||||
adminPasswordHash,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load API access key
|
||||
*/
|
||||
private loadAccessKey(): string | undefined {
|
||||
const nodeEnv = this.configService.get<string>("NODE_ENV", "development");
|
||||
const isDev = nodeEnv !== "production";
|
||||
|
||||
return this.getFirst([
|
||||
isDev ? "WHMCS_DEV_API_ACCESS_KEY" : undefined,
|
||||
"WHMCS_API_ACCESS_KEY",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: read the first defined value across a list of keys
|
||||
*/
|
||||
private getFirst(keys: Array<string | undefined>): string | undefined {
|
||||
for (const key of keys) {
|
||||
if (!key) continue;
|
||||
const v = this.configService.get<string | undefined>(key);
|
||||
if (v && `${v}`.length > 0) return v;
|
||||
const raw = process.env[key];
|
||||
if (raw && `${raw}`.length > 0) return raw;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get numeric configuration value with fallback
|
||||
*/
|
||||
private getNumberConfig(key: string, defaultValue: number): number {
|
||||
const value = this.configService.get<string>(key);
|
||||
if (!value) return defaultValue;
|
||||
|
||||
const parsed = parseInt(value, 10);
|
||||
return isNaN(parsed) ? defaultValue : parsed;
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { Input } from "@/components/atoms";
|
||||
import { FormField } from "@/components/molecules/FormField";
|
||||
import { FormField } from "@/components/molecules/FormField/FormField";
|
||||
import type { SignupFormData } from "@customer-portal/domain";
|
||||
import type { FormErrors, FormTouched, UseZodFormReturn } from "@customer-portal/validation";
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"use client";
|
||||
|
||||
import { Input, Checkbox } from "@/components/atoms";
|
||||
import { FormField } from "@/components/molecules/FormField";
|
||||
import { FormField } from "@/components/molecules/FormField/FormField";
|
||||
import { type SignupFormData } from "@customer-portal/domain";
|
||||
import type { UseZodFormReturn } from "@customer-portal/validation";
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"use client";
|
||||
|
||||
import { Input } from "@/components/atoms";
|
||||
import { FormField } from "@/components/molecules/FormField";
|
||||
import { FormField } from "@/components/molecules/FormField/FormField";
|
||||
import { type SignupFormData } from "@customer-portal/domain";
|
||||
import type { FormErrors, FormTouched, UseZodFormReturn } from "@customer-portal/validation";
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
ExclamationTriangleIcon,
|
||||
ClockIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { DataTable } from "@/components/molecules/DataTable";
|
||||
import { DataTable } from "@/components/molecules/DataTable/DataTable";
|
||||
import { BillingStatusBadge } from "../BillingStatusBadge";
|
||||
import type { Invoice } from "@customer-portal/domain";
|
||||
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
|
||||
|
||||
@ -6,7 +6,7 @@ import Link from "next/link";
|
||||
import { Button } from "@/components/atoms/button";
|
||||
import { ErrorBoundary } from "@/components/molecules";
|
||||
import { PageLayout } from "@/components/templates/PageLayout";
|
||||
import { DataTable } from "@/components/molecules/DataTable";
|
||||
import { DataTable } from "@/components/molecules/DataTable/DataTable";
|
||||
import { StatusPill } from "@/components/atoms/status-pill";
|
||||
import { SubCard } from "@/components/molecules/SubCard";
|
||||
import { SearchFilterBar } from "@/components/molecules/SearchFilterBar";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user