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)
 | 
					    // Next.jsのSSRを処理
 | 
				
			||||||
    // 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);
 | 
					 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
            const file = Bun.file(publicAssetPath);
 | 
					        // Next.jsのページレンダリング
 | 
				
			||||||
            if (await file.exists() && !fs.statSync(publicAssetPath).isDirectory()) { // Ensure it's a file
 | 
					        const app = require("./frontend/src/app");
 | 
				
			||||||
                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
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    // SPA Fallback: For non-API, non-file paths, serve frontend/public/index.html
 | 
					        // ページコンポーネントを取得
 | 
				
			||||||
    // This handles requests like `/`, `/some-route` for client-side routing.
 | 
					        const pageComponent = app.default || app.Home || app;
 | 
				
			||||||
    const indexHtmlPath = path.join(frontendPublicDir, "index.html");
 | 
					        
 | 
				
			||||||
    try {
 | 
					        // React要素を作成
 | 
				
			||||||
        const indexFile = Bun.file(indexHtmlPath);
 | 
					        const element = React.createElement(pageComponent);
 | 
				
			||||||
        if (await indexFile.exists()) {
 | 
					        
 | 
				
			||||||
            return new Response(indexFile, { headers: { "Content-Type": "text/html; charset=utf-8" } });
 | 
					        // 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) {
 | 
					    } 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 });
 | 
					    return new Response("Not Found", { status: 404 });
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user