Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

@angular/ssr/node not working as intended

未關閉
#3,596 2 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
4/5
預估耗時
3-5 天
新手友好度
38/100
Issue 類型
缺陷
描述清晰度
基本清楚
活躍度
停滯
技術堆疊
angular, express, firebase, node.js, typescript
領域
backend, cloud, web-dev

研究方向

從 src/server.ts 和 functions/ssr/index.js 開始,接著將 src/app/app.routes.ts 中 /error 路由的重新導向與已部署的 Firebase Hosting 請求進行比較。檢查 Cloud Function 的 302 Location 回應,並驗證完成後的行為會保留公開的 Hosting URL,而不是暴露連接埠 8080。

由索引模型根據 Issue 內容生成。

描述

I am using @angular/fire@19.0.0-rc.4, and I have the following:

src/server.ts

import {
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
} from "@angular/ssr/node";

import express from "express";

import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, "../browser");

const server = express();
const angularNodeAppEngine = new AngularNodeAppEngine();

server.use(
express.static(browserDistFolder, {
maxAge: "1y",
index: false,
redirect: false,
}),
);

server.use("/**", (serverRequest, serverResponse, serverNextFunction) => {
angularNodeAppEngine
.handle(serverRequest)
.then((response) => {
if (response) {
return writeResponseToNodeResponse(response, serverResponse);
} else {
return serverNextFunction();
}
})
.catch(serverNextFunction);
});

if (isMainModule(import.meta.url)) {
const port = process.env["PORT"] || 4000;
server.listen(port, () => {
console.log(Node Express server listening on http://localhost:${port});
});
}

export const reqHandler = createNodeRequestHandler(server);

src/app/app.routes.ts

import { Route } from "@angular/router";

import { ErrorComponent } from "./error/error.component";
import { errorResolver } from "./error/error.resolver";
import { HomeComponent } from "./home/home.component";
import { homeResolver } from "./home/home.resolver";

export const routes: Route[] = [
{
path: "error",
component: ErrorComponent,
resolve: {
error: errorResolver,
},
},
{
path: "",
pathMatch: "full",
component: HomeComponent,
resolve: {
home: homeResolver,
},
},
{
path: "**",
redirectTo: "/error",
},
];

src/app/app.routes.server.ts

import { RenderMode, ServerRoute } from "@angular/ssr";

export const serverRoutes: ServerRoute[] = [
{
path: "**",
renderMode: RenderMode.Server,
},
];

src/app/app.config.ts

import {
ApplicationConfig,
provideExperimentalZonelessChangeDetection,
} from "@angular/core";
import { initializeApp, provideFirebaseApp } from "@angular/fire/app";
import { getAnalytics, provideAnalytics } from "@angular/fire/analytics";
import { getFirestore, provideFirestore } from "@angular/fire/firestore";
import { getStorage, provideStorage } from "@angular/fire/storage";
import {
provideClientHydration,
withEventReplay,
} from "@angular/platform-browser";
import {
provideRouter,
withEnabledBlockingInitialNavigation,
withInMemoryScrolling,
} from "@angular/router";

import { routes } from "./app.routes";

import { environment } from "../environments/environment.production";

export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
provideClientHydration(withEventReplay()),
provideRouter(
routes,
withEnabledBlockingInitialNavigation(),
withInMemoryScrolling({
anchorScrolling: "enabled",
scrollPositionRestoration: "enabled",
}),
),
provideFirebaseApp(() => {
return initializeApp(environment.firebase);
}),
provideAnalytics(() => {
return getAnalytics();
}),
provideFirestore(() => {
return getFirestore();
}),
provideStorage(() => {
return getStorage();
}),
],
};

src/app/app.config.server.ts

import { mergeApplicationConfig, ApplicationConfig } from "@angular/core";
import { provideServerRendering } from "@angular/platform-server";
import { provideServerRoutesConfig } from "@angular/ssr";

import { appConfig } from "./app.config";
import { serverRoutes } from "./app.routes.server";

const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
provideServerRoutesConfig(serverRoutes),
],
};

export const config = mergeApplicationConfig(appConfig, serverConfig);

Now, I am not using ng deploy or firebase deploy since they require @angular-devkit/build-angular and the SSR function being deployed is not working. Instead, I created a separate Firebase Repo and initialised both hosting and functions.

firebase.json

{
"functions": [
{
"source": "functions/ssr",
"codebase": "ssr",
"ignore": [...]
}
],
"hosting": {
"public": "hosting",
"ignore": [...],
"rewrites": [
{
"source": "**",
"function": {
"functionId": "ssr",
"region": "europe-west1"
}
}
]
}
}

functions/ssr/index.js

import { onRequest } from "firebase-functions/v2/https";

import { reqHandler } from "./build/template/server/server.mjs";

export const ssr = onRequest(
{
region: "europe-west1",
invoker: "public",
memory: "256MiB",
cpu: 1,
timeoutSeconds: 60,
concurrency: 80,
minInstances: 1,
maxInstances: 100,
ingressSettings: "ALLOW_ALL",
preserveExternalChanges: false,
},
reqHandler,
);

In the Firebase Repo, I am successfully deploying both hosting and functions using firebase deploy. The SSR function works, save for paths redirected to the ErrorComponent:

src/app/app.routes.ts

{
path: "**",
redirectTo: "/error",
},

When seeing the Google Cloud Console logs for the Cloud function, the express instance is giving a 302 response (which is what you'd expect from a redirect), however, instead of staying on port 80, the redirect URL is given on port 8080, which is the internal port being using by the express instance. I reckon that server.ts needs to be modified, however, I don't know how.

主要語言
TypeScript
星號
7.8k
分支
2.2k
平均合併
3 天 6 小時
30 天內合併 PR
5

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

angular/angularfire 的其他 Issue

查看 angular/angularfire 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。