Skip to content

Commit 804672a

Browse files
committed
fix(cli): harden Angular project selection and proxy.conf rewrite
Throw E_VALIDATION when angular.json has several projects and no defaultProject instead of wiring the proxy into an arbitrary one. Provide the prefix strip in both the http-proxy-middleware form (pathRewrite) and the Vite form (rewrite) via a shared stripPrefix, mirroring the dual bearer hooks, so the prefix is stripped whichever proxy layer Angular's dev server uses.
1 parent c758834 commit 804672a

3 files changed

Lines changed: 43 additions & 11 deletions

File tree

apps/cli/src/lib/orca/patchers/rule/angular/angular-json.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { isObject, parseJsonObject } from "../../../../json";
77
* `serve` target. The project name is discovered from the file (`defaultProject`,
88
* else the sole project) rather than hardcoded, since it varies per app.
99
* Idempotent — already-set values are left as-is. Throws `E_VALIDATION` when the
10-
* file is absent or the project/serve target cannot be located.
10+
* file is absent, the project/serve target cannot be located, or the workspace
11+
* has several projects with no `defaultProject` to disambiguate (rather than
12+
* guessing and wiring the proxy into an arbitrary one).
1113
*/
1214
export function angularProxyEdit(opts: {
1315
proxyConfig: string;
@@ -23,10 +25,21 @@ export function angularProxyEdit(opts: {
2325
const projects = isObject(root.projects)
2426
? (root.projects as Record<string, unknown>)
2527
: undefined;
26-
const projectName =
27-
typeof root.defaultProject === "string"
28-
? root.defaultProject
29-
: Object.keys(projects ?? {})[0];
28+
const projectNames = Object.keys(projects ?? {});
29+
let projectName: string | undefined;
30+
if (typeof root.defaultProject === "string") {
31+
projectName = root.defaultProject;
32+
} else if (projectNames.length === 1) {
33+
projectName = projectNames[0];
34+
} else if (projectNames.length > 1) {
35+
throw new ZitadelError(
36+
"E_VALIDATION",
37+
"angular.json has multiple projects and no defaultProject to choose from",
38+
{
39+
hint: `Set "defaultProject" in angular.json, or add "proxyConfig" to the right project's serve target manually. Projects: ${projectNames.join(", ")}.`,
40+
},
41+
);
42+
}
3043
const project = projects && projectName ? projects[projectName] : undefined;
3144
if (!isObject(project)) {
3245
throw new ZitadelError("E_VALIDATION", "No project found in angular.json", {

apps/cli/src/lib/orca/patchers/rule/angular/templates.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@ export function appTemplateHtml(): string {
5757
}
5858

5959
/**
60-
* The managed `proxy.conf.cjs` for `ng serve` (Angular's Vite-based dev server).
61-
* `proxy.conf.cjs` for `ng serve`: forwards `/__nextgen/*` to the backend (from
62-
* `zitadel.json`), strips the prefix, and attaches the project's `sk_<project_id>`
63-
* bearer to every proxied request. Both `onProxyReq` (http-proxy-middleware) and
64-
* `configure` (Vite) hooks are set so whichever Angular's dev server honors fires.
60+
* The managed `proxy.conf.cjs` for `ng serve`: forwards `/__nextgen/*` to the
61+
* backend (from `zitadel.json`), strips the prefix, and attaches the project's
62+
* `sk_<project_id>` bearer to every proxied request. The prefix strip and the
63+
* bearer are each provided in both the http-proxy-middleware form
64+
* (`pathRewrite`/`onProxyReq`) and the Vite form (`rewrite`/`configure`), so
65+
* both fire whichever proxy layer Angular's dev server uses.
6566
*/
6667
export function proxyConfTemplate(): string {
6768
return `${MANAGED_MARKER}
@@ -74,11 +75,16 @@ function setBearer(proxyReq) {
7475
proxyReq.setHeader("authorization", bearer);
7576
}
7677
78+
function stripPrefix(path) {
79+
return path.replace(/^\\${PROXY_PATH}/, "") || "/";
80+
}
81+
7782
module.exports = {
7883
"${PROXY_PATH}": {
7984
target: config.server,
8085
changeOrigin: false,
81-
pathRewrite: (path) => path.replace(/^\\${PROXY_PATH}/, "") || "/",
86+
pathRewrite: stripPrefix,
87+
rewrite: stripPrefix,
8288
onProxyReq: setBearer,
8389
configure: (proxy) => proxy.on("proxyReq", setBearer),
8490
},

apps/cli/tests/unit/lib/orca/patchers/rule/angular/angular-json.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ describe("angularProxyEdit", () => {
4040
expect(() => edit(JSON.stringify({ projects: {} }))).toThrowError(/No project/);
4141
});
4242

43+
it("throws on multiple projects with no defaultProject instead of guessing", () => {
44+
expect(() =>
45+
edit(
46+
JSON.stringify({
47+
projects: {
48+
web: { architect: { serve: { options: {} } } },
49+
admin: { architect: { serve: { options: {} } } },
50+
},
51+
}),
52+
),
53+
).toThrowError(/multiple projects/);
54+
});
55+
4356
it("throws when there is no serve target", () => {
4457
expect(() => edit(ng({ architect: {} }))).toThrowError(/serve/);
4558
});

0 commit comments

Comments
 (0)