feat(admin-ui): add configurable Admin UI domain routing
This commit is contained in:
@@ -4,6 +4,7 @@ import * as interfaces from '../../../ts_interfaces/index.ts';
|
||||
import { requireAdminIdentity } from '../helpers/guards.ts';
|
||||
import { logger } from '../../logging.ts';
|
||||
import { getErrorMessage } from '../../utils/error.ts';
|
||||
import { isValidHostname, normalizeHostname } from '../../utils/domain.ts';
|
||||
|
||||
export class SettingsHandler {
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
@@ -23,6 +24,7 @@ export class SettingsHandler {
|
||||
return {
|
||||
cloudflareToken: cloudflareToken || '',
|
||||
cloudflareZoneId: settingsMap['cloudflareZoneId'] || '',
|
||||
adminUiDomain: settingsMap['adminUiDomain'] || '',
|
||||
dcrouterMode: managedDcRouter.getMode(),
|
||||
dcrouterManagedImage: managedDcRouter.getImage(),
|
||||
dcrouterManagedOpsPort: managedDcRouter.getOpsPort(),
|
||||
@@ -64,8 +66,10 @@ export class SettingsHandler {
|
||||
const db = this.opsServerRef.oneboxRef.database;
|
||||
const updates = dataArg.settings;
|
||||
|
||||
const normalizedUpdates = this.normalizeUpdates(updates);
|
||||
|
||||
// Store each setting as key-value pair
|
||||
for (const [key, value] of Object.entries(updates)) {
|
||||
for (const [key, value] of Object.entries(normalizedUpdates)) {
|
||||
if (value !== undefined) {
|
||||
if (db.isSecretSettingKey(key)) {
|
||||
await db.setSecretSetting(key, String(value));
|
||||
@@ -75,8 +79,8 @@ export class SettingsHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasExternalGatewaySetting(updates)) {
|
||||
this.refreshDcRouterGateway().catch((error) => {
|
||||
if (this.hasRouteSyncSetting(normalizedUpdates)) {
|
||||
this.refreshGatewayRoutes(normalizedUpdates).catch((error) => {
|
||||
logger.warn(`dcrouter gateway settings refresh failed: ${getErrorMessage(error)}`);
|
||||
});
|
||||
}
|
||||
@@ -110,8 +114,23 @@ export class SettingsHandler {
|
||||
);
|
||||
}
|
||||
|
||||
private hasExternalGatewaySetting(settings: Partial<interfaces.data.ISettings>): boolean {
|
||||
private normalizeUpdates(
|
||||
settings: Partial<interfaces.data.ISettings>,
|
||||
): Partial<interfaces.data.ISettings> {
|
||||
const normalizedUpdates = { ...settings };
|
||||
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'adminUiDomain')) {
|
||||
const normalizedDomain = normalizeHostname(String(normalizedUpdates.adminUiDomain || ''));
|
||||
if (!isValidHostname(normalizedDomain)) {
|
||||
throw new plugins.typedrequest.TypedResponseError('Invalid Admin UI domain');
|
||||
}
|
||||
normalizedUpdates.adminUiDomain = normalizedDomain;
|
||||
}
|
||||
return normalizedUpdates;
|
||||
}
|
||||
|
||||
private hasRouteSyncSetting(settings: Partial<interfaces.data.ISettings>): boolean {
|
||||
return [
|
||||
'adminUiDomain',
|
||||
'dcrouterMode',
|
||||
'dcrouterManagedImage',
|
||||
'dcrouterManagedOpsPort',
|
||||
@@ -127,23 +146,29 @@ export class SettingsHandler {
|
||||
].some((key) => Object.prototype.hasOwnProperty.call(settings, key));
|
||||
}
|
||||
|
||||
private async refreshDcRouterGateway(): Promise<void> {
|
||||
private hasManagedDcRouterRuntimeSetting(settings: Partial<interfaces.data.ISettings>): boolean {
|
||||
return [
|
||||
'dcrouterMode',
|
||||
'dcrouterManagedImage',
|
||||
'dcrouterManagedOpsPort',
|
||||
'dcrouterManagedHttpPort',
|
||||
'dcrouterManagedHttpsPort',
|
||||
'dcrouterManagedDataDir',
|
||||
].some((key) => Object.prototype.hasOwnProperty.call(settings, key));
|
||||
}
|
||||
|
||||
private async refreshGatewayRoutes(settings: Partial<interfaces.data.ISettings>): Promise<void> {
|
||||
const onebox = this.opsServerRef.oneboxRef;
|
||||
if (onebox.managedDcRouter.getMode() === 'managed') {
|
||||
await onebox.managedDcRouter.restart();
|
||||
} else {
|
||||
await onebox.managedDcRouter.stop();
|
||||
if (this.hasManagedDcRouterRuntimeSetting(settings)) {
|
||||
if (onebox.managedDcRouter.getMode() === 'managed') {
|
||||
await onebox.managedDcRouter.restart();
|
||||
} else {
|
||||
await onebox.managedDcRouter.stop();
|
||||
}
|
||||
}
|
||||
|
||||
await onebox.reverseProxy.reloadRoutes();
|
||||
await onebox.externalGateway.syncDomains();
|
||||
|
||||
const services = onebox.database.getAllServices().filter((service) => service.domain);
|
||||
await Promise.all(services.map(async (service) => {
|
||||
try {
|
||||
await onebox.externalGateway.syncServiceRoute(service);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to sync external gateway route for ${service.domain}: ${getErrorMessage(error)}`);
|
||||
}
|
||||
}));
|
||||
await onebox.externalGateway.syncServiceRoutes();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user