feat(gateway): refactor gateway form and connection check logic

This commit is contained in:
Abhimanyu Saharan
2026-02-08 23:26:57 +05:30
parent cdda147feb
commit 03317f0baf
4 changed files with 341 additions and 378 deletions

View File

@@ -6,11 +6,9 @@ import { useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
import { ApiError } from "@/api/mutator";
import {
gatewaysStatusApiV1GatewaysStatusGet,
type getGatewayApiV1GatewaysGatewayIdGetResponse,
useGetGatewayApiV1GatewaysGatewayIdGet,
useUpdateGatewayApiV1GatewaysGatewayIdPatch,
@@ -20,30 +18,17 @@ import {
useGetMyMembershipApiV1OrganizationsMeMemberGet,
} from "@/api/generated/organizations/organizations";
import type { GatewayUpdate } from "@/api/generated/model";
import { GatewayForm } from "@/components/gateways/GatewayForm";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
const DEFAULT_MAIN_SESSION_KEY = "agent:main:main";
const DEFAULT_WORKSPACE_ROOT = "~/.openclaw";
const validateGatewayUrl = (value: string) => {
const trimmed = value.trim();
if (!trimmed) return "Gateway URL is required.";
try {
const url = new URL(trimmed);
if (url.protocol !== "ws:" && url.protocol !== "wss:") {
return "Gateway URL must start with ws:// or wss://.";
}
if (!url.port) {
return "Gateway URL must include an explicit port.";
}
return null;
} catch {
return "Enter a valid gateway URL including port.";
}
};
import {
DEFAULT_MAIN_SESSION_KEY,
DEFAULT_WORKSPACE_ROOT,
checkGatewayConnection,
type GatewayCheckStatus,
validateGatewayUrl,
} from "@/lib/gateway-form";
export default function EditGatewayPage() {
const { isSignedIn } = useAuth();
@@ -81,9 +66,8 @@ export default function EditGatewayPage() {
);
const [gatewayUrlError, setGatewayUrlError] = useState<string | null>(null);
const [gatewayCheckStatus, setGatewayCheckStatus] = useState<
"idle" | "checking" | "success" | "error"
>("idle");
const [gatewayCheckStatus, setGatewayCheckStatus] =
useState<GatewayCheckStatus>("idle");
const [gatewayCheckMessage, setGatewayCheckMessage] = useState<string | null>(
null,
);
@@ -147,36 +131,13 @@ export default function EditGatewayPage() {
if (!isSignedIn) return;
setGatewayCheckStatus("checking");
setGatewayCheckMessage(null);
try {
const params: Record<string, string> = {
gateway_url: resolvedGatewayUrl.trim(),
};
if (resolvedGatewayToken.trim()) {
params.gateway_token = resolvedGatewayToken.trim();
}
if (resolvedMainSessionKey.trim()) {
params.gateway_main_session_key = resolvedMainSessionKey.trim();
}
const response = await gatewaysStatusApiV1GatewaysStatusGet(params);
if (response.status !== 200) {
setGatewayCheckStatus("error");
setGatewayCheckMessage("Unable to reach gateway.");
return;
}
const data = response.data;
if (!data.connected) {
setGatewayCheckStatus("error");
setGatewayCheckMessage(data.error ?? "Unable to reach gateway.");
return;
}
setGatewayCheckStatus("success");
setGatewayCheckMessage("Gateway reachable.");
} catch (err) {
setGatewayCheckStatus("error");
setGatewayCheckMessage(
err instanceof Error ? err.message : "Unable to reach gateway.",
);
}
const { ok, message } = await checkGatewayConnection({
gatewayUrl: resolvedGatewayUrl,
gatewayToken: resolvedGatewayToken,
mainSessionKey: resolvedMainSessionKey,
});
setGatewayCheckStatus(ok ? "success" : "error");
setGatewayCheckMessage(message);
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
@@ -253,139 +214,45 @@ export default function EditGatewayPage() {
Only organization owners and admins can edit gateways.
</div>
) : (
<form
<GatewayForm
name={resolvedName}
gatewayUrl={resolvedGatewayUrl}
gatewayToken={resolvedGatewayToken}
mainSessionKey={resolvedMainSessionKey}
workspaceRoot={resolvedWorkspaceRoot}
gatewayUrlError={gatewayUrlError}
gatewayCheckStatus={gatewayCheckStatus}
gatewayCheckMessage={gatewayCheckMessage}
errorMessage={errorMessage}
isLoading={isLoading}
canSubmit={canSubmit}
mainSessionKeyPlaceholder={DEFAULT_MAIN_SESSION_KEY}
workspaceRootPlaceholder={DEFAULT_WORKSPACE_ROOT}
cancelLabel="Back"
submitLabel="Save changes"
submitBusyLabel="Saving…"
onSubmit={handleSubmit}
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway name <span className="text-red-500">*</span>
</label>
<Input
value={resolvedName}
onChange={(event) => setName(event.target.value)}
placeholder="Primary gateway"
disabled={isLoading}
/>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway URL <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
value={resolvedGatewayUrl}
onChange={(event) => {
setGatewayUrl(event.target.value);
setGatewayUrlError(null);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onBlur={runGatewayCheck}
placeholder="ws://gateway:18789"
disabled={isLoading}
className={
gatewayUrlError ? "border-red-500" : undefined
}
/>
<button
type="button"
onClick={runGatewayCheck}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
aria-label="Check gateway connection"
>
{gatewayCheckStatus === "checking" ? (
<RefreshCcw className="h-4 w-4 animate-spin" />
) : gatewayCheckStatus === "success" ? (
<CheckCircle2 className="h-4 w-4 text-emerald-500" />
) : gatewayCheckStatus === "error" ? (
<XCircle className="h-4 w-4 text-red-500" />
) : (
<RefreshCcw className="h-4 w-4" />
)}
</button>
</div>
{gatewayUrlError ? (
<p className="text-xs text-red-500">{gatewayUrlError}</p>
) : gatewayCheckMessage ? (
<p
className={
gatewayCheckStatus === "success"
? "text-xs text-emerald-600"
: "text-xs text-red-500"
}
>
{gatewayCheckMessage}
</p>
) : null}
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway token
</label>
<Input
value={resolvedGatewayToken}
onChange={(event) => {
setGatewayToken(event.target.value);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onBlur={runGatewayCheck}
placeholder="Bearer token"
disabled={isLoading}
/>
</div>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Main session key <span className="text-red-500">*</span>
</label>
<Input
value={resolvedMainSessionKey}
onChange={(event) => {
setMainSessionKey(event.target.value);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
placeholder={DEFAULT_MAIN_SESSION_KEY}
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Workspace root <span className="text-red-500">*</span>
</label>
<Input
value={resolvedWorkspaceRoot}
onChange={(event) => setWorkspaceRoot(event.target.value)}
placeholder={DEFAULT_WORKSPACE_ROOT}
disabled={isLoading}
/>
</div>
</div>
{errorMessage ? (
<p className="text-sm text-red-500">{errorMessage}</p>
) : null}
<div className="flex justify-end gap-3">
<Button
type="button"
variant="ghost"
onClick={() => router.push("/gateways")}
disabled={isLoading}
>
Back
</Button>
<Button type="submit" disabled={isLoading || !canSubmit}>
{isLoading ? "Saving…" : "Save changes"}
</Button>
</div>
</form>
onCancel={() => router.push("/gateways")}
onRunGatewayCheck={runGatewayCheck}
onNameChange={setName}
onGatewayUrlChange={(next) => {
setGatewayUrl(next);
setGatewayUrlError(null);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onGatewayTokenChange={(next) => {
setGatewayToken(next);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onMainSessionKeyChange={(next) => {
setMainSessionKey(next);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onWorkspaceRootChange={setWorkspaceRoot}
/>
)}
</div>
</main>

View File

@@ -6,41 +6,24 @@ import { useState } from "react";
import { useRouter } from "next/navigation";
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
import { ApiError } from "@/api/mutator";
import {
gatewaysStatusApiV1GatewaysStatusGet,
useCreateGatewayApiV1GatewaysPost,
} from "@/api/generated/gateways/gateways";
import { useCreateGatewayApiV1GatewaysPost } from "@/api/generated/gateways/gateways";
import {
type getMyMembershipApiV1OrganizationsMeMemberGetResponse,
useGetMyMembershipApiV1OrganizationsMeMemberGet,
} from "@/api/generated/organizations/organizations";
import { GatewayForm } from "@/components/gateways/GatewayForm";
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
const DEFAULT_MAIN_SESSION_KEY = "agent:main:main";
const DEFAULT_WORKSPACE_ROOT = "~/.openclaw";
const validateGatewayUrl = (value: string) => {
const trimmed = value.trim();
if (!trimmed) return "Gateway URL is required.";
try {
const url = new URL(trimmed);
if (url.protocol !== "ws:" && url.protocol !== "wss:") {
return "Gateway URL must start with ws:// or wss://.";
}
if (!url.port) {
return "Gateway URL must include an explicit port.";
}
return null;
} catch {
return "Enter a valid gateway URL including port.";
}
};
import {
DEFAULT_MAIN_SESSION_KEY,
DEFAULT_WORKSPACE_ROOT,
checkGatewayConnection,
type GatewayCheckStatus,
validateGatewayUrl,
} from "@/lib/gateway-form";
export default function NewGatewayPage() {
const { isSignedIn } = useAuth();
@@ -69,9 +52,8 @@ export default function NewGatewayPage() {
const [workspaceRoot, setWorkspaceRoot] = useState(DEFAULT_WORKSPACE_ROOT);
const [gatewayUrlError, setGatewayUrlError] = useState<string | null>(null);
const [gatewayCheckStatus, setGatewayCheckStatus] = useState<
"idle" | "checking" | "success" | "error"
>("idle");
const [gatewayCheckStatus, setGatewayCheckStatus] =
useState<GatewayCheckStatus>("idle");
const [gatewayCheckMessage, setGatewayCheckMessage] = useState<string | null>(
null,
);
@@ -111,37 +93,13 @@ export default function NewGatewayPage() {
if (!isSignedIn) return;
setGatewayCheckStatus("checking");
setGatewayCheckMessage(null);
try {
const params: Record<string, string> = {
gateway_url: gatewayUrl.trim(),
};
if (gatewayToken.trim()) {
params.gateway_token = gatewayToken.trim();
}
if (mainSessionKey.trim()) {
params.gateway_main_session_key = mainSessionKey.trim();
}
const response = await gatewaysStatusApiV1GatewaysStatusGet(params);
if (response.status !== 200) {
setGatewayCheckStatus("error");
setGatewayCheckMessage("Unable to reach gateway.");
return;
}
const data = response.data;
if (!data.connected) {
setGatewayCheckStatus("error");
setGatewayCheckMessage(data.error ?? "Unable to reach gateway.");
return;
}
setGatewayCheckStatus("success");
setGatewayCheckMessage("Gateway reachable.");
} catch (err) {
setGatewayCheckStatus("error");
setGatewayCheckMessage(
err instanceof Error ? err.message : "Unable to reach gateway.",
);
}
const { ok, message } = await checkGatewayConnection({
gatewayUrl,
gatewayToken,
mainSessionKey,
});
setGatewayCheckStatus(ok ? "success" : "error");
setGatewayCheckMessage(message);
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
@@ -214,137 +172,45 @@ export default function NewGatewayPage() {
Only organization owners and admins can create gateways.
</div>
) : (
<form
<GatewayForm
name={name}
gatewayUrl={gatewayUrl}
gatewayToken={gatewayToken}
mainSessionKey={mainSessionKey}
workspaceRoot={workspaceRoot}
gatewayUrlError={gatewayUrlError}
gatewayCheckStatus={gatewayCheckStatus}
gatewayCheckMessage={gatewayCheckMessage}
errorMessage={error}
isLoading={isLoading}
canSubmit={canSubmit}
mainSessionKeyPlaceholder={DEFAULT_MAIN_SESSION_KEY}
workspaceRootPlaceholder={DEFAULT_WORKSPACE_ROOT}
cancelLabel="Cancel"
submitLabel="Create gateway"
submitBusyLabel="Creating…"
onSubmit={handleSubmit}
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway name <span className="text-red-500">*</span>
</label>
<Input
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="Primary gateway"
disabled={isLoading}
/>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway URL <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
value={gatewayUrl}
onChange={(event) => {
setGatewayUrl(event.target.value);
setGatewayUrlError(null);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onBlur={runGatewayCheck}
placeholder="ws://gateway:18789"
disabled={isLoading}
className={
gatewayUrlError ? "border-red-500" : undefined
}
/>
<button
type="button"
onClick={runGatewayCheck}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
aria-label="Check gateway connection"
>
{gatewayCheckStatus === "checking" ? (
<RefreshCcw className="h-4 w-4 animate-spin" />
) : gatewayCheckStatus === "success" ? (
<CheckCircle2 className="h-4 w-4 text-emerald-500" />
) : gatewayCheckStatus === "error" ? (
<XCircle className="h-4 w-4 text-red-500" />
) : (
<RefreshCcw className="h-4 w-4" />
)}
</button>
</div>
{gatewayUrlError ? (
<p className="text-xs text-red-500">{gatewayUrlError}</p>
) : gatewayCheckMessage ? (
<p
className={
gatewayCheckStatus === "success"
? "text-xs text-emerald-600"
: "text-xs text-red-500"
}
>
{gatewayCheckMessage}
</p>
) : null}
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway token
</label>
<Input
value={gatewayToken}
onChange={(event) => {
setGatewayToken(event.target.value);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onBlur={runGatewayCheck}
placeholder="Bearer token"
disabled={isLoading}
/>
</div>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Main session key <span className="text-red-500">*</span>
</label>
<Input
value={mainSessionKey}
onChange={(event) => {
setMainSessionKey(event.target.value);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
placeholder={DEFAULT_MAIN_SESSION_KEY}
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Workspace root <span className="text-red-500">*</span>
</label>
<Input
value={workspaceRoot}
onChange={(event) => setWorkspaceRoot(event.target.value)}
placeholder={DEFAULT_WORKSPACE_ROOT}
disabled={isLoading}
/>
</div>
</div>
{error ? <p className="text-sm text-red-500">{error}</p> : null}
<div className="flex justify-end gap-3">
<Button
type="button"
variant="ghost"
onClick={() => router.push("/gateways")}
disabled={isLoading}
>
Cancel
</Button>
<Button type="submit" disabled={isLoading || !canSubmit}>
{isLoading ? "Creating…" : "Create gateway"}
</Button>
</div>
</form>
onCancel={() => router.push("/gateways")}
onRunGatewayCheck={runGatewayCheck}
onNameChange={setName}
onGatewayUrlChange={(next) => {
setGatewayUrl(next);
setGatewayUrlError(null);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onGatewayTokenChange={(next) => {
setGatewayToken(next);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onMainSessionKeyChange={(next) => {
setMainSessionKey(next);
setGatewayCheckStatus("idle");
setGatewayCheckMessage(null);
}}
onWorkspaceRootChange={setWorkspaceRoot}
/>
)}
</div>
</main>

View File

@@ -0,0 +1,174 @@
import type { FormEvent } from "react";
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
import type { GatewayCheckStatus } from "@/lib/gateway-form";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
type GatewayFormProps = {
name: string;
gatewayUrl: string;
gatewayToken: string;
mainSessionKey: string;
workspaceRoot: string;
gatewayUrlError: string | null;
gatewayCheckStatus: GatewayCheckStatus;
gatewayCheckMessage: string | null;
errorMessage: string | null;
isLoading: boolean;
canSubmit: boolean;
mainSessionKeyPlaceholder: string;
workspaceRootPlaceholder: string;
cancelLabel: string;
submitLabel: string;
submitBusyLabel: string;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
onCancel: () => void;
onRunGatewayCheck: () => Promise<void>;
onNameChange: (next: string) => void;
onGatewayUrlChange: (next: string) => void;
onGatewayTokenChange: (next: string) => void;
onMainSessionKeyChange: (next: string) => void;
onWorkspaceRootChange: (next: string) => void;
};
export function GatewayForm({
name,
gatewayUrl,
gatewayToken,
mainSessionKey,
workspaceRoot,
gatewayUrlError,
gatewayCheckStatus,
gatewayCheckMessage,
errorMessage,
isLoading,
canSubmit,
mainSessionKeyPlaceholder,
workspaceRootPlaceholder,
cancelLabel,
submitLabel,
submitBusyLabel,
onSubmit,
onCancel,
onRunGatewayCheck,
onNameChange,
onGatewayUrlChange,
onGatewayTokenChange,
onMainSessionKeyChange,
onWorkspaceRootChange,
}: GatewayFormProps) {
return (
<form
onSubmit={onSubmit}
className="space-y-6 rounded-xl border border-slate-200 bg-white p-6 shadow-sm"
>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway name <span className="text-red-500">*</span>
</label>
<Input
value={name}
onChange={(event) => onNameChange(event.target.value)}
placeholder="Primary gateway"
disabled={isLoading}
/>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway URL <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
value={gatewayUrl}
onChange={(event) => onGatewayUrlChange(event.target.value)}
onBlur={onRunGatewayCheck}
placeholder="ws://gateway:18789"
disabled={isLoading}
className={gatewayUrlError ? "border-red-500" : undefined}
/>
<button
type="button"
onClick={() => void onRunGatewayCheck()}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
aria-label="Check gateway connection"
>
{gatewayCheckStatus === "checking" ? (
<RefreshCcw className="h-4 w-4 animate-spin" />
) : gatewayCheckStatus === "success" ? (
<CheckCircle2 className="h-4 w-4 text-emerald-500" />
) : gatewayCheckStatus === "error" ? (
<XCircle className="h-4 w-4 text-red-500" />
) : (
<RefreshCcw className="h-4 w-4" />
)}
</button>
</div>
{gatewayUrlError ? (
<p className="text-xs text-red-500">{gatewayUrlError}</p>
) : gatewayCheckMessage ? (
<p
className={
gatewayCheckStatus === "success"
? "text-xs text-emerald-600"
: "text-xs text-red-500"
}
>
{gatewayCheckMessage}
</p>
) : null}
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Gateway token
</label>
<Input
value={gatewayToken}
onChange={(event) => onGatewayTokenChange(event.target.value)}
onBlur={onRunGatewayCheck}
placeholder="Bearer token"
disabled={isLoading}
/>
</div>
</div>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Main session key <span className="text-red-500">*</span>
</label>
<Input
value={mainSessionKey}
onChange={(event) => onMainSessionKeyChange(event.target.value)}
placeholder={mainSessionKeyPlaceholder}
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Workspace root <span className="text-red-500">*</span>
</label>
<Input
value={workspaceRoot}
onChange={(event) => onWorkspaceRootChange(event.target.value)}
placeholder={workspaceRootPlaceholder}
disabled={isLoading}
/>
</div>
</div>
{errorMessage ? <p className="text-sm text-red-500">{errorMessage}</p> : null}
<div className="flex justify-end gap-3">
<Button type="button" variant="ghost" onClick={onCancel} disabled={isLoading}>
{cancelLabel}
</Button>
<Button type="submit" disabled={isLoading || !canSubmit}>
{isLoading ? submitBusyLabel : submitLabel}
</Button>
</div>
</form>
);
}

View File

@@ -0,0 +1,56 @@
import { gatewaysStatusApiV1GatewaysStatusGet } from "@/api/generated/gateways/gateways";
export const DEFAULT_MAIN_SESSION_KEY = "agent:main:main";
export const DEFAULT_WORKSPACE_ROOT = "~/.openclaw";
export type GatewayCheckStatus = "idle" | "checking" | "success" | "error";
export const validateGatewayUrl = (value: string) => {
const trimmed = value.trim();
if (!trimmed) return "Gateway URL is required.";
try {
const url = new URL(trimmed);
if (url.protocol !== "ws:" && url.protocol !== "wss:") {
return "Gateway URL must start with ws:// or wss://.";
}
if (!url.port) {
return "Gateway URL must include an explicit port.";
}
return null;
} catch {
return "Enter a valid gateway URL including port.";
}
};
export async function checkGatewayConnection(params: {
gatewayUrl: string;
gatewayToken: string;
mainSessionKey: string;
}): Promise<{ ok: boolean; message: string }> {
try {
const requestParams: Record<string, string> = {
gateway_url: params.gatewayUrl.trim(),
};
if (params.gatewayToken.trim()) {
requestParams.gateway_token = params.gatewayToken.trim();
}
if (params.mainSessionKey.trim()) {
requestParams.gateway_main_session_key = params.mainSessionKey.trim();
}
const response = await gatewaysStatusApiV1GatewaysStatusGet(requestParams);
if (response.status !== 200) {
return { ok: false, message: "Unable to reach gateway." };
}
const data = response.data;
if (!data.connected) {
return { ok: false, message: data.error ?? "Unable to reach gateway." };
}
return { ok: true, message: "Gateway reachable." };
} catch (error) {
return {
ok: false,
message: error instanceof Error ? error.message : "Unable to reach gateway.",
};
}
}