@smartsamurai/krapi-sdk
Version:
KRAPI TypeScript SDK - Easy-to-use client SDK for connecting to self-hosted KRAPI servers (like Appwrite SDK)
306 lines (287 loc) • 11.2 kB
text/typescript
/**
* System Adapter
*
* Unifies SystemHttpClient and SystemService behind a common interface.
*/
import { SystemHttpClient, SystemSettings as HttpClientSystemSettings } from "../../http-clients/system-http-client";
import { SystemService } from "../../system-service";
import { SystemInfo } from "../../types";
import { createAdapterInitError } from "./error-handler";
type Mode = "client" | "server";
/**
* Transform service SystemSettings (snake_case) to HTTP client SystemSettings (camelCase)
*/
function transformServiceToHttpSettings(
serviceData: unknown
): HttpClientSystemSettings {
if (!serviceData || typeof serviceData !== "object") {
return {} as HttpClientSystemSettings;
}
const data = serviceData as Record<string, unknown>;
const general = (data.general as Record<string, unknown>) || {};
const security = (data.security as Record<string, unknown>) || {};
const email = (data.email as Record<string, unknown>) || {};
const database = (data.database as Record<string, unknown>) || {};
return {
general: {
siteName:
(typeof general.site_name === "string" ? general.site_name : "") ||
(typeof general.siteName === "string" ? general.siteName : ""),
siteUrl:
(typeof general.site_url === "string" ? general.site_url : "") ||
(typeof general.siteUrl === "string" ? general.siteUrl : ""),
adminEmail:
(typeof general.admin_email === "string"
? general.admin_email
: "") ||
(typeof general.adminEmail === "string" ? general.adminEmail : ""),
timezone:
(typeof general.timezone === "string" ? general.timezone : "UTC") ||
"UTC",
defaultLanguage:
(typeof general.default_language === "string"
? general.default_language
: "en") ||
(typeof general.defaultLanguage === "string"
? general.defaultLanguage
: "en"),
},
security: {
requireTwoFactor:
typeof security.require_two_factor === "boolean"
? security.require_two_factor
: typeof security.requireTwoFactor === "boolean"
? security.requireTwoFactor
: false,
sessionTimeout:
typeof security.session_timeout === "number"
? security.session_timeout
: typeof security.sessionTimeout === "number"
? security.sessionTimeout
: 3600,
passwordMinLength:
typeof security.password_min_length === "number"
? security.password_min_length
: typeof security.passwordMinLength === "number"
? security.passwordMinLength
: 8,
passwordRequireUppercase:
typeof security.password_require_uppercase === "boolean"
? security.password_require_uppercase
: typeof security.passwordRequireUppercase === "boolean"
? security.passwordRequireUppercase
: false,
passwordRequireNumbers:
typeof security.password_require_numbers === "boolean"
? security.password_require_numbers
: typeof security.passwordRequireNumbers === "boolean"
? security.passwordRequireNumbers
: false,
passwordRequireSymbols:
typeof security.password_require_symbols === "boolean"
? security.password_require_symbols
: typeof security.passwordRequireSymbols === "boolean"
? security.passwordRequireSymbols
: false,
maxLoginAttempts:
typeof security.max_login_attempts === "number"
? security.max_login_attempts
: typeof security.maxLoginAttempts === "number"
? security.maxLoginAttempts
: 5,
},
email: {
smtpHost:
(typeof email.smtp_host === "string" ? email.smtp_host : "") ||
(typeof email.smtpHost === "string" ? email.smtpHost : ""),
smtpPort:
typeof email.smtp_port === "number"
? email.smtp_port
: typeof email.smtpPort === "number"
? email.smtpPort
: 587,
smtpUsername:
(typeof email.smtp_username === "string"
? email.smtp_username
: "") ||
(typeof email.smtpUsername === "string" ? email.smtpUsername : ""),
smtpPassword:
(typeof email.smtp_password === "string"
? email.smtp_password
: "") ||
(typeof email.smtpPassword === "string" ? email.smtpPassword : ""),
smtpSecure:
typeof email.smtp_secure === "boolean"
? email.smtp_secure
: typeof email.smtpSecure === "boolean"
? email.smtpSecure
: false,
fromEmail:
(typeof email.from_email === "string" ? email.from_email : "") ||
(typeof email.fromEmail === "string" ? email.fromEmail : ""),
fromName:
(typeof email.from_name === "string" ? email.from_name : "") ||
(typeof email.fromName === "string" ? email.fromName : ""),
},
database: {
connectionPoolSize:
typeof database.connection_pool_size === "number"
? database.connection_pool_size
: typeof database.connectionPoolSize === "number"
? database.connectionPoolSize
: 10,
queryTimeout:
typeof database.query_timeout === "number"
? database.query_timeout
: typeof database.queryTimeout === "number"
? database.queryTimeout
: 30000,
enableQueryLogging:
typeof database.enable_query_logging === "boolean"
? database.enable_query_logging
: typeof database.enableQueryLogging === "boolean"
? database.enableQueryLogging
: false,
backupSchedule:
(typeof database.backup_schedule === "string"
? database.backup_schedule
: "0 0 * * *") ||
(typeof database.backupSchedule === "string"
? database.backupSchedule
: "0 0 * * *"),
backupRetentionDays:
typeof database.backup_retention_days === "number"
? database.backup_retention_days
: typeof database.backupRetentionDays === "number"
? database.backupRetentionDays
: 30,
},
};
}
/**
* Transform HTTP client SystemSettings (camelCase) to service SystemSettings (snake_case)
* Note: Service SystemSettings has a different structure, so we use Record<string, unknown>
*/
function transformHttpToServiceSettings(
settings: Partial<HttpClientSystemSettings>
): Partial<Record<string, unknown>> {
const serviceSettings: Record<string, unknown> = {};
if (settings.general) {
serviceSettings.general = {
site_name: settings.general.siteName,
site_url: settings.general.siteUrl,
admin_email: settings.general.adminEmail,
timezone: settings.general.timezone,
default_language: settings.general.defaultLanguage,
};
}
if (settings.security) {
serviceSettings.security = {
require_two_factor: settings.security.requireTwoFactor,
session_timeout: settings.security.sessionTimeout,
password_min_length: settings.security.passwordMinLength,
password_require_uppercase: settings.security.passwordRequireUppercase,
password_require_numbers: settings.security.passwordRequireNumbers,
password_require_symbols: settings.security.passwordRequireSymbols,
max_login_attempts: settings.security.maxLoginAttempts,
};
}
if (settings.email) {
serviceSettings.email = {
smtp_host: settings.email.smtpHost,
smtp_port: settings.email.smtpPort,
smtp_username: settings.email.smtpUsername,
smtp_password: settings.email.smtpPassword,
smtp_secure: settings.email.smtpSecure,
from_email: settings.email.fromEmail,
from_name: settings.email.fromName,
};
}
if (settings.database) {
serviceSettings.database = {
connection_pool_size: settings.database.connectionPoolSize,
query_timeout: settings.database.queryTimeout,
enable_query_logging: settings.database.enableQueryLogging,
backup_schedule: settings.database.backupSchedule,
backup_retention_days: settings.database.backupRetentionDays,
};
}
return serviceSettings;
}
export class SystemAdapter {
private mode: Mode;
private httpClient: SystemHttpClient | undefined;
private service: SystemService | undefined;
constructor(mode: Mode, httpClient?: SystemHttpClient, service?: SystemService) {
this.mode = mode;
this.httpClient = httpClient;
this.service = service;
}
async getInfo(): Promise<SystemInfo> {
if (this.mode === "client") {
if (!this.httpClient) {
throw createAdapterInitError("HTTP client", this.mode);
}
const response = await this.httpClient.getSystemInfo();
const info = (response.data as unknown as SystemInfo) || ({} as SystemInfo);
// Ensure version is always present
if (!info.version) {
(info as unknown as Record<string, unknown>).version = "unknown";
}
return info;
} else {
if (!this.service) {
throw createAdapterInitError("System service", this.mode);
}
const response = await this.service.getSystemInfo();
const info = (response.data || {}) as SystemInfo;
// Ensure version is always present - use package.json version or default
if (!info.version) {
(info as unknown as Record<string, unknown>).version = process.env.npm_package_version || "1.0.0";
}
return info;
}
}
async getSettings(): Promise<HttpClientSystemSettings> {
if (this.mode === "client") {
if (!this.httpClient) {
throw createAdapterInitError("HTTP client", this.mode);
}
const response = await this.httpClient.getSettings();
return response.data || ({} as HttpClientSystemSettings);
} else {
if (!this.service) {
throw createAdapterInitError("System service", this.mode);
}
const result = await this.service.getSettings();
// Transform service SystemSettings to HTTP client SystemSettings format
const serviceData = result.data;
if (!serviceData) {
return {} as HttpClientSystemSettings;
}
return transformServiceToHttpSettings(serviceData);
}
}
async updateSettings(settings: Partial<HttpClientSystemSettings>): Promise<HttpClientSystemSettings> {
if (this.mode === "client") {
if (!this.httpClient) {
throw createAdapterInitError("HTTP client", this.mode);
}
const response = await this.httpClient.updateSettings(settings);
return response.data || ({} as HttpClientSystemSettings);
} else {
if (!this.service) {
throw createAdapterInitError("System service", this.mode);
}
// Transform HTTP client SystemSettings to service format
const serviceSettings = transformHttpToServiceSettings(settings);
const result = await this.service.updateSettings(serviceSettings);
// Transform back to HTTP client format
const resultData = result.data;
if (!resultData) {
return {} as HttpClientSystemSettings;
}
return transformServiceToHttpSettings(resultData);
}
}
}