mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-17 21:27:16 +02:00
322 lines
9.8 KiB
TypeScript
322 lines
9.8 KiB
TypeScript
import type {
|
|
Folder,
|
|
GrpcRequest,
|
|
HttpRequest,
|
|
InheritedBoolSetting,
|
|
InheritedIntSetting,
|
|
WebsocketRequest,
|
|
Workspace,
|
|
} from "@yaakapp-internal/models";
|
|
import { patchModel } from "@yaakapp-internal/models";
|
|
import { useModelAncestors } from "../hooks/useModelAncestors";
|
|
import { Checkbox } from "./core/Checkbox";
|
|
import { PlainInput } from "./core/PlainInput";
|
|
import {
|
|
SettingOverrideRow,
|
|
SettingRowBoolean,
|
|
SettingRowNumber,
|
|
SettingsList,
|
|
SettingsSection,
|
|
} from "./core/SettingRow";
|
|
|
|
interface Props {
|
|
showSectionTitles?: boolean;
|
|
model: ModelWithSettings;
|
|
}
|
|
|
|
type ModelWithSettings = Workspace | Folder | HttpRequest | WebsocketRequest | GrpcRequest;
|
|
type ModelWithHttpSettings = Workspace | Folder | HttpRequest;
|
|
type ModelWithCookieSettings = Workspace | Folder | HttpRequest | WebsocketRequest | GrpcRequest;
|
|
type BooleanSetting = boolean | InheritedBoolSetting;
|
|
type IntegerSetting = number | InheritedIntSetting;
|
|
type CookieSettingsPatch = {
|
|
settingSendCookies?: ModelWithCookieSettings["settingSendCookies"];
|
|
settingStoreCookies?: ModelWithCookieSettings["settingStoreCookies"];
|
|
};
|
|
type HttpSettingsPatch = {
|
|
settingValidateCertificates?: ModelWithHttpSettings["settingValidateCertificates"];
|
|
settingFollowRedirects?: ModelWithHttpSettings["settingFollowRedirects"];
|
|
settingRequestTimeout?: ModelWithHttpSettings["settingRequestTimeout"];
|
|
};
|
|
|
|
export function ModelSettingsEditor({ model, showSectionTitles = false }: Props) {
|
|
const ancestors = useModelAncestors(model);
|
|
const supportsHttpSettings =
|
|
model.model === "workspace" || model.model === "folder" || model.model === "http_request";
|
|
|
|
return (
|
|
<SettingsList className="space-y-8">
|
|
{supportsHttpSettings && (
|
|
<SettingsSection title={showSectionTitles ? "Requests" : null}>
|
|
<IntegerSettingRow
|
|
title="Request Timeout"
|
|
description="Maximum request duration in milliseconds. Set to 0 to disable the timeout."
|
|
name="settingRequestTimeout"
|
|
setting={model.settingRequestTimeout}
|
|
inheritedValue={resolveInheritedValue(
|
|
ancestors,
|
|
"settingRequestTimeout",
|
|
model.settingRequestTimeout,
|
|
)}
|
|
onChange={(settingRequestTimeout) =>
|
|
patchHttpSettings(model, {
|
|
settingRequestTimeout,
|
|
})
|
|
}
|
|
/>
|
|
<BooleanSettingRow
|
|
title="Validate TLS certificates"
|
|
description="When disabled, skip validation of server certificates."
|
|
setting={model.settingValidateCertificates}
|
|
inheritedValue={resolveInheritedValue(
|
|
ancestors,
|
|
"settingValidateCertificates",
|
|
model.settingValidateCertificates,
|
|
)}
|
|
onChange={(settingValidateCertificates) =>
|
|
patchHttpSettings(model, {
|
|
settingValidateCertificates,
|
|
})
|
|
}
|
|
/>
|
|
<BooleanSettingRow
|
|
title="Follow redirects"
|
|
description="Follow HTTP redirects automatically."
|
|
setting={model.settingFollowRedirects}
|
|
inheritedValue={resolveInheritedValue(
|
|
ancestors,
|
|
"settingFollowRedirects",
|
|
model.settingFollowRedirects,
|
|
)}
|
|
onChange={(settingFollowRedirects) =>
|
|
patchHttpSettings(model, {
|
|
settingFollowRedirects,
|
|
})
|
|
}
|
|
/>
|
|
</SettingsSection>
|
|
)}
|
|
<SettingsSection title={supportsHttpSettings || showSectionTitles ? "Cookies" : null}>
|
|
<BooleanSettingRow
|
|
title="Automatically send cookies"
|
|
description="Attach matching cookies from the active cookie jar to outgoing requests."
|
|
setting={model.settingSendCookies}
|
|
inheritedValue={resolveInheritedValue(
|
|
ancestors,
|
|
"settingSendCookies",
|
|
model.settingSendCookies,
|
|
)}
|
|
onChange={(settingSendCookies) =>
|
|
patchCookieSettings(model, {
|
|
settingSendCookies,
|
|
})
|
|
}
|
|
/>
|
|
<BooleanSettingRow
|
|
title="Automatically store cookies"
|
|
description="Save cookies from Set-Cookie response headers to the active cookie jar."
|
|
setting={model.settingStoreCookies}
|
|
inheritedValue={resolveInheritedValue(
|
|
ancestors,
|
|
"settingStoreCookies",
|
|
model.settingStoreCookies,
|
|
)}
|
|
onChange={(settingStoreCookies) =>
|
|
patchCookieSettings(model, {
|
|
settingStoreCookies,
|
|
})
|
|
}
|
|
/>
|
|
</SettingsSection>
|
|
</SettingsList>
|
|
);
|
|
}
|
|
|
|
export function countOverriddenSettings(model: ModelWithSettings) {
|
|
const settings: (BooleanSetting | IntegerSetting)[] = [
|
|
model.settingSendCookies,
|
|
model.settingStoreCookies,
|
|
];
|
|
|
|
if (model.model === "workspace" || model.model === "folder" || model.model === "http_request") {
|
|
settings.push(
|
|
model.settingValidateCertificates,
|
|
model.settingFollowRedirects,
|
|
model.settingRequestTimeout,
|
|
);
|
|
}
|
|
|
|
return settings.filter((setting) => isInheritedSetting(setting) && setting.enabled === true)
|
|
.length;
|
|
}
|
|
|
|
function patchCookieSettings(model: ModelWithCookieSettings, patch: Partial<CookieSettingsPatch>) {
|
|
if (model.model === "workspace") return patchModel(model, patch as Partial<Workspace>);
|
|
if (model.model === "folder") return patchModel(model, patch as Partial<Folder>);
|
|
if (model.model === "http_request") return patchModel(model, patch as Partial<HttpRequest>);
|
|
if (model.model === "websocket_request")
|
|
return patchModel(model, patch as Partial<WebsocketRequest>);
|
|
return patchModel(model, patch as Partial<GrpcRequest>);
|
|
}
|
|
|
|
function patchHttpSettings(model: ModelWithHttpSettings, patch: Partial<HttpSettingsPatch>) {
|
|
if (model.model === "workspace") return patchModel(model, patch as Partial<Workspace>);
|
|
if (model.model === "folder") return patchModel(model, patch as Partial<Folder>);
|
|
return patchModel(model, patch as Partial<HttpRequest>);
|
|
}
|
|
|
|
function BooleanSettingRow({
|
|
description,
|
|
inheritedValue,
|
|
setting,
|
|
title,
|
|
onChange,
|
|
}: {
|
|
description: string;
|
|
inheritedValue: boolean;
|
|
setting: BooleanSetting;
|
|
title: string;
|
|
onChange: (setting: BooleanSetting) => void;
|
|
}) {
|
|
const inherited = isInheritedSetting(setting);
|
|
const overridden = inherited ? setting.enabled === true : false;
|
|
const value = inherited ? (overridden ? setting.value : inheritedValue) : setting;
|
|
|
|
if (!inherited) {
|
|
return (
|
|
<SettingRowBoolean
|
|
checked={value}
|
|
title={title}
|
|
description={description}
|
|
onChange={(value) => onChange(value)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SettingOverrideRow
|
|
title={title}
|
|
description={description}
|
|
overridden={overridden}
|
|
onResetOverride={() => onChange({ ...setting, enabled: false })}
|
|
>
|
|
<Checkbox
|
|
hideLabel
|
|
size="md"
|
|
title={title}
|
|
checked={value}
|
|
onChange={(value) => onChange({ ...setting, enabled: true, value })}
|
|
/>
|
|
</SettingOverrideRow>
|
|
);
|
|
}
|
|
|
|
function IntegerSettingRow({
|
|
description,
|
|
inheritedValue,
|
|
name,
|
|
setting,
|
|
title,
|
|
onChange,
|
|
}: {
|
|
description: string;
|
|
inheritedValue: number;
|
|
name: string;
|
|
setting: IntegerSetting;
|
|
title: string;
|
|
onChange: (setting: IntegerSetting) => void;
|
|
}) {
|
|
const inherited = isInheritedSetting(setting);
|
|
const overridden = inherited ? setting.enabled === true : false;
|
|
const value = inherited ? (overridden ? setting.value : inheritedValue) : setting;
|
|
const showReset = overridden && value !== inheritedValue;
|
|
|
|
if (!inherited) {
|
|
return (
|
|
<SettingRowNumber
|
|
name={name}
|
|
title={title}
|
|
description={description}
|
|
value={value}
|
|
placeholder="0"
|
|
validate={(value) => value === "" || Number.parseInt(value, 10) >= 0}
|
|
onChange={(value) => onChange(value)}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SettingOverrideRow
|
|
title={title}
|
|
description={description}
|
|
overridden={showReset}
|
|
onResetOverride={() => onChange({ ...setting, enabled: false })}
|
|
>
|
|
<PlainInput
|
|
hideLabel
|
|
name={name}
|
|
label={title}
|
|
size="sm"
|
|
type="number"
|
|
placeholder="0"
|
|
defaultValue={`${value}`}
|
|
containerClassName="!w-48"
|
|
validate={(value) => value === "" || Number.parseInt(value, 10) >= 0}
|
|
onChange={(value) =>
|
|
onChange({
|
|
...setting,
|
|
enabled: true,
|
|
value: Number.parseInt(value, 10) || 0,
|
|
})
|
|
}
|
|
/>
|
|
</SettingOverrideRow>
|
|
);
|
|
}
|
|
|
|
function isInheritedSetting<T>(
|
|
setting: T | { enabled?: boolean; value: T },
|
|
): setting is { enabled?: boolean; value: T } {
|
|
return typeof setting === "object" && setting != null && "value" in setting;
|
|
}
|
|
|
|
function resolveInheritedValue(
|
|
ancestors: (Folder | Workspace)[],
|
|
key: "settingRequestTimeout",
|
|
fallback: IntegerSetting,
|
|
): number;
|
|
function resolveInheritedValue(
|
|
ancestors: (Folder | Workspace)[],
|
|
key: BooleanWorkspaceSettingKey,
|
|
fallback: BooleanSetting,
|
|
): boolean;
|
|
function resolveInheritedValue(
|
|
ancestors: (Folder | Workspace)[],
|
|
key: keyof WorkspaceSettings,
|
|
fallback: BooleanSetting | IntegerSetting,
|
|
) {
|
|
for (const ancestor of ancestors) {
|
|
const setting = ancestor[key] as BooleanSetting | IntegerSetting;
|
|
if (isInheritedSetting(setting)) {
|
|
if (setting.enabled === true) {
|
|
return setting.value;
|
|
}
|
|
continue;
|
|
}
|
|
return setting;
|
|
}
|
|
|
|
return isInheritedSetting(fallback) ? fallback.value : fallback;
|
|
}
|
|
|
|
type WorkspaceSettings = Pick<
|
|
Workspace,
|
|
| "settingFollowRedirects"
|
|
| "settingRequestTimeout"
|
|
| "settingSendCookies"
|
|
| "settingStoreCookies"
|
|
| "settingValidateCertificates"
|
|
>;
|
|
|
|
type BooleanWorkspaceSettingKey = Exclude<keyof WorkspaceSettings, "settingRequestTimeout">;
|