From 3f62c8ec6a320f81ad0adead228ce1730dcc8ce4 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 25 Aug 2026 11:13:12 -0700 Subject: [PATCH] resolve scoping issue by fetching proxy summary --- client/src/Pages/CreateMonitor/index.tsx | 5 +---- client/src/Pages/Settings/index.tsx | 13 ++++-------- client/src/Types/Settings.ts | 6 ++++++ .../src/api/controllers/settingsController.ts | 21 +++++++++++++++---- server/src/config/controllers.ts | 2 +- server/src/domain/proxies/proxy.service.ts | 13 +++++++++++- server/src/domain/proxies/proxy.type.ts | 2 ++ 7 files changed, 43 insertions(+), 19 deletions(-) diff --git a/client/src/Pages/CreateMonitor/index.tsx b/client/src/Pages/CreateMonitor/index.tsx index 6d19ae25b..5c2b693e0 100644 --- a/client/src/Pages/CreateMonitor/index.tsx +++ b/client/src/Pages/CreateMonitor/index.tsx @@ -484,10 +484,7 @@ const CreateMonitorPage = () => { })), [t] ); - const globalProxyName = - appSettings?.settings?.globalProxyEnabled === true - ? proxies?.find((proxy) => proxy.id === appSettings?.settings?.globalProxyId)?.name - : undefined; + const globalProxyName = appSettings?.globalProxy?.name; const proxyModeOptions = useMemo( () => diff --git a/client/src/Pages/Settings/index.tsx b/client/src/Pages/Settings/index.tsx index 3377182af..70a8e1bb6 100644 --- a/client/src/Pages/Settings/index.tsx +++ b/client/src/Pages/Settings/index.tsx @@ -45,12 +45,7 @@ import { FormSwitchField } from "@/Components/inputs/forms/FormSwitchField"; import { FormNumberField } from "@/Components/inputs/forms/FormNumberField"; import { FormSelectField } from "@/Components/inputs/forms/FormSelectField"; import type { ProxyResponse } from "@/Types/Proxy"; - -interface SettingsResponse { - settings: any; - pagespeedKeySet: boolean; - emailPasswordSet: boolean; -} +import type { AppSettingsResponse } from "@/Types/Settings"; export const SettingsPage = () => { const theme = useTheme(); @@ -66,9 +61,9 @@ export const SettingsPage = () => { const { post: importMonitors, loading: isImportingMonitors } = usePost(); // Fetch settings data from API - const { data: fetchedSettings } = useGet("/settings"); + const { data: fetchedSettings } = useGet("/settings"); // Get proxies - const { data: proxies } = useGet("/proxies/team"); + const { data: proxies } = useGet("/proxies"); const proxyOptions = useMemo( () => (proxies ?? []).map((proxy) => ({ @@ -79,7 +74,7 @@ export const SettingsPage = () => { ); // Form submission - const { patch, loading: isSaving } = usePatch(); + const { patch, loading: isSaving } = usePatch(); // Local state for API key reset const [isApiKeySet, setIsApiKeySet] = useState( diff --git a/client/src/Types/Settings.ts b/client/src/Types/Settings.ts index 22cbb6a8d..01d34aead 100644 --- a/client/src/Types/Settings.ts +++ b/client/src/Types/Settings.ts @@ -33,5 +33,11 @@ export interface Settings { export interface AppSettingsResponse { pagespeedKeySet: boolean; emailPasswordSet: boolean; + globalProxy: { + id: string; + name: string; + host: string; + port: number; + } | null; settings: Settings; } diff --git a/server/src/api/controllers/settingsController.ts b/server/src/api/controllers/settingsController.ts index a8113ed2b..d02c3886a 100644 --- a/server/src/api/controllers/settingsController.ts +++ b/server/src/api/controllers/settingsController.ts @@ -5,6 +5,7 @@ import { sendTestEmailBodyValidation } from "@/api/validation/notificationValida import { AppError } from "@/utils/AppError.js"; import { ISettingsService } from "@/domain/app-settings/app-settings.service.js"; import { IEmailService } from "@/service/emailService.js"; +import { IProxiesService } from "@/domain/proxies/proxy.service.js"; import { Settings } from "@/domain/app-settings/app-settings.type.js"; export interface ISettingsController { @@ -16,18 +17,23 @@ export interface ISettingsController { class SettingsController implements ISettingsController { private settingsService: ISettingsService; private emailService: IEmailService; - constructor(settingsService: ISettingsService, emailService: IEmailService) { + private proxiesService: IProxiesService; + constructor(settingsService: ISettingsService, emailService: IEmailService, proxiesService: IProxiesService) { this.settingsService = settingsService; this.emailService = emailService; + this.proxiesService = proxiesService; } - buildAppSettings = (dbSettings: Settings) => { + buildAppSettings = async (dbSettings: Settings) => { const sanitizedSettings: Record = { ...dbSettings }; delete sanitizedSettings.version; delete sanitizedSettings.jwtSecret; + const globalProxy = + dbSettings.globalProxyEnabled && dbSettings.globalProxyId ? await this.proxiesService.getProxySummary(dbSettings.globalProxyId) : null; const returnSettings: Record = { pagespeedKeySet: false, emailPasswordSet: false, + globalProxy, settings: null, }; @@ -46,7 +52,7 @@ class SettingsController implements ISettingsController { getAppSettings = catchAsync(async (req: Request, res: Response) => { const dbSettings = await this.settingsService.getDBSettings(); - const returnSettings = this.buildAppSettings(dbSettings); + const returnSettings = await this.buildAppSettings(dbSettings); return res.status(200).json({ success: true, msg: "App settings fetched successfully", @@ -57,8 +63,15 @@ class SettingsController implements ISettingsController { updateAppSettings = catchAsync(async (req: Request, res: Response) => { const validatedBody = updateAppSettingsBodyValidation.parse(req.body); + if (validatedBody.globalProxyId) { + const proxy = await this.proxiesService.getProxySummary(validatedBody.globalProxyId); + if (!proxy) { + throw new AppError({ message: "Referenced proxy does not exist", status: 422 }); + } + } + const updatedSettings = await this.settingsService.updateDbSettings(validatedBody); - const returnSettings = this.buildAppSettings(updatedSettings); + const returnSettings = await this.buildAppSettings(updatedSettings); return res.status(200).json({ success: true, msg: "App settings updated successfully", diff --git a/server/src/config/controllers.ts b/server/src/config/controllers.ts index 4f683babf..7787a389e 100644 --- a/server/src/config/controllers.ts +++ b/server/src/config/controllers.ts @@ -36,7 +36,7 @@ export const initializeControllers = (apiServices: ApiServices): InitializedCont return { authController: new AuthController(apiServices.userService), monitorController: new MonitorController(apiServices.monitorService, apiServices.notificationsService), - settingsController: new SettingsController(apiServices.settingsService, apiServices.emailService), + settingsController: new SettingsController(apiServices.settingsService, apiServices.emailService, apiServices.proxiesService), checkController: new CheckController(apiServices.checkService), geoCheckController: new GeoCheckController(apiServices.geoChecksService), inviteController: new InviteController(apiServices.inviteService), diff --git a/server/src/domain/proxies/proxy.service.ts b/server/src/domain/proxies/proxy.service.ts index a1c726f1e..9758f2856 100644 --- a/server/src/domain/proxies/proxy.service.ts +++ b/server/src/domain/proxies/proxy.service.ts @@ -1,4 +1,4 @@ -import { Proxy, ProxyResponse } from "@/domain/proxies/proxy.type.js"; +import { Proxy, ProxyResponse, ProxySummary } from "@/domain/proxies/proxy.type.js"; import { IMonitorsRepository } from "@/domain/monitors/monitor.repository.interface.js"; import { IProxiesRepository } from "@/domain/proxies/proxy.repository.interface.js"; import { AppError } from "@/utils/AppError.js"; @@ -8,6 +8,7 @@ export interface IProxiesService { createProxy(proxy: Partial, teamId: string): Promise; getProxy(proxyId: string, teamId: string): Promise; getProxies(): Promise; + getProxySummary(proxyId: string): Promise; getProxiesByTeamId(teamId: string): Promise; updateProxy(proxyId: string, teamId: string, patch: Partial & { clearPassword?: boolean; clearUsername?: boolean }): Promise; deleteProxy(proxyId: string, teamId: string): Promise; @@ -47,6 +48,16 @@ export class ProxiesService implements IProxiesService { }); return clean; }; + + getProxySummary = async (proxyId: string): Promise => { + const proxy = await this.proxiesRepository.findByIdOrNull(proxyId); + if (!proxy) { + return null; + } + const { id, name, host, port } = proxy; + return { id, name, host, port }; + }; + getProxiesByTeamId = async (teamId: string): Promise => { const raw = await this.proxiesRepository.findByTeamId(teamId); const clean = raw.map((r) => { diff --git a/server/src/domain/proxies/proxy.type.ts b/server/src/domain/proxies/proxy.type.ts index dd098dee3..c16b0c28c 100644 --- a/server/src/domain/proxies/proxy.type.ts +++ b/server/src/domain/proxies/proxy.type.ts @@ -15,3 +15,5 @@ export interface Proxy { } export type ProxyResponse = Omit & { hasPassword: boolean }; + +export type ProxySummary = Pick;