mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-16 12:47:09 +02:00
Compare commits
4 Commits
v2026.4.0-
...
codex-revi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab785b18a4 | ||
|
|
dcfdf077e7 | ||
|
|
947e3f2e97 | ||
|
|
8b1f5e807f |
@@ -1 +1,2 @@
|
|||||||
vp lint
|
vp lint
|
||||||
|
vp staged
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { Cookie } from "@yaakapp-internal/models";
|
import type { Cookie, CookieDomain, CookieJar } from "@yaakapp-internal/models";
|
||||||
import { cookieJarsAtom, patchModel } from "@yaakapp-internal/models";
|
import { cookieJarsAtom, patchModelById } from "@yaakapp-internal/models";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { cookieDomain } from "../lib/model_util";
|
import { cookieDomain } from "../lib/model_util";
|
||||||
|
import { showPromptForm } from "../lib/prompt-form";
|
||||||
import { Banner, InlineCode } from "@yaakapp-internal/ui";
|
import { Banner, InlineCode } from "@yaakapp-internal/ui";
|
||||||
import { IconButton } from "./core/IconButton";
|
import { IconButton } from "./core/IconButton";
|
||||||
|
|
||||||
@@ -9,6 +10,109 @@ interface Props {
|
|||||||
cookieJarId: string | null;
|
cookieJarId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function showAddCookieForm(cookieJarId: string): Promise<void> {
|
||||||
|
const result = await showPromptForm({
|
||||||
|
id: "add-cookie",
|
||||||
|
title: "Add Cookie",
|
||||||
|
size: "md",
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: "cookie_pairs",
|
||||||
|
label: "Cookie Attributes",
|
||||||
|
type: "key_value",
|
||||||
|
description:
|
||||||
|
"Add key-value pairs for the cookie. These will be combined into the cookie string.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "domain_value",
|
||||||
|
label: "Domain",
|
||||||
|
type: "text",
|
||||||
|
placeholder: "example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hostOnly",
|
||||||
|
label: "Host Only",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: "true",
|
||||||
|
description:
|
||||||
|
"If enabled, cookie is restricted to the exact host. Otherwise, it applies to the domain and its subdomains.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "path",
|
||||||
|
label: "Path",
|
||||||
|
type: "text",
|
||||||
|
placeholder: "/",
|
||||||
|
defaultValue: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "secure",
|
||||||
|
label: "Secure",
|
||||||
|
type: "checkbox",
|
||||||
|
defaultValue: "true",
|
||||||
|
description: "If enabled, cookie will only be sent over HTTPS connections.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result == null) return;
|
||||||
|
|
||||||
|
// Parse the form results
|
||||||
|
const cookie_pairs_raw = result.cookie_pairs;
|
||||||
|
const domain_value = (result.domain_value as string) ?? "";
|
||||||
|
const path = (result.path as string) ?? "/";
|
||||||
|
const hostOnly = (result.hostOnly as string) === "true";
|
||||||
|
const secure = (result.secure as string) === "true";
|
||||||
|
|
||||||
|
// Convert key-value pairs to raw_cookie string format: key1=value1;key2=value2
|
||||||
|
// Parse cookie_pairs - it comes as a JSON string from the key_value input
|
||||||
|
let parsedPairs: Array<{ name: string; value: string }> = [];
|
||||||
|
try {
|
||||||
|
// Handle null, undefined, or string value
|
||||||
|
const pairsStr =
|
||||||
|
typeof cookie_pairs_raw === "string"
|
||||||
|
? cookie_pairs_raw
|
||||||
|
: cookie_pairs_raw != null
|
||||||
|
? JSON.stringify(cookie_pairs_raw)
|
||||||
|
: "[]";
|
||||||
|
if (pairsStr && pairsStr !== "") {
|
||||||
|
parsedPairs = JSON.parse(pairsStr);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
parsedPairs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const validPairs = parsedPairs.filter((p) => p?.name?.trim());
|
||||||
|
// Ensure at least one valid pair exists
|
||||||
|
if (validPairs.length === 0) {
|
||||||
|
console.log("No valid cookie pairs provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw_cookie = validPairs.map((p) => `${p.name}=${p.value}`).join(";");
|
||||||
|
|
||||||
|
const domain: CookieDomain = hostOnly
|
||||||
|
? { HostOnly: domain_value ?? "" }
|
||||||
|
: { Suffix: domain_value ?? "" };
|
||||||
|
|
||||||
|
// Build the new cookie with explicit tuple type for path
|
||||||
|
const newCookie: Cookie = {
|
||||||
|
raw_cookie,
|
||||||
|
domain,
|
||||||
|
expires: "SessionEnd",
|
||||||
|
path: [path, secure] as [string, boolean],
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await patchModelById<"cookie_jar", CookieJar>("cookie_jar", cookieJarId, (prev) => ({
|
||||||
|
...prev,
|
||||||
|
cookies: [...prev.cookies, newCookie],
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to add cookie:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const CookieDialog = ({ cookieJarId }: Props) => {
|
export const CookieDialog = ({ cookieJarId }: Props) => {
|
||||||
const cookieJars = useAtomValue(cookieJarsAtom);
|
const cookieJars = useAtomValue(cookieJarsAtom);
|
||||||
const cookieJar = cookieJars?.find((c) => c.id === cookieJarId);
|
const cookieJar = cookieJars?.find((c) => c.id === cookieJarId);
|
||||||
@@ -17,12 +121,47 @@ export const CookieDialog = ({ cookieJarId }: Props) => {
|
|||||||
return <div>No cookie jar selected</div>;
|
return <div>No cookie jar selected</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onAddCookie = () => showAddCookieForm(cookieJar.id);
|
||||||
|
|
||||||
|
let tableBody;
|
||||||
if (cookieJar.cookies.length === 0) {
|
if (cookieJar.cookies.length === 0) {
|
||||||
return (
|
tableBody = (
|
||||||
<Banner>
|
<tr>
|
||||||
Cookies will appear when a response contains the <InlineCode>Set-Cookie</InlineCode> header
|
<td colSpan={3}>
|
||||||
</Banner>
|
<Banner>
|
||||||
|
Cookies will appear when a response contains the <InlineCode>Set-Cookie</InlineCode>{" "}
|
||||||
|
header
|
||||||
|
</Banner>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
);
|
);
|
||||||
|
// );
|
||||||
|
} else {
|
||||||
|
tableBody = cookieJar?.cookies.map((c: Cookie) => (
|
||||||
|
<tr key={JSON.stringify(c)}>
|
||||||
|
<td className="py-2 select-text cursor-text font-mono font-semibold max-w-0">
|
||||||
|
{cookieDomain(c)}
|
||||||
|
</td>
|
||||||
|
<td className="py-2 pl-4 select-text cursor-text font-mono text-text-subtle whitespace-nowrap overflow-x-auto max-w-[200px] hide-scrollbars">
|
||||||
|
{c.raw_cookie}
|
||||||
|
</td>
|
||||||
|
<td className="max-w-0 w-10">
|
||||||
|
<IconButton
|
||||||
|
icon="trash"
|
||||||
|
size="xs"
|
||||||
|
iconSize="sm"
|
||||||
|
title="Delete"
|
||||||
|
className="ml-auto"
|
||||||
|
onClick={async () =>
|
||||||
|
await patchModelById<"cookie_jar", CookieJar>("cookie_jar", cookieJar.id, (prev) => ({
|
||||||
|
...prev,
|
||||||
|
cookies: prev.cookies.filter((c2: Cookie) => c2 !== c),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -32,35 +171,19 @@ export const CookieDialog = ({ cookieJarId }: Props) => {
|
|||||||
<tr>
|
<tr>
|
||||||
<th className="py-2 text-left">Domain</th>
|
<th className="py-2 text-left">Domain</th>
|
||||||
<th className="py-2 text-left pl-4">Cookie</th>
|
<th className="py-2 text-left pl-4">Cookie</th>
|
||||||
<th className="py-2 pl-4" />
|
<th className="py-2 pl-4 w-10">
|
||||||
|
<IconButton
|
||||||
|
icon="plus"
|
||||||
|
size="xs"
|
||||||
|
iconSize="sm"
|
||||||
|
title="Add Cookie"
|
||||||
|
className="ml-auto"
|
||||||
|
onClick={onAddCookie}
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-surface-highlight">
|
<tbody className="divide-y divide-surface-highlight">{tableBody}</tbody>
|
||||||
{cookieJar?.cookies.map((c: Cookie) => (
|
|
||||||
<tr key={JSON.stringify(c)}>
|
|
||||||
<td className="py-2 select-text cursor-text font-mono font-semibold max-w-0">
|
|
||||||
{cookieDomain(c)}
|
|
||||||
</td>
|
|
||||||
<td className="py-2 pl-4 select-text cursor-text font-mono text-text-subtle whitespace-nowrap overflow-x-auto max-w-[200px] hide-scrollbars">
|
|
||||||
{c.raw_cookie}
|
|
||||||
</td>
|
|
||||||
<td className="max-w-0 w-10">
|
|
||||||
<IconButton
|
|
||||||
icon="trash"
|
|
||||||
size="xs"
|
|
||||||
iconSize="sm"
|
|
||||||
title="Delete"
|
|
||||||
className="ml-auto"
|
|
||||||
onClick={() =>
|
|
||||||
patchModel(cookieJar, {
|
|
||||||
cookies: cookieJar.cookies.filter((c2: Cookie) => c2 !== c),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { defineConfig } from "vite-plus";
|
import { defineConfig } from "vite-plus";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
staged: {
|
||||||
|
"*": "vp check --fix",
|
||||||
|
},
|
||||||
lint: {
|
lint: {
|
||||||
ignorePatterns: ["npm/**", "crates/yaak-templates/pkg/**", "**/bindings/gen_*.ts"],
|
ignorePatterns: ["npm/**", "crates/yaak-templates/pkg/**", "**/bindings/gen_*.ts"],
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
Reference in New Issue
Block a user