From e630331974c5f08c5d0b7cb1d8e0ce73887a72b4 Mon Sep 17 00:00:00 2001 From: Xuan Gu <162244362+xuang7@users.noreply.github.com> Date: Sat, 4 Jul 2026 19:39:29 -0700 Subject: [PATCH 1/4] fix: add missing /pve route to single-node nginx --- bin/single-node/nginx.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bin/single-node/nginx.conf b/bin/single-node/nginx.conf index 515a7016da0..a5b4490ffc7 100644 --- a/bin/single-node/nginx.conf +++ b/bin/single-node/nginx.conf @@ -95,6 +95,16 @@ http { proxy_set_header Host $host; } + # Python Virtual Environment (PVE) REST endpoints are served by the + # amber webserver under /api/pve. Mirror the dev proxy and k8s gateway + # by rewriting /pve -> /api/pve before proxying. + location /pve/ { + rewrite ^/pve/(.*)$ /api/pve/$1 break; + proxy_pass http://workflow-runtime-coordinator-service:8085; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + # Fallback for all other routes location / { proxy_pass http://dashboard-service:8080; From a913e42bf7f5af832e9f541c7e7edb284de44d6b Mon Sep 17 00:00:00 2001 From: Xuan Gu <162244362+xuang7@users.noreply.github.com> Date: Mon, 6 Jul 2026 15:49:37 -0700 Subject: [PATCH 2/4] simplify /pve route to prefix-replacing proxy_pass Co-Authored-By: Claude Fable 5 --- bin/single-node/nginx.conf | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bin/single-node/nginx.conf b/bin/single-node/nginx.conf index a5b4490ffc7..deffb6021ef 100644 --- a/bin/single-node/nginx.conf +++ b/bin/single-node/nginx.conf @@ -95,12 +95,8 @@ http { proxy_set_header Host $host; } - # Python Virtual Environment (PVE) REST endpoints are served by the - # amber webserver under /api/pve. Mirror the dev proxy and k8s gateway - # by rewriting /pve -> /api/pve before proxying. location /pve/ { - rewrite ^/pve/(.*)$ /api/pve/$1 break; - proxy_pass http://workflow-runtime-coordinator-service:8085; + proxy_pass http://workflow-runtime-coordinator-service:8085/api/pve/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } From b86474568b20a46bd23b80a7feba5eacab0c5b56 Mon Sep 17 00:00:00 2001 From: Xuan Gu <162244362+xuang7@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:46:35 -0700 Subject: [PATCH 3/4] request /api/pve from the frontend and drop all gateway rewrites Per review: the frontend now calls /api/pve directly via AppSettings.getApiEndpoint(), so the /pve-to-/api/pve rewrite rules in single-node nginx, the dev proxy, and the k8s gateway become plain routing entries. Co-Authored-By: Claude Fable 5 --- .../templates/base/gateway/gateway-routes.yaml | 13 +------------ bin/single-node/nginx.conf | 4 ++-- frontend/proxy.config.json | 13 +++++-------- .../computing-unit-selection.component.ts | 2 +- .../virtual-environment.service.spec.ts | 9 +++++---- .../virtual-environment.service.ts | 17 +++++++++-------- 6 files changed, 23 insertions(+), 35 deletions(-) diff --git a/bin/k8s/templates/base/gateway/gateway-routes.yaml b/bin/k8s/templates/base/gateway/gateway-routes.yaml index ac8096aa212..935d38bb079 100644 --- a/bin/k8s/templates/base/gateway/gateway-routes.yaml +++ b/bin/k8s/templates/base/gateway/gateway-routes.yaml @@ -124,20 +124,9 @@ spec: - path: type: PathPrefix value: /api/executions/result/export - backendRefs: - - group: gateway.envoyproxy.io - kind: Backend - name: texera-dynamic-backend - - matches: - path: type: PathPrefix - value: /pve - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: /api/pve + value: /api/pve backendRefs: - group: gateway.envoyproxy.io kind: Backend diff --git a/bin/single-node/nginx.conf b/bin/single-node/nginx.conf index deffb6021ef..c05a45e1bfe 100644 --- a/bin/single-node/nginx.conf +++ b/bin/single-node/nginx.conf @@ -95,8 +95,8 @@ http { proxy_set_header Host $host; } - location /pve/ { - proxy_pass http://workflow-runtime-coordinator-service:8085/api/pve/; + location /api/pve/ { + proxy_pass http://workflow-runtime-coordinator-service:8085; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } diff --git a/frontend/proxy.config.json b/frontend/proxy.config.json index 7d68961d9b6..c26a239b178 100755 --- a/frontend/proxy.config.json +++ b/frontend/proxy.config.json @@ -55,18 +55,15 @@ "secure": false, "changeOrigin": true }, - "/api": { - "target": "http://localhost:8080", + "/api/pve": { + "target": "http://localhost:8085", "secure": false, "changeOrigin": false }, - "/pve": { - "target": "http://localhost:8085", + "/api": { + "target": "http://localhost:8080", "secure": false, - "changeOrigin": false, - "pathRewrite": { - "^/pve": "/api/pve" - } + "changeOrigin": false }, "/wsapi": { "target": "http://localhost:8085", diff --git a/frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts b/frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts index fed31fddb4e..b633e6eca31 100644 --- a/frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts +++ b/frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts @@ -134,7 +134,7 @@ export class ComputingUnitSelectionComponent implements OnInit { // variables for creating a virtual environment pves: PveDraft[] = []; systemPackages: { name: string; version: string }[] = []; - // True while a /pve/system response is in flight. The server resolves + // True while an /api/pve/system response is in flight. The server resolves // the full pinned set with a `pip freeze` against a throwaway venv, // which can take 30–60s on the first request after a server restart. systemPackagesLoading = false; diff --git a/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.spec.ts b/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.spec.ts index c194c115ccc..c124972baf7 100644 --- a/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.spec.ts +++ b/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.spec.ts @@ -18,6 +18,7 @@ */ import { TestBed } from "@angular/core/testing"; +import { AppSettings } from "../../../common/app-setting"; import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; import { UserPveRecord, WorkflowPveService } from "./virtual-environment.service"; import { commonTestProviders } from "../../../common/testing/test-utils"; @@ -45,7 +46,7 @@ describe("WorkflowPveService", () => { expect(resp.veid).toBe(42); }); - const req = httpTestingController.expectOne("/pve/db"); + const req = httpTestingController.expectOne(`${AppSettings.getApiEndpoint()}/pve/db`); expect(req.request.method).toBe("POST"); expect(req.request.body).toEqual({ name: "env-a", packages }); req.flush({ veid: 42 }); @@ -57,7 +58,7 @@ describe("WorkflowPveService", () => { expect(resp.veid).toBe(7); }); - const req = httpTestingController.expectOne("/pve/db/7"); + const req = httpTestingController.expectOne(`${AppSettings.getApiEndpoint()}/pve/db/7`); expect(req.request.method).toBe("PUT"); expect(req.request.body).toEqual({ name: "env-b", packages }); req.flush({ veid: 7 }); @@ -69,7 +70,7 @@ describe("WorkflowPveService", () => { expect(resp).toEqual(records); }); - const req = httpTestingController.expectOne("/pve/db"); + const req = httpTestingController.expectOne(`${AppSettings.getApiEndpoint()}/pve/db`); expect(req.request.method).toBe("GET"); req.flush(records); }); @@ -77,7 +78,7 @@ describe("WorkflowPveService", () => { it("deleteUserPve() DELETEs /pve/db/{veid}", () => { service.deleteUserPve(9).subscribe(); - const req = httpTestingController.expectOne("/pve/db/9"); + const req = httpTestingController.expectOne(`${AppSettings.getApiEndpoint()}/pve/db/9`); expect(req.request.method).toBe("DELETE"); req.flush(null); }); diff --git a/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.ts b/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.ts index d399f643a0c..cc4d7df9424 100644 --- a/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.ts +++ b/frontend/src/app/workspace/service/virtual-environment/virtual-environment.service.ts @@ -21,6 +21,7 @@ import { Observable } from "rxjs"; import { map } from "rxjs/operators"; import { HttpClient, HttpParams } from "@angular/common/http"; import { AuthService } from "../../../common/service/user/auth.service"; +import { AppSettings } from "../../../common/app-setting"; export interface PackageResponse { system: string[]; @@ -42,19 +43,19 @@ export class WorkflowPveService { constructor(private http: HttpClient) {} savePve(name: string, packages: Record): Observable<{ veid: number }> { - return this.http.post<{ veid: number }>("/pve/db", { name, packages }); + return this.http.post<{ veid: number }>(`${AppSettings.getApiEndpoint()}/pve/db`, { name, packages }); } updateUserPve(veid: number, name: string, packages: Record): Observable<{ veid: number }> { - return this.http.put<{ veid: number }>(`/pve/db/${veid}`, { name, packages }); + return this.http.put<{ veid: number }>(`${AppSettings.getApiEndpoint()}/pve/db/${veid}`, { name, packages }); } listUserPves(): Observable { - return this.http.get("/pve/db"); + return this.http.get(`${AppSettings.getApiEndpoint()}/pve/db`); } deleteUserPve(veid: number): Observable { - return this.http.delete(`/pve/db/${veid}`); + return this.http.delete(`${AppSettings.getApiEndpoint()}/pve/db/${veid}`); } getAccessToken(): string | null { @@ -73,12 +74,12 @@ export class WorkflowPveService { getSystemPackages(cuid: number): Observable { const params = this.buildBaseParams().set("cuid", cuid.toString()); - return this.http.get("/pve/system", { params }); + return this.http.get(`${AppSettings.getApiEndpoint()}/pve/system`, { params }); } fetchPVEs(cuid: number): Observable { const params = this.buildBaseParams().set("cuid", cuid.toString()); - return this.http.get("/pve/pves", { params }); + return this.http.get(`${AppSettings.getApiEndpoint()}/pve/pves`, { params }); } getUserPackages(cuid: number, pveName: string): Observable { @@ -86,14 +87,14 @@ export class WorkflowPveService { } deleteEnvironments(cuid: number) { - return this.http.delete(`/pve/pves/${cuid}`); + return this.http.delete(`${AppSettings.getApiEndpoint()}/pve/pves/${cuid}`); } deletePackage(cuid: number, pveName: string, packageName: string) { const params = this.buildBaseParams(); return this.http.delete( - `/pve/${cuid}/${encodeURIComponent(pveName)}/packages/${encodeURIComponent(packageName)}`, + `${AppSettings.getApiEndpoint()}/pve/${cuid}/${encodeURIComponent(pveName)}/packages/${encodeURIComponent(packageName)}`, { params } ); } From e7ee16cc4146c881e9d8aa7fc5cea3fd77270b29 Mon Sep 17 00:00:00 2001 From: Xuan Gu <162244362+xuang7@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:19:58 -0700 Subject: [PATCH 4/4] group /api/pve/ with the other /api/ locations and document prefix matching Co-Authored-By: Claude Fable 5 --- bin/single-node/nginx.conf | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bin/single-node/nginx.conf b/bin/single-node/nginx.conf index c05a45e1bfe..c5e500e6670 100644 --- a/bin/single-node/nginx.conf +++ b/bin/single-node/nginx.conf @@ -81,6 +81,13 @@ http { proxy_send_timeout 1d; } + location /api/pve/ { + proxy_pass http://workflow-runtime-coordinator-service:8085; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # nginx prefix locations match by longest prefix location /api/ { proxy_pass http://dashboard-service:8080; proxy_set_header Host $host; @@ -95,12 +102,6 @@ http { proxy_set_header Host $host; } - location /api/pve/ { - proxy_pass http://workflow-runtime-coordinator-service:8085; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } - # Fallback for all other routes location / { proxy_pass http://dashboard-service:8080;