feat(cloud): Add custom cloud API URL configuration support (#181)

* feat(cloud): Add custom cloud API URL configuration support

- Implement RPC methods to set, get, and reset cloud URL
- Update cloud registration to remove hardcoded cloud API URL
- Modify UI to allow configuring custom cloud API URL in developer settings
- Remove environment-specific cloud configuration files
- Simplify cloud URL configuration in UI config

* fix(ui): Update cloud app URL to production environment in device mode

* refactor(ui): Remove SIGNAL_API env & Rename to DEVICE_API to make clear distinction between  CLOUD_API and DEVICE_API.

* feat(ui): Only show Cloud API URL Change on device mode

* fix(cloud): Don't override the CloudURL on deregistration from the cloud.
This commit is contained in:
Adam Shiervani
2025-02-25 16:10:46 +01:00
committed by GitHub
parent de5403eada
commit 7304e6b672
22 changed files with 201 additions and 113 deletions

View File

@@ -1,6 +1,6 @@
import { LoaderFunctionArgs, redirect } from "react-router-dom";
import api from "../api";
import { CLOUD_API, CLOUD_APP, SIGNAL_API } from "@/ui.config";
import { CLOUD_APP, DEVICE_API } from "@/ui.config";
const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
@@ -11,15 +11,11 @@ const loader = async ({ request }: LoaderFunctionArgs) => {
const oidcGoogle = searchParams.get("oidcGoogle");
const clientId = searchParams.get("clientId");
const res = await api.POST(
`${SIGNAL_API}/cloud/register`,
{
token: tempToken,
cloudApi: CLOUD_API,
oidcGoogle,
clientId,
},
);
const res = await api.POST(`${DEVICE_API}/cloud/register`, {
token: tempToken,
oidcGoogle,
clientId,
});
if (!res.ok) throw new Error("Failed to register device");
return redirect(CLOUD_APP + `/devices/${deviceId}/setup`);

View File

@@ -36,7 +36,7 @@ import { DeviceStatus } from "./welcome-local";
import FocusTrap from "focus-trap-react";
import OtherSessionConnectedModal from "@/components/OtherSessionConnectedModal";
import Terminal from "@components/Terminal";
import { CLOUD_API, SIGNAL_API } from "@/ui.config";
import { CLOUD_API, DEVICE_API } from "@/ui.config";
interface LocalLoaderResp {
authMode: "password" | "noPassword" | null;
@@ -57,12 +57,12 @@ export interface LocalDevice {
const deviceLoader = async () => {
const res = await api
.GET(`${SIGNAL_API}/device/status`)
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>);
if (!res.isSetup) return redirect("/welcome");
const deviceRes = await api.GET(`${SIGNAL_API}/device`);
const deviceRes = await api.GET(`${DEVICE_API}/device`);
if (deviceRes.status === 401) return redirect("/login-local");
if (deviceRes.ok) {
const device = (await deviceRes.json()) as LocalDevice;
@@ -78,9 +78,7 @@ const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> =>
const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`);
const iceConfig = await iceResp.json();
const deviceResp = await api.GET(
`${CLOUD_API}/devices/${params.id}`,
);
const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`);
if (!deviceResp.ok) {
if (deviceResp.status === 404) {
@@ -143,7 +141,11 @@ export default function KvmIdRoute() {
try {
const sd = btoa(JSON.stringify(pc.localDescription));
const res = await api.POST(`${SIGNAL_API}/webrtc/session`, {
const sessionUrl = isOnDevice
? `${DEVICE_API}/webrtc/session`
: `${CLOUD_API}/webrtc/session`;
const res = await api.POST(sessionUrl, {
sd,
// When on device, we don't need to specify the device id, as it's already known
...(isOnDevice ? {} : { id: params.id }),

View File

@@ -12,16 +12,16 @@ import LogoWhiteIcon from "@/assets/logo-white.svg";
import api from "../api";
import { DeviceStatus } from "./welcome-local";
import ExtLink from "../components/ExtLink";
import { SIGNAL_API } from "@/ui.config";
import { DEVICE_API } from "@/ui.config";
const loader = async () => {
const res = await api
.GET(`${SIGNAL_API}/device/status`)
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>);
if (!res.isSetup) return redirect("/welcome");
const deviceRes = await api.GET(`${SIGNAL_API}/device`);
const deviceRes = await api.GET(`${DEVICE_API}/device`);
if (deviceRes.ok) return redirect("/");
return null;
};
@@ -32,7 +32,7 @@ const action = async ({ request }: ActionFunctionArgs) => {
try {
const response = await api.POST(
`${SIGNAL_API}/auth/login-local`,
`${DEVICE_API}/auth/login-local`,
{
password,
},

View File

@@ -9,11 +9,11 @@ import LogoWhiteIcon from "@/assets/logo-white.svg";
import { cx } from "../cva.config";
import api from "../api";
import { DeviceStatus } from "./welcome-local";
import { SIGNAL_API } from "@/ui.config";
import { DEVICE_API } from "@/ui.config";
const loader = async () => {
const res = await api
.GET(`${SIGNAL_API}/device/status`)
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>);
if (res.isSetup) return redirect("/login-local");
@@ -31,7 +31,7 @@ const action = async ({ request }: ActionFunctionArgs) => {
if (localAuthMode === "noPassword") {
try {
await api.POST(`${SIGNAL_API}/device/setup`, {
await api.POST(`${DEVICE_API}/device/setup`, {
localAuthMode,
});
return redirect("/");

View File

@@ -10,11 +10,11 @@ import LogoBlueIcon from "@/assets/logo-blue.png";
import LogoWhiteIcon from "@/assets/logo-white.svg";
import api from "../api";
import { DeviceStatus } from "./welcome-local";
import { SIGNAL_API } from "@/ui.config";
import { DEVICE_API } from "@/ui.config";
const loader = async () => {
const res = await api
.GET(`${SIGNAL_API}/device/status`)
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>);
if (res.isSetup) return redirect("/login-local");
@@ -31,7 +31,7 @@ const action = async ({ request }: ActionFunctionArgs) => {
}
try {
const response = await api.POST(`${SIGNAL_API}/device/setup`, {
const response = await api.POST(`${DEVICE_API}/device/setup`, {
localAuthMode: "password",
password,
});

View File

@@ -9,7 +9,7 @@ import LogoMark from "@/assets/logo-mark.png";
import { cx } from "cva";
import api from "../api";
import { redirect } from "react-router-dom";
import { SIGNAL_API } from "@/ui.config";
import { DEVICE_API } from "@/ui.config";
export interface DeviceStatus {
isSetup: boolean;
@@ -17,7 +17,7 @@ export interface DeviceStatus {
const loader = async () => {
const res = await api
.GET(`${SIGNAL_API}/device/status`)
.GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>);
if (res.isSetup) return redirect("/login-local");