mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
feat: redesign user host defaults system
This commit is contained in:
1 parent
3028ab1176
commit
a4a4d4f27b
144 files changed
+24609
-2555
No files matched your search
@@ -323,7 +323,9 @@
|
||||
"enableToolbar": {
|
||||
"label": "Remote desktop toolbar",
|
||||
"description": "Show the toolbar in RDP, VNC and Telnet sessions."
|
||||
}
|
||||
},
|
||||
"displayGroup": "Display",
|
||||
"displayDefaultsTab": "Remote Desktop"
|
||||
}
|
||||
},
|
||||
"palette": {
|
||||
|
||||
@@ -150,7 +150,8 @@
|
||||
"labelKey": "settings.user.colorDepth32"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "resizeMethod",
|
||||
@@ -172,7 +173,8 @@
|
||||
"labelKey": "settings.user.resizeReconnect"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "forceLossless",
|
||||
@@ -194,7 +196,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "enableWallpaper",
|
||||
@@ -216,7 +219,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "enableFontSmoothing",
|
||||
@@ -238,7 +242,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "enableDesktopComposition",
|
||||
@@ -260,7 +265,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "disableAudio",
|
||||
@@ -282,7 +288,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "enablePrinting",
|
||||
@@ -304,7 +311,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "enableDrive",
|
||||
@@ -326,7 +334,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "disableCopy",
|
||||
@@ -348,7 +357,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "disablePaste",
|
||||
@@ -370,7 +380,8 @@
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit"
|
||||
"default": "inherit",
|
||||
"hidden": true
|
||||
}
|
||||
],
|
||||
"host": {
|
||||
@@ -380,21 +391,24 @@
|
||||
"type": "boolean",
|
||||
"labelKey": "settings.host.enableRdp.label",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "enableVnc",
|
||||
"type": "boolean",
|
||||
"labelKey": "settings.host.enableVnc.label",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "enableTelnet",
|
||||
"type": "boolean",
|
||||
"labelKey": "settings.host.enableTelnet.label",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "rdpPort",
|
||||
@@ -403,7 +417,8 @@
|
||||
"min": 1,
|
||||
"max": 65535,
|
||||
"default": 3389,
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "vncPort",
|
||||
@@ -412,7 +427,8 @@
|
||||
"min": 1,
|
||||
"max": 65535,
|
||||
"default": 5900,
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "telnetPort",
|
||||
@@ -421,7 +437,8 @@
|
||||
"min": 1,
|
||||
"max": 65535,
|
||||
"default": 23,
|
||||
"hidden": true
|
||||
"hidden": true,
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "rdpSecurity",
|
||||
@@ -444,7 +461,8 @@
|
||||
"default": {},
|
||||
"hidden": true,
|
||||
"shareRead": "edit",
|
||||
"secretKeys": ["gateway-password"]
|
||||
"secretKeys": ["gateway-password"],
|
||||
"defaultable": false
|
||||
},
|
||||
{
|
||||
"key": "enableToolbar",
|
||||
@@ -453,6 +471,263 @@
|
||||
"descriptionKey": "settings.host.enableToolbar.description",
|
||||
"default": true,
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"key": "colorDepth",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.colorDepth.label",
|
||||
"descriptionKey": "settings.user.colorDepth.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "16",
|
||||
"labelKey": "settings.user.colorDepth16"
|
||||
},
|
||||
{
|
||||
"value": "24",
|
||||
"labelKey": "settings.user.colorDepth24"
|
||||
},
|
||||
{
|
||||
"value": "32",
|
||||
"labelKey": "settings.user.colorDepth32"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "resizeMethod",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.resizeMethod.label",
|
||||
"descriptionKey": "settings.user.resizeMethod.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "display-update",
|
||||
"labelKey": "settings.user.resizeDisplayUpdate"
|
||||
},
|
||||
{
|
||||
"value": "reconnect",
|
||||
"labelKey": "settings.user.resizeReconnect"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "forceLossless",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.forceLossless.label",
|
||||
"descriptionKey": "settings.user.forceLossless.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "enableWallpaper",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.enableWallpaper.label",
|
||||
"descriptionKey": "settings.user.enableWallpaper.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "enableFontSmoothing",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.enableFontSmoothing.label",
|
||||
"descriptionKey": "settings.user.enableFontSmoothing.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "enableDesktopComposition",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.enableDesktopComposition.label",
|
||||
"descriptionKey": "settings.user.enableDesktopComposition.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "disableAudio",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.disableAudio.label",
|
||||
"descriptionKey": "settings.user.disableAudio.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "enablePrinting",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.enablePrinting.label",
|
||||
"descriptionKey": "settings.user.enablePrinting.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "enableDrive",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.enableDrive.label",
|
||||
"descriptionKey": "settings.user.enableDrive.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "disableCopy",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.disableCopy.label",
|
||||
"descriptionKey": "settings.user.disableCopy.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
},
|
||||
{
|
||||
"key": "disablePaste",
|
||||
"type": "select",
|
||||
"labelKey": "settings.user.disablePaste.label",
|
||||
"descriptionKey": "settings.user.disablePaste.description",
|
||||
"group": "settings.host.displayGroup",
|
||||
"options": [
|
||||
{
|
||||
"value": "inherit",
|
||||
"labelKey": "settings.user.inherit"
|
||||
},
|
||||
{
|
||||
"value": "on",
|
||||
"labelKey": "settings.user.on"
|
||||
},
|
||||
{
|
||||
"value": "off",
|
||||
"labelKey": "settings.user.off"
|
||||
}
|
||||
],
|
||||
"default": "inherit",
|
||||
"personal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ export function hostPayloadLegacy(
|
||||
return Object.keys(out).length > 0 ? out : null;
|
||||
}
|
||||
|
||||
/** User setting key to the guacd parameter it sets. */
|
||||
/** Display field key to the guacd parameter it sets. */
|
||||
const USER_DEFAULT_PARAMS: Record<string, string> = {
|
||||
colorDepth: "color-depth",
|
||||
resizeMethod: "resize-method",
|
||||
@@ -193,8 +193,8 @@ const USER_DEFAULT_PARAMS: Record<string, string> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The user's RDP defaults as guacd parameters. "inherit" leaves a parameter
|
||||
* to the host and guacd; on and off become booleans.
|
||||
* Display settings as guacd parameters. "inherit" leaves a parameter to the
|
||||
* host and guacd; on and off become booleans.
|
||||
*/
|
||||
export function userDefaultParams(
|
||||
values: Record<string, unknown>,
|
||||
@@ -215,9 +215,22 @@ export function userDefaultParams(
|
||||
return params;
|
||||
}
|
||||
|
||||
export async function readUserDefaults(
|
||||
/**
|
||||
* The display settings a session runs with, as guacd parameters: the host's
|
||||
* as this user sees them (their own defaults while the host follows its
|
||||
* defaults), or with no host, this user's defaults.
|
||||
*/
|
||||
export async function readDisplayDefaults(
|
||||
ctx: PluginContext,
|
||||
userId: string,
|
||||
hostId?: number,
|
||||
): Promise<Record<string, unknown>> {
|
||||
return userDefaultParams(await ctx.settings.getAll("user", userId));
|
||||
const values: Record<string, unknown> = {};
|
||||
for (const key of Object.keys(USER_DEFAULT_PARAMS)) {
|
||||
values[key] =
|
||||
hostId === undefined
|
||||
? await ctx.settings.getHostDefault(userId, key)
|
||||
: await ctx.settings.getHostFor(hostId, userId, key);
|
||||
}
|
||||
return userDefaultParams(values);
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
PORT_KEY,
|
||||
isRemoteProtocol,
|
||||
readHostSettings,
|
||||
readUserDefaults,
|
||||
readDisplayDefaults,
|
||||
type RemoteProtocol,
|
||||
} from "./host-settings.js";
|
||||
import { errorMessage, type RemoteDesktopLogger } from "./log.js";
|
||||
@@ -203,7 +203,7 @@ export function registerRoutes(router: Router, deps: RouteDeps): void {
|
||||
const userId = ctx.currentActor();
|
||||
if (type === "rdp" && userId) {
|
||||
for (const [key, value] of Object.entries(
|
||||
await readUserDefaults(ctx, userId),
|
||||
await readDisplayDefaults(ctx, userId),
|
||||
)) {
|
||||
if (isServerOwnedSetting(key)) continue;
|
||||
if (options[key] === undefined) options[key] = value;
|
||||
@@ -319,9 +319,11 @@ export function registerRoutes(router: Router, deps: RouteDeps): void {
|
||||
});
|
||||
}
|
||||
|
||||
// The user's RDP defaults sit under whatever the host sets itself.
|
||||
// The display settings sit under whatever the host's guacd settings say.
|
||||
const { config, guacdOverrides } = cleanGuacConfig({
|
||||
...(protocol === "rdp" ? await readUserDefaults(ctx, userId) : {}),
|
||||
...(protocol === "rdp"
|
||||
? await readDisplayDefaults(ctx, userId, hostId)
|
||||
: {}),
|
||||
...settings.guacamoleConfig,
|
||||
});
|
||||
let guacConfig = config;
|
||||
@@ -545,7 +547,9 @@ export function registerRoutes(router: Router, deps: RouteDeps): void {
|
||||
.json({ error: "Protocol is not enabled for this host" });
|
||||
}
|
||||
const { guacdOverrides } = cleanGuacConfig({
|
||||
...(protocol === "rdp" ? await readUserDefaults(ctx, userId) : {}),
|
||||
...(protocol === "rdp"
|
||||
? await readDisplayDefaults(ctx, userId, hostId)
|
||||
: {}),
|
||||
...settings.guacamoleConfig,
|
||||
});
|
||||
guacd = {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from "@termix/plugin-sdk/frontend";
|
||||
import {
|
||||
FakeSwitch,
|
||||
HostFeatureFields,
|
||||
SectionCard,
|
||||
SettingRow,
|
||||
isElectron,
|
||||
@@ -166,6 +167,7 @@ function ToolbarCard({
|
||||
<SettingRow
|
||||
label={t("settings.host.enableToolbar.label")}
|
||||
description={t("settings.host.enableToolbar.description")}
|
||||
defaultKey="enableToolbar"
|
||||
>
|
||||
<FakeSwitch
|
||||
checked={form.enableToolbar}
|
||||
@@ -254,6 +256,27 @@ function TelnetSection(props: HostEditorSectionProps) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The defaults editor's remote desktop section: the display settings and the
|
||||
* toolbar, which every host follows unless its own guacd settings say
|
||||
* otherwise. A host edits these in its protocol tabs instead.
|
||||
*/
|
||||
function DisplayDefaultsSection(props: HostEditorSectionProps) {
|
||||
const { t } = useTranslation();
|
||||
const { form, setField } = remoteDesktopForm(props);
|
||||
return (
|
||||
<>
|
||||
<SectionCard
|
||||
title={t("settings.host.displayGroup")}
|
||||
icon={<Monitor className="size-3.5" />}
|
||||
>
|
||||
<HostFeatureFields form={props.form} updateForm={props.updateForm} />
|
||||
</SectionCard>
|
||||
<ToolbarCard form={form} setField={setField} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const SECTIONS: Record<Protocol, ComponentType<HostEditorSectionProps>> = {
|
||||
rdp: RdpSection,
|
||||
vnc: VncSection,
|
||||
@@ -343,6 +366,16 @@ function registerHostSurfaces(app: TermixApp): void {
|
||||
});
|
||||
}
|
||||
|
||||
app.registerHostEditorSection({
|
||||
id: "remote-desktop-display",
|
||||
group: "top",
|
||||
titleKey: "settings.host.displayDefaultsTab",
|
||||
icon: Monitor,
|
||||
order: 40,
|
||||
defaults: "only",
|
||||
component: DisplayDefaultsSection,
|
||||
});
|
||||
|
||||
if (isElectron()) registerNativeRdp(app);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,14 +149,15 @@ describe("POST /connect-host/:hostId", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("puts the user's RDP defaults under the host's own values", async () => {
|
||||
it("puts the host's display settings under its own guacd settings", async () => {
|
||||
server = await start({ protocolTargets: { "7:rdp": target() } });
|
||||
await server.mock.ctx.settings.setHost(7, "enableRdp", true);
|
||||
await server.mock.ctx.settings.setHost(7, "guacamoleConfig", {
|
||||
"color-depth": 32,
|
||||
});
|
||||
await server.mock.ctx.settings.setUser("user-1", "colorDepth", "16");
|
||||
await server.mock.ctx.settings.setUser("user-1", "disableCopy", "on");
|
||||
// What the host follows from its defaults.
|
||||
await server.mock.ctx.settings.setHost(7, "colorDepth", "16");
|
||||
await server.mock.ctx.settings.setHost(7, "disableCopy", "on");
|
||||
|
||||
const response = await server.request("POST", "/connect-host/7", {
|
||||
protocol: "rdp",
|
||||
|
||||
Reference in new issue
Block a user