feat: integrate Next.js SSR for frontend
This commit is contained in:
65
server.ts
65
server.ts
@ -140,39 +140,44 @@ serve({
|
||||
}
|
||||
}
|
||||
|
||||
// Serve static files from frontend/public (e.g., favicon.ico, manifest.json)
|
||||
// Note: index.html is handled by SPA fallback primarily, but direct access could be supported here.
|
||||
// We avoid serving index.html here directly to let SPA fallback handle it for cleaner URLs.
|
||||
if (pathname !== "/" && pathname !== "/index.html") { // Avoid double serving index.html
|
||||
const publicAssetPath = path.join(frontendPublicDir, pathname.startsWith("/") ? pathname.substring(1) : pathname);
|
||||
// Next.jsのSSRを処理
|
||||
try {
|
||||
const file = Bun.file(publicAssetPath);
|
||||
if (await file.exists() && !fs.statSync(publicAssetPath).isDirectory()) { // Ensure it's a file
|
||||
let contentType = "application/octet-stream";
|
||||
if (publicAssetPath.endsWith(".css")) contentType = "text/css; charset=utf-8";
|
||||
else if (publicAssetPath.endsWith(".js")) contentType = "application/javascript; charset=utf-8";
|
||||
else if (publicAssetPath.endsWith(".json")) contentType = "application/json; charset=utf-8";
|
||||
else if (publicAssetPath.endsWith(".png")) contentType = "image/png";
|
||||
else if (publicAssetPath.endsWith(".jpg") || publicAssetPath.endsWith(".jpeg")) contentType = "image/jpeg";
|
||||
else if (publicAssetPath.endsWith(".ico")) contentType = "image/x-icon";
|
||||
// 他の静的アセットタイプをここに追加
|
||||
return new Response(file, { headers: { "Content-Type": contentType } });
|
||||
}
|
||||
} catch (e) {
|
||||
// Not found or other error, fall through to SPA fallback or 404
|
||||
}
|
||||
}
|
||||
// Next.jsのページレンダリング
|
||||
const app = require("./frontend/src/app");
|
||||
|
||||
// SPA Fallback: For non-API, non-file paths, serve frontend/public/index.html
|
||||
// This handles requests like `/`, `/some-route` for client-side routing.
|
||||
const indexHtmlPath = path.join(frontendPublicDir, "index.html");
|
||||
try {
|
||||
const indexFile = Bun.file(indexHtmlPath);
|
||||
if (await indexFile.exists()) {
|
||||
return new Response(indexFile, { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
||||
}
|
||||
// ページコンポーネントを取得
|
||||
const pageComponent = app.default || app.Home || app;
|
||||
|
||||
// React要素を作成
|
||||
const element = React.createElement(pageComponent);
|
||||
|
||||
// React DOMを使用してHTMLを生成
|
||||
const ReactDOMServer = require("react-dom/server");
|
||||
const html = ReactDOMServer.renderToString(element);
|
||||
|
||||
// 完全なHTMLドキュメントを構築
|
||||
const htmlTemplate = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ポッドキャスト管理画面</title>
|
||||
<script src="/_next/static/chunks/react.js"></script>
|
||||
<script src="/_next/static/chunks/app.js"></script>
|
||||
<link rel="stylesheet" href="/_next/static/chunks/app.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">${html}</div>
|
||||
</body>
|
||||
</html>
|
||||
`.trim();
|
||||
|
||||
return new Response(htmlTemplate, {
|
||||
headers: { "Content-Type": "text/html; charset=utf-8" }
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`Error serving index.html ${indexHtmlPath}:`, e);
|
||||
console.error("Error rendering Next.js app:", e);
|
||||
}
|
||||
|
||||
return new Response("Not Found", { status: 404 });
|
||||
|
Reference in New Issue
Block a user