Skip to content

Commit 71ccd82

Browse files
committed
fix: switch /api/sessions to POST to avoid URL length overflow
With 500+ project directories, the comma-joined dir list in the GET query string exceeds Axum/hyper's 8 KB URL limit. The server rejects the request before adding CORS headers, which the browser reports as a CORS error. Change the endpoint to accept dirs as a JSON body (POST) instead, which has no size limit. - http_api.rs: GET → POST, Query extractor → Json<DiscoverBody> - src/lib/invoke.ts: discover_sessions route updated to POST - tui/src/api.ts: discoverSessions uses post() instead of get() - invoke.test.ts: updated to assert POST with body
1 parent c38c36d commit 71ccd82

9 files changed

Lines changed: 119 additions & 19 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/gen/schemas/acl-manifests.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src-tauri/gen/schemas/desktop-schema.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,12 +1159,24 @@
11591159
"const": "fs:allow-size",
11601160
"markdownDescription": "Enables the size command without any pre-configured scope."
11611161
},
1162+
{
1163+
"description": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope.",
1164+
"type": "string",
1165+
"const": "fs:allow-start-accessing-security-scoped-resource",
1166+
"markdownDescription": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope."
1167+
},
11621168
{
11631169
"description": "Enables the stat command without any pre-configured scope.",
11641170
"type": "string",
11651171
"const": "fs:allow-stat",
11661172
"markdownDescription": "Enables the stat command without any pre-configured scope."
11671173
},
1174+
{
1175+
"description": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope.",
1176+
"type": "string",
1177+
"const": "fs:allow-stop-accessing-security-scoped-resource",
1178+
"markdownDescription": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope."
1179+
},
11681180
{
11691181
"description": "Enables the truncate command without any pre-configured scope.",
11701182
"type": "string",
@@ -1315,12 +1327,24 @@
13151327
"const": "fs:deny-size",
13161328
"markdownDescription": "Denies the size command without any pre-configured scope."
13171329
},
1330+
{
1331+
"description": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope.",
1332+
"type": "string",
1333+
"const": "fs:deny-start-accessing-security-scoped-resource",
1334+
"markdownDescription": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope."
1335+
},
13181336
{
13191337
"description": "Denies the stat command without any pre-configured scope.",
13201338
"type": "string",
13211339
"const": "fs:deny-stat",
13221340
"markdownDescription": "Denies the stat command without any pre-configured scope."
13231341
},
1342+
{
1343+
"description": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope.",
1344+
"type": "string",
1345+
"const": "fs:deny-stop-accessing-security-scoped-resource",
1346+
"markdownDescription": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope."
1347+
},
13241348
{
13251349
"description": "Denies the truncate command without any pre-configured scope.",
13261350
"type": "string",
@@ -5216,12 +5240,24 @@
52165240
"const": "fs:allow-size",
52175241
"markdownDescription": "Enables the size command without any pre-configured scope."
52185242
},
5243+
{
5244+
"description": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope.",
5245+
"type": "string",
5246+
"const": "fs:allow-start-accessing-security-scoped-resource",
5247+
"markdownDescription": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope."
5248+
},
52195249
{
52205250
"description": "Enables the stat command without any pre-configured scope.",
52215251
"type": "string",
52225252
"const": "fs:allow-stat",
52235253
"markdownDescription": "Enables the stat command without any pre-configured scope."
52245254
},
5255+
{
5256+
"description": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope.",
5257+
"type": "string",
5258+
"const": "fs:allow-stop-accessing-security-scoped-resource",
5259+
"markdownDescription": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope."
5260+
},
52255261
{
52265262
"description": "Enables the truncate command without any pre-configured scope.",
52275263
"type": "string",
@@ -5372,12 +5408,24 @@
53725408
"const": "fs:deny-size",
53735409
"markdownDescription": "Denies the size command without any pre-configured scope."
53745410
},
5411+
{
5412+
"description": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope.",
5413+
"type": "string",
5414+
"const": "fs:deny-start-accessing-security-scoped-resource",
5415+
"markdownDescription": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope."
5416+
},
53755417
{
53765418
"description": "Denies the stat command without any pre-configured scope.",
53775419
"type": "string",
53785420
"const": "fs:deny-stat",
53795421
"markdownDescription": "Denies the stat command without any pre-configured scope."
53805422
},
5423+
{
5424+
"description": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope.",
5425+
"type": "string",
5426+
"const": "fs:deny-stop-accessing-security-scoped-resource",
5427+
"markdownDescription": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope."
5428+
},
53815429
{
53825430
"description": "Denies the truncate command without any pre-configured scope.",
53835431
"type": "string",

src-tauri/gen/schemas/macOS-schema.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,12 +1159,24 @@
11591159
"const": "fs:allow-size",
11601160
"markdownDescription": "Enables the size command without any pre-configured scope."
11611161
},
1162+
{
1163+
"description": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope.",
1164+
"type": "string",
1165+
"const": "fs:allow-start-accessing-security-scoped-resource",
1166+
"markdownDescription": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope."
1167+
},
11621168
{
11631169
"description": "Enables the stat command without any pre-configured scope.",
11641170
"type": "string",
11651171
"const": "fs:allow-stat",
11661172
"markdownDescription": "Enables the stat command without any pre-configured scope."
11671173
},
1174+
{
1175+
"description": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope.",
1176+
"type": "string",
1177+
"const": "fs:allow-stop-accessing-security-scoped-resource",
1178+
"markdownDescription": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope."
1179+
},
11681180
{
11691181
"description": "Enables the truncate command without any pre-configured scope.",
11701182
"type": "string",
@@ -1315,12 +1327,24 @@
13151327
"const": "fs:deny-size",
13161328
"markdownDescription": "Denies the size command without any pre-configured scope."
13171329
},
1330+
{
1331+
"description": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope.",
1332+
"type": "string",
1333+
"const": "fs:deny-start-accessing-security-scoped-resource",
1334+
"markdownDescription": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope."
1335+
},
13181336
{
13191337
"description": "Denies the stat command without any pre-configured scope.",
13201338
"type": "string",
13211339
"const": "fs:deny-stat",
13221340
"markdownDescription": "Denies the stat command without any pre-configured scope."
13231341
},
1342+
{
1343+
"description": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope.",
1344+
"type": "string",
1345+
"const": "fs:deny-stop-accessing-security-scoped-resource",
1346+
"markdownDescription": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope."
1347+
},
13241348
{
13251349
"description": "Denies the truncate command without any pre-configured scope.",
13261350
"type": "string",
@@ -5216,12 +5240,24 @@
52165240
"const": "fs:allow-size",
52175241
"markdownDescription": "Enables the size command without any pre-configured scope."
52185242
},
5243+
{
5244+
"description": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope.",
5245+
"type": "string",
5246+
"const": "fs:allow-start-accessing-security-scoped-resource",
5247+
"markdownDescription": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope."
5248+
},
52195249
{
52205250
"description": "Enables the stat command without any pre-configured scope.",
52215251
"type": "string",
52225252
"const": "fs:allow-stat",
52235253
"markdownDescription": "Enables the stat command without any pre-configured scope."
52245254
},
5255+
{
5256+
"description": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope.",
5257+
"type": "string",
5258+
"const": "fs:allow-stop-accessing-security-scoped-resource",
5259+
"markdownDescription": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope."
5260+
},
52255261
{
52265262
"description": "Enables the truncate command without any pre-configured scope.",
52275263
"type": "string",
@@ -5372,12 +5408,24 @@
53725408
"const": "fs:deny-size",
53735409
"markdownDescription": "Denies the size command without any pre-configured scope."
53745410
},
5411+
{
5412+
"description": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope.",
5413+
"type": "string",
5414+
"const": "fs:deny-start-accessing-security-scoped-resource",
5415+
"markdownDescription": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope."
5416+
},
53755417
{
53765418
"description": "Denies the stat command without any pre-configured scope.",
53775419
"type": "string",
53785420
"const": "fs:deny-stat",
53795421
"markdownDescription": "Denies the stat command without any pre-configured scope."
53805422
},
5423+
{
5424+
"description": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope.",
5425+
"type": "string",
5426+
"const": "fs:deny-stop-accessing-security-scoped-resource",
5427+
"markdownDescription": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope."
5428+
},
53815429
{
53825430
"description": "Denies the truncate command without any pre-configured scope.",
53835431
"type": "string",

src-tauri/src/http_api.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub async fn start_http_server(app: AppHandle) {
3838
.route("/api/settings", get(api_get_settings))
3939
.route("/api/settings/dir", post(api_set_projects_dir))
4040
.route("/api/project-dirs", get(api_get_project_dirs))
41-
.route("/api/sessions", get(api_discover_sessions))
41+
.route("/api/sessions", post(api_discover_sessions))
4242
.route("/api/session", get(api_get_session_by_id))
4343
.route("/api/session/load", post(api_load_session))
4444
.route("/api/session/meta", get(api_get_session_meta))
@@ -183,16 +183,16 @@ async fn api_get_project_dirs(State(state): State<Arc<HttpState>>) -> Response {
183183
// ---------------------------------------------------------------------------
184184

185185
#[derive(Deserialize)]
186-
struct DiscoverQuery {
187-
dirs: String, // comma-separated
186+
struct DiscoverBody {
187+
dirs: Vec<String>,
188188
}
189189

190190
async fn api_discover_sessions(
191191
State(state): State<Arc<HttpState>>,
192-
Query(q): Query<DiscoverQuery>,
192+
Json(body): Json<DiscoverBody>,
193193
) -> Response {
194194
let app_state = app_state(&state);
195-
let project_dirs: Vec<String> = q.dirs.split(',').map(|s| s.to_string()).collect();
195+
let project_dirs = body.dirs;
196196
let cache = match app_state.session_cache.lock() {
197197
Ok(c) => c,
198198
Err(e) => {

src/lib/invoke.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,20 @@ describe("invoke (web/HTTP mode)", () => {
6060
expect(res).toEqual(dirs);
6161
});
6262

63-
it("discover_sessions calls GET /api/sessions with dirs query", async () => {
63+
it("discover_sessions calls POST /api/sessions with dirs body", async () => {
6464
const fetchFn = mockFetch([]);
6565
const { invoke } = await import("./invoke");
6666

6767
await invoke("discover_sessions", { projectDirs: ["/a", "/b"] });
6868
const url = fetchFn.mock.calls[0][0] as string;
69-
expect(url).toContain("/api/sessions?dirs=");
70-
expect(url).toContain(encodeURIComponent("/a,/b"));
69+
expect(url).toBe(`${API_BASE}/api/sessions`);
70+
expect(fetchFn).toHaveBeenCalledWith(
71+
`${API_BASE}/api/sessions`,
72+
expect.objectContaining({
73+
method: "POST",
74+
body: JSON.stringify({ dirs: ["/a", "/b"] }),
75+
}),
76+
);
7177
});
7278

7379
it("watch/unwatch commands resolve without error", async () => {

src/lib/invoke.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ const routes: Record<string, Route> = {
2828
},
2929
get_project_dirs: { path: "/api/project-dirs" },
3030
discover_sessions: {
31-
path: (a) => {
32-
const dirs = (a.projectDirs as string[]) ?? [];
33-
return `/api/sessions?dirs=${encodeURIComponent(dirs.join(","))}`;
34-
},
31+
method: "POST",
32+
path: "/api/sessions",
33+
body: (a) => ({ dirs: (a.projectDirs as string[]) ?? [] }),
3534
},
3635
load_session: {
3736
method: "POST",

tui/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tui/src/api.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ async function post<T>(path: string, body?: unknown): Promise<T> {
4343
export const api = {
4444
getSettings: () => get<SettingsResponse>("/api/settings"),
4545
getProjectDirs: () => get<string[]>("/api/project-dirs"),
46-
discoverSessions: (dirs: string[]) =>
47-
get<SessionInfo[]>(`/api/sessions?dirs=${encodeURIComponent(dirs.join(","))}`),
46+
discoverSessions: (dirs: string[]) => post<SessionInfo[]>("/api/sessions", { dirs }),
4847
loadSession: (path: string) => post<LoadResult>("/api/session/load", { path }),
4948
watchSession: (path: string) => post<void>("/api/session/watch", { path }),
5049
unwatchSession: () => post<void>("/api/session/unwatch"),

0 commit comments

Comments
 (0)