feat: migrate frontend to Next.js
This commit is contained in:
@ -2,9 +2,10 @@
|
|||||||
"name": "podcast-frontend",
|
"name": "podcast-frontend",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun dev",
|
"dev": "next dev",
|
||||||
"build": "bun build",
|
"build": "next build",
|
||||||
"start": "bun start"
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
@ -17,7 +18,6 @@
|
|||||||
"bun-types": "^0.1.0",
|
"bun-types": "^0.1.0",
|
||||||
"@types/bun": "latest"
|
"@types/bun": "latest"
|
||||||
},
|
},
|
||||||
"module": "src/index.tsx",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
14
frontend/src/app/page.tsx
Normal file
14
frontend/src/app/page.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from "react";
|
||||||
|
import FeedList from "../components/FeedList";
|
||||||
|
import EpisodeList from "../components/EpisodeList";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "20px", fontFamily: "sans-serif" }}>
|
||||||
|
<h1>ポッドキャスト自動生成サービス 管理画面</h1>
|
||||||
|
<FeedList />
|
||||||
|
<hr style={{ margin: "20px 0" }} />
|
||||||
|
<EpisodeList />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,14 +1,20 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "es5",
|
||||||
"module": "ESNext",
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"jsx": "react-jsx",
|
"allowJs": true,
|
||||||
"strict": true,
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"outDir": "build"
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"outDir": "dist"
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
11
package.json
11
package.json
@ -2,16 +2,23 @@
|
|||||||
"name": "podcast-generator",
|
"name": "podcast-generator",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "bun run server.ts"
|
"start": "bun run server.ts",
|
||||||
|
"build:frontend": "cd frontend && bun build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-polly": "^3.823.0",
|
"@aws-sdk/client-polly": "^3.823.0",
|
||||||
|
"next": "^14.0.0",
|
||||||
|
"react": "^18.0.0",
|
||||||
|
"react-dom": "^18.0.0",
|
||||||
"openai": "^4.104.0",
|
"openai": "^4.104.0",
|
||||||
"rss-parser": "^3.13.0"
|
"rss-parser": "^3.13.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest"
|
"typescript": "^5",
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"@types/react": "^18.0.0",
|
||||||
|
"@types/react-dom": "^18.0.0"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
12
server.ts
12
server.ts
@ -93,21 +93,23 @@ serve({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve /build/* from frontend/build (compiled JS/CSS)
|
// Next.jsの静的ファイルを提供
|
||||||
if (pathname.startsWith("/build/")) {
|
if (pathname.startsWith("/_next/")) {
|
||||||
const assetPath = pathname.substring("/build/".length);
|
const assetPath = pathname.substring("/_next/".length);
|
||||||
const filePath = path.join(frontendBuildDir, assetPath);
|
const filePath = path.join(frontendBuildDir, "_next", assetPath);
|
||||||
try {
|
try {
|
||||||
const file = Bun.file(filePath);
|
const file = Bun.file(filePath);
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
let contentType = "application/octet-stream";
|
let contentType = "application/octet-stream";
|
||||||
if (filePath.endsWith(".js")) contentType = "application/javascript; charset=utf-8";
|
if (filePath.endsWith(".js")) contentType = "application/javascript; charset=utf-8";
|
||||||
else if (filePath.endsWith(".css")) contentType = "text/css; charset=utf-8";
|
else if (filePath.endsWith(".css")) contentType = "text/css; charset=utf-8";
|
||||||
|
else if (filePath.endsWith(".png")) contentType = "image/png";
|
||||||
|
else if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg")) contentType = "image/jpeg";
|
||||||
// 必要に応じて他のMIMEタイプを追加
|
// 必要に応じて他のMIMEタイプを追加
|
||||||
return new Response(file, { headers: { "Content-Type": contentType } });
|
return new Response(file, { headers: { "Content-Type": contentType } });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Error serving file from frontend build dir ${filePath}:`, e);
|
console.error(`Error serving Next.js static file ${filePath}:`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user