๐Ÿš€ ่ถ…ๅ‰็ง‘ๆŠ€ๆ„Ÿไธชไบบๅšๅฎข โ€” ๅ‰ๅŽ็ซฏๅˆ†็ฆปๆžถๆž„่ฎพ่ฎก๏ผˆๅ…จๆ ˆ็‰ˆ๏ผ‰

้ข„่ฎก้˜…่ฏปๆ—ถ้—ด๏ผš19 ๅˆ†้’Ÿ

๐Ÿš€ ่ถ…ๅ‰็ง‘ๆŠ€ๆ„Ÿไธชไบบๅšๅฎข โ€” ๅ‰ๅŽ็ซฏๅˆ†็ฆปๆžถๆž„่ฎพ่ฎก

ไธ€ใ€ๆ•ดไฝ“ๆžถๆž„ๆฆ‚่งˆ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                          CDN / ่ดŸ่ฝฝๅ‡่กก                          โ”‚
โ”‚                        Cloudflare / Nginx                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                        โ”‚                โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”
    โ”‚ ๅ‰็ซฏ SPA โ”‚            โ”‚ ๅŽ็ซฏ API     โ”‚   โ”‚ ็ฎก็†ๅŽๅฐ โ”‚
    โ”‚ React +  โ”‚   HTTP/RESTโ”‚ NestJS      โ”‚   โ”‚ React   โ”‚
    โ”‚ Vite     โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ + WebSocket  โ”‚   โ”‚ Admin   โ”‚
    โ”‚          โ”‚            โ”‚              โ”‚   โ”‚         โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                   โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚              โ”‚                   โ”‚
              โ”Œโ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚ PostgreSQL โ”‚  โ”‚  Redis  โ”‚       โ”‚  MinIO/S3   โ”‚
              โ”‚  ไธปๆ•ฐๆฎๅบ“   โ”‚  โ”‚  ็ผ“ๅญ˜   โ”‚       โ”‚  ๅฏน่ฑกๅญ˜ๅ‚จ   โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚ (ๅ›พ็‰‡/ๆ–‡ไปถ)  โ”‚
                                                โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
ๅฑ‚็บง ๆŠ€ๆœฏ้€‰ๅž‹ ไฝœ็”จ
ๅ‰็ซฏ React 19 + Vite + TypeScript ็”จๆˆท็•Œ้ข๏ผŒSSR ๅฏ้€‰
ๅŽ็ซฏ NestJS + TypeScript RESTful API + WebSocket
ๆ•ฐๆฎๅบ“ PostgreSQL 16 ไธปๆ•ฐๆฎๅญ˜ๅ‚จ
็ผ“ๅญ˜ Redis 7 ไผš่ฏ็ผ“ๅญ˜ใ€ๆ–‡็ซ ็ผ“ๅญ˜ใ€้˜Ÿๅˆ—
ๅญ˜ๅ‚จ MinIO / ้˜ฟ้‡Œไบ‘OSS ๅ›พ็‰‡ใ€้™„ไปถใ€้™ๆ€่ต„ๆบ
็ฝ‘ๅ…ณ Nginx / Cloudflare ๅๅ‘ไปฃ็†ใ€้™ๆตใ€SSL

ไบŒใ€้กถ็บง้กน็›ฎ็ป“ๆž„๏ผˆMonorepo๏ผ‰

blog/
โ”œโ”€โ”€ packages/                     # ๅ…ฑไบซๅŒ…
โ”‚   โ”œโ”€โ”€ shared/                   # ๅ…ฑไบซ็ฑปๅž‹ใ€DTOใ€ๅธธ้‡
โ”‚   โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ types/            # ๅ…ฑ็”จ TS ็ฑปๅž‹ (IArticle, IUser ็ญ‰)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dto/              # ่ฏทๆฑ‚/ๅ“ๅบ” DTO
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ enums/            # ๆžšไธพๅธธ้‡
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ utils/            # ๅ…ฑไบซๅทฅๅ…ทๅ‡ฝๆ•ฐ
โ”‚   โ”‚   โ””โ”€โ”€ package.json
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ eslint-config/            # ็ปŸไธ€ ESLint ้…็ฝฎ
โ”‚
โ”œโ”€โ”€ apps/
โ”‚   โ”œโ”€โ”€ client/                   # ===== ๅ‰็ซฏ (SPA) =====
โ”‚   โ”‚   โ”œโ”€โ”€ public/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ fonts/            # ่‡ชๅฎšไน‰ๅญ—ไฝ“
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ images/           # ้™ๆ€ๅ›พ็‰‡
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ models/           # 3D ๆจกๅž‹ๆ–‡ไปถ
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ main.tsx          # ๅ…ฅๅฃ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ App.tsx           # ๆ น็ป„ไปถ๏ผˆ่ทฏ็”ฑ + ๅ…จๅฑ€ Provider๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ pages/            # ่ทฏ็”ฑ้กต้ข
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Home/         # ้ฆ–้กต๏ผˆTerminal ๅ…ฅๅฃ + ๆœ€ๆ–ฐๆ–‡็ซ ๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Posts/        # ๆ–‡็ซ ๅˆ—่กจ + ่ฏฆๆƒ…
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Projects/     # ้กน็›ฎๅฑ•็คบๅข™
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ About/        # ๅ…ณไบŽ้กต + ๆŠ€่ƒฝๆ˜Ÿ็›˜
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Guestbook/    # ็•™่จ€ๆฟ
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Snippets/     # ไปฃ็ ็‰‡ๆฎต
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ NotFound/     # 404 ๆ•…้šœ้ฃŽ
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ components/       # UI ็ป„ไปถ็›ฎๅฝ•
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ui/           # ๅŸบ็ก€็ป„ไปถ๏ผˆButton, Card, Input...๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Button.tsx      # ้œ“่™นๅ…‰ๆ•ˆๆŒ‰้’ฎ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Card.tsx        # ็Žป็’ƒๆ€ๅก็‰‡
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Input.tsx       # ๅ‘ๅ…‰่พ“ๅ…ฅๆก†
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Badge.tsx       # ๅ…จๆฏๆ ‡็ญพ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Modal.tsx       # ๆฏ›็Žป็’ƒๆจกๆ€ๆก†
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Toast.tsx       # ๆตๅŠจๅ…‰ๆ็คบ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ layout/       # ๅธƒๅฑ€็ป„ไปถ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Navbar.tsx       # ๆ‚ฌๆตฎๅฏผ่ˆช๏ผˆๆปš่ฝฎๅ˜่‰ฒ๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Footer.tsx       # ๅบ•้ƒจๆ˜Ÿๅบง่ฟž็บฟ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Sidebar.tsx      # ไพง่พนๆ ็›ฎๅฝ•
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ MobileNav.tsx    # ็งปๅŠจ็ซฏๆŠฝๅฑ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ effects/      # ็‰นๆ•ˆ็ป„ไปถ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ParticleBackground.tsx  # Three.js ็ฒ’ๅญ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ GridGlow.tsx           # ็ฝ‘ๆ ผๅ…‰ๆ•ˆ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ GlitchText.tsx         # ๆ•…้šœๆ–‡ๅญ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ TypeWriter.tsx         # ๆ‰“ๅญ—ๆœบ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ScanLine.tsx           # CRT ๆ‰ซๆ็บฟ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ Aurora.tsx             # ๆžๅ…‰ๅŠจๆ€่ƒŒๆ™ฏ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ blog/         # ๅšๅฎข็ป„ไปถ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ PostCard.tsx        # 3D ๅ€พๆ–œๅก็‰‡
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ PostHeader.tsx      # ๆ–‡็ซ ๅคด้ƒจ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ TableOfContents.tsx # ๆ‚ฌๆตฎ็›ฎๅฝ•
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ReadingProgress.tsx # ๅ…‰ๆ•ˆ่ฟ›ๅบฆๆก
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ CodeBlock.tsx       # ้œ“่™นไปฃ็ ๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ CommentSection.tsx  # ่ฏ„่ฎบๅŒบ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ home/         # ้ฆ–้กต็ป„ไปถ
โ”‚   โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ HeroSection.tsx     # 3D ๆฌข่ฟŽๅŒบ
โ”‚   โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ TerminalIntro.tsx   # Terminal ไบคไบ’
โ”‚   โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ FeaturedPosts.tsx   # ็ฒพ้€‰่ฝฎๆ’ญ
โ”‚   โ”‚   โ”‚   โ”‚       โ””โ”€โ”€ StatsBoard.tsx      # ๆ•ฐๆฎ้ขๆฟ
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ hooks/            # ่‡ชๅฎšไน‰ Hooks
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ useMousePosition.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ useScrollProgress.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ useIntersectionObserver.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ useApi.ts              # ๅฐ่ฃ… API ่ฏทๆฑ‚
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ services/         # API ่ฐƒ็”จๅฑ‚
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ http.ts               # Axios ๅฎžไพ‹๏ผˆๆ‹ฆๆˆชๅ™จ๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ postService.ts        # ๆ–‡็ซ ๆจกๅ— API
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ projectService.ts     # ้กน็›ฎๆจกๅ— API
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ commentService.ts     # ่ฏ„่ฎบๆจกๅ— API
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ authService.ts        # ่ฎค่ฏๆจกๅ— API
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ guestbookService.ts   # ็•™่จ€ๆจกๅ— API
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ stores/           # ็Šถๆ€็ฎก็†๏ผˆZustand๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ useThemeStore.ts      # ไธป้ข˜็Šถๆ€
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ useUserStore.ts       # ็”จๆˆท็Šถๆ€
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ useUIStore.ts         # UI ็Šถๆ€๏ผˆไพง่พนๆ ใ€ๅผน็ช—๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ styles/           # ๆ ทๅผ
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ globals.css           # ๅ…จๅฑ€ๆ ทๅผ + CSS ๅ˜้‡
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ animations.css        # ๅ…ณ้”ฎๅธงๅŠจ็”ป
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ markdown.css          # ๆ–‡็ซ ๅ†…ๅฎนๆ ทๅผ
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ utils/            # ๅทฅๅ…ทๅ‡ฝๆ•ฐ
โ”‚   โ”‚   โ”‚       โ””โ”€โ”€ cn.ts                 # classNames ๅˆๅนถ
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”œโ”€โ”€ index.html
โ”‚   โ”‚   โ”œโ”€โ”€ vite.config.ts
โ”‚   โ”‚   โ”œโ”€โ”€ tailwind.config.ts
โ”‚   โ”‚   โ”œโ”€โ”€ tsconfig.json
โ”‚   โ”‚   โ””โ”€โ”€ package.json
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ server/                   # ===== ๅŽ็ซฏ API (NestJS) =====
โ”‚   โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ main.ts                   # ๅ…ฅๅฃ๏ผˆๅฏๅŠจ HTTP + WebSocket๏ผ‰
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ app.module.ts             # ๆ นๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ common/                   # ๅ…ฌๅ…ฑๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ decorators/           # ่‡ชๅฎšไน‰่ฃ…้ฅฐๅ™จ
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ filters/              # ๅผ‚ๅธธ่ฟ‡ๆปคๅ™จ
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ guards/               # ๅฎˆๅซ๏ผˆJWT Auth Guard๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ interceptors/         # ๆ‹ฆๆˆชๅ™จ๏ผˆๆ—ฅๅฟ—ใ€ๅ“ๅบ”ๆ ผๅผ๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ pipes/                # ็ฎก้“๏ผˆๅ‚ๆ•ฐๆ ก้ชŒ๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ middleware/           # ไธญ้—ดไปถ๏ผˆ้™ๆตใ€CORS๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ constants/            # ๅ…จๅฑ€ๅธธ้‡
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ modules/                  # ไธšๅŠกๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ posts/                # ๆ–‡็ซ ๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ posts.module.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ posts.controller.ts    # REST ่ทฏ็”ฑ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ posts.service.ts        # ไธšๅŠก้€ป่พ‘
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ posts.gateway.ts        # WebSocket ็ฝ‘ๅ…ณ
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dto/                    # ่ฏทๆฑ‚ DTO
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ entities/               # ๅฎžไฝ“/ๆจกๅž‹
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ posts.controller.spec.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ projects/             # ้กน็›ฎๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ comments/             # ่ฏ„่ฎบๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ guestbook/            # ็•™่จ€ๆฟๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ snippets/             # ไปฃ็ ็‰‡ๆฎตๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ auth/                 # ่ฎค่ฏๆจกๅ—๏ผˆJWT + OAuth๏ผ‰
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ users/                # ็”จๆˆทๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ tags/                 # ๆ ‡็ญพๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ analytics/            # ๆ•ฐๆฎ็ปŸ่ฎกๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ upload/               # ๆ–‡ไปถไธŠไผ ๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ search/               # ๆœ็ดขๆจกๅ—
โ”‚   โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ config/                   # ้…็ฝฎๆจกๅ—
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ database.config.ts     # ๆ•ฐๆฎๅบ“้…็ฝฎ
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ redis.config.ts        # Redis ้…็ฝฎ
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ jwt.config.ts          # JWT ้…็ฝฎ
โ”‚   โ”‚   โ”‚       โ””โ”€โ”€ cors.config.ts         # CORS ้…็ฝฎ
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”œโ”€โ”€ prisma/                       # Prisma ORM
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ schema.prisma             # ๆ•ฐๆฎๅบ“ๆจกๅž‹ๅฎšไน‰
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ seed.ts                   # ็งๅญๆ•ฐๆฎ
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ”œโ”€โ”€ test/                         # ็ซฏๅˆฐ็ซฏๆต‹่ฏ•
โ”‚   โ”‚   โ”œโ”€โ”€ nest-cli.json
โ”‚   โ”‚   โ”œโ”€โ”€ tsconfig.json
โ”‚   โ”‚   โ””โ”€โ”€ package.json
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ admin/                     # ===== ็ฎก็†ๅŽๅฐ =====
โ”‚       โ”œโ”€โ”€ src/
โ”‚       โ”‚   โ”œโ”€โ”€ pages/             # ๅŽๅฐ้กต้ข
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ Dashboard/     # ไปช่กจ็›˜
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ Posts/         # ๆ–‡็ซ ็ฎก็†๏ผˆMarkdown ็ผ–่พ‘ๅ™จ๏ผ‰
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ Comments/      # ่ฏ„่ฎบๅฎกๆ ธ
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ Users/         # ็”จๆˆท็ฎก็†
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ Settings/      # ็ซ™็‚น่ฎพ็ฝฎ
โ”‚       โ”‚   โ”‚   โ””โ”€โ”€ Analytics/     # ๆ•ฐๆฎ็œ‹ๆฟ
โ”‚       โ”‚   โ”‚
โ”‚       โ”‚   โ”œโ”€โ”€ components/        # ๅŽๅฐ็ป„ไปถ
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ RichEditor.tsx # ๅฏŒๆ–‡ๆœฌ/Markdown ็ผ–่พ‘ๅ™จ
โ”‚       โ”‚   โ”‚   โ”œโ”€โ”€ ImageUploader.tsx
โ”‚       โ”‚   โ”‚   โ””โ”€โ”€ DataTable.tsx
โ”‚       โ”‚   โ”‚
โ”‚       โ”‚   โ”œโ”€โ”€ services/          # API ่ฐƒ็”จ
โ”‚       โ”‚   โ””โ”€โ”€ hooks/
โ”‚       โ”‚
โ”‚       โ”œโ”€โ”€ vite.config.ts
โ”‚       โ””โ”€โ”€ package.json
โ”‚
โ”œโ”€โ”€ docker/                        # Docker ็ผ–ๆŽ’
โ”‚   โ”œโ”€โ”€ docker-compose.yml         # ๅ…จๆ ˆไธ€้”ฎๅฏๅŠจ
โ”‚   โ”œโ”€โ”€ Dockerfile.client
โ”‚   โ”œโ”€โ”€ Dockerfile.server
โ”‚   โ””โ”€โ”€ nginx/
โ”‚       โ”œโ”€โ”€ nginx.conf             # ๅๅ‘ไปฃ็†้…็ฝฎ
โ”‚       โ””โ”€โ”€ ssl/                   # SSL ่ฏไนฆ
โ”‚
โ”œโ”€โ”€ scripts/                       # ๅผ€ๅ‘/้ƒจ็ฝฒ่„šๆœฌ
โ”‚   โ”œโ”€โ”€ dev.sh                     # ๆœฌๅœฐๅผ€ๅ‘ๅฏๅŠจ
โ”‚   โ”œโ”€โ”€ seed.sh                    # ๆ•ฐๆฎๅˆๅง‹ๅŒ–
โ”‚   โ””โ”€โ”€ deploy.sh                  # ้ƒจ็ฝฒ่„šๆœฌ
โ”‚
โ”œโ”€โ”€ .github/                       # CI/CD
โ”‚   โ””โ”€โ”€ workflows/
โ”‚       โ”œโ”€โ”€ ci.yml                 # PR ่‡ชๅŠจๆต‹่ฏ•
โ”‚       โ””โ”€โ”€ deploy.yml             # ่‡ชๅŠจ้ƒจ็ฝฒ
โ”‚
โ”œโ”€โ”€ .env.example                   # ็Žฏๅขƒๅ˜้‡ๆจกๆฟ
โ”œโ”€โ”€ .gitignore
โ”œโ”€โ”€ package.json                   # ๆ น Monorepo ้…็ฝฎ
โ”œโ”€โ”€ pnpm-workspace.yaml           # pnpm workspace
โ”œโ”€โ”€ turbo.json                     # Turborepo ๆž„ๅปบ็ผ“ๅญ˜
โ””โ”€โ”€ README.md

ไธ‰ใ€ๆ•ฐๆฎๅบ“่ฎพ่ฎก (Prisma Schema)

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// ---- ็”จๆˆท ----
model User {
  id        String   @id @default(cuid())
  username  String   @unique
  email     String   @unique
  password  String
  avatar    String?
  bio       String?
  role      Role     @default(USER)     // USER | ADMIN
  githubId  String?  @unique            // OAuth ็ป‘ๅฎš
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  posts       Post[]
  comments    Comment[]
  guestbooks  Guestbook[]
  likes       Like[]
  sessions    Session[]
}

// ---- ๆ–‡็ซ  ----
model Post {
  id          String   @id @default(cuid())
  title       String
  slug        String   @unique
  excerpt     String?                     // ๆ–‡็ซ ๆ‘˜่ฆ
  content     String                      // Markdown ๆญฃๆ–‡
  coverImage  String?                     // ๅฐ้ขๅ›พ
  published   Boolean  @default(false)
  pinned      Boolean  @default(false)    // ๆ˜ฏๅฆ็ฝฎ้กถ
  views       Int      @default(0)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  publishedAt DateTime?

  authorId  String
  author    User     @relation(fields: [authorId], references: [id])

  tags      TagRelation[]
  comments  Comment[]
  likes     Like[]
}

// ---- ๆ ‡็ญพ ----
model Tag {
  id        String @id @default(cuid())
  name      String @unique
  color     String?                    // ๆ ‡็ญพ้ขœ่‰ฒ๏ผˆ็”จไบŽ UI๏ผ‰

  posts     TagRelation[]
}

model TagRelation {
  postId String
  tagId  String
  post   Post @relation(fields: [postId], references: [id], onDelete: Cascade)
  tag    Tag  @relation(fields: [tagId], references: [id], onDelete: Cascade)

  @@id([postId, tagId])
}

// ---- ้กน็›ฎ ----
model Project {
  id          String   @id @default(cuid())
  title       String
  slug        String   @unique
  description String
  content     String?                    // ่ฏฆ็ป†ไป‹็ป๏ผˆMarkdown๏ผ‰
  coverImage  String?
  techStack   String[]                   // ["React", "Three.js", "Go"]
  liveUrl     String?                    // ็บฟไธŠๅœฐๅ€
  githubUrl   String?                    // ๆบ็ ๅœฐๅ€
  featured    Boolean  @default(false)
  sortOrder   Int      @default(0)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

// ---- ่ฏ„่ฎบ ----
model Comment {
  id        String   @id @default(cuid())
  content   String
  postId    String
  post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  parentId  String?                      // ๅ›žๅคๆŸๆก่ฏ„่ฎบ๏ผˆๅตŒๅฅ—่ฏ„่ฎบ๏ผ‰
  parent    Comment?  @relation("CommentReplies", fields: [parentId], references: [id])
  replies   Comment[] @relation("CommentReplies")
  likes     Int      @default(0)
  createdAt DateTime @default(now())
}

// ---- ็•™่จ€ๆฟ ----
model Guestbook {
  id        String   @id @default(cuid())
  content   String
  authorId  String
  author    User     @relation(fields: [authorId], references: [id])
  createdAt DateTime @default(now())
}

// ---- ็‚น่ตž ----
model Like {
  id        String   @id @default(cuid())
  userId    String
  user      User     @relation(fields: [userId], references: [id])
  postId    String?
  post      Post?    @relation(fields: [postId], references: [id], onDelete: Cascade)
  commentId String?
  comment   Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade)
  createdAt DateTime @default(now())

  @@unique([userId, postId])      // ไธ€็ฏ‡ๆ–‡็ซ ไธ€ไธช็”จๆˆทๅช่ƒฝ็‚นไธ€ๆฌก่ตž
}

// ---- ็ซ™็‚น็ปŸ่ฎก ----
model SiteStats {
  id         String @id @default("main")
  totalViews Int    @default(0)
  totalPosts Int    @default(0)
  totalUsers Int    @default(0)
  updatedAt  DateTime @updatedAt
}

// ---- ไผš่ฏ ----
model Session {
  id        String   @id @default(cuid())
  userId    String
  user      User     @relation(fields: [userId], references: [id])
  token     String   @unique
  expiresAt DateTime
  createdAt DateTime @default(now())
}

enum Role {
  USER
  ADMIN
}

ๅ››ใ€API ๆŽฅๅฃ่ฎพ่ฎก (RESTful)

4.1 ๆ–‡็ซ ๆจกๅ— /api/v1/posts

ๆ–นๆณ• ่ทฏๅพ„ ่ฏดๆ˜Ž ๆƒ้™
GET /posts ๆ–‡็ซ ๅˆ—่กจ๏ผˆๅˆ†้กต+็ญ›้€‰+ๆŽ’ๅบ๏ผ‰ ๅ…ฌๅผ€
GET /posts/:slug ๆ–‡็ซ ่ฏฆๆƒ… ๅ…ฌๅผ€
POST /posts ๅˆ›ๅปบๆ–‡็ซ  ADMIN
PUT /posts/:id ๆ›ดๆ–ฐๆ–‡็ซ  ADMIN
DELETE /posts/:id ๅˆ ้™คๆ–‡็ซ  ADMIN
POST /posts/:id/views ้˜…่ฏป้‡ +1 ๅ…ฌๅผ€
POST /posts/:id/like ็‚น่ตž/ๅ–ๆถˆ USER

4.2 ๅ…ถไป–ๆจกๅ—

ๆ–นๆณ• ่ทฏๅพ„ ่ฏดๆ˜Ž
GET/POST /api/v1/comments ่ฏ„่ฎบๅˆ—่กจ / ๅ‘่กจ่ฏ„่ฎบ
GET/POST /api/v1/guestbook ็•™่จ€ๅˆ—่กจ / ๅ‘่กจ็•™่จ€
GET/POST /api/v1/projects ้กน็›ฎๅˆ—่กจ / CRUD
POST /api/v1/auth/login ็™ปๅฝ•๏ผˆJWT๏ผ‰
POST /api/v1/auth/register ๆณจๅ†Œ
POST /api/v1/auth/github GitHub OAuth
GET /api/v1/search?q=xxx ๅ…จ็ซ™ๆœ็ดข
POST /api/v1/upload ๆ–‡ไปถไธŠไผ 
GET /api/v1/analytics ็ปŸ่ฎกๆ•ฐๆฎ

4.3 WebSocket ไบ‹ไปถ

ไบ‹ไปถ ๆ–นๅ‘ ่ฏดๆ˜Ž
post:view Client โ†’ Server ๅฎžๆ—ถ้˜…่ฏป้‡ๆŽจ้€
post:updated Server โ†’ Client ๆ–‡็ซ ๆ›ดๆ–ฐ้€š็Ÿฅ
comment:new Server โ†’ Client ๆ–ฐ่ฏ„่ฎบ้€š็Ÿฅ
online:count Server โ†’ Client ๅฎžๆ—ถๅœจ็บฟไบบๆ•ฐ

ไบ”ใ€ๆ•ฐๆฎๆต่ฎพ่ฎก

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ็”จๆˆทๆ“ไฝœ   โ”‚ โ”€โ”€โ†’โ”‚  ๅ‰็ซฏ Service โ”‚ โ”€โ”€โ†’โ”‚  NestJS API  โ”‚ โ”€โ”€โ†’โ”‚    DB    โ”‚
โ”‚ (็‚นๅ‡ป/่พ“ๅ…ฅ)โ”‚    โ”‚ (Axios)     โ”‚     โ”‚ (Controller) โ”‚     โ”‚ Prisma   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                            โ”‚
                                     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                     โ”‚  Redis ็ผ“ๅญ˜   โ”‚ โ—„โ”€โ”€โ”‚  ็ผ“ๅญ˜็ญ–็•ฅ โ”‚
                                     โ”‚              โ”‚     โ”‚          โ”‚
                                     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

็ผ“ๅญ˜็ญ–็•ฅ:
  Cache-Aside: ่ฏป: ๅ…ˆๆŸฅ Redis โ†’ ๆœชๅ‘ฝไธญ โ†’ ๆŸฅ DB โ†’ ๅ†™ๅ›ž Redis
               ๅ†™: ๆ›ดๆ–ฐ DB โ†’ ๅˆ ้™ค็ผ“ๅญ˜ โ†’ ไธ‹ๆฌก่ฏป้‡ๅปบ
  TTL:         ๆ–‡็ซ ่ฏฆๆƒ…: 1h / ๆ–‡็ซ ๅˆ—่กจ: 30min / ๆ ‡็ญพ: 1h
  Cache Key:   blog:post:{slug}  /  blog:posts:page:{n}  /  blog:tags

ๅ…ญใ€ไบคไบ’่ฎพ่ฎก๏ผˆๅฎขๆˆท็ซฏๆธฒๆŸ“ๆต็จ‹๏ผ‰

้กต้ขๅŠ ่ฝฝ
  โ”‚
  โ”œโ”€โ”€ 1. ๅˆๅง‹ๅŒ– Three.js ็ฒ’ๅญ่ƒŒๆ™ฏ
  โ”‚     ๏ผˆไฝŽไผ˜ๅ…ˆ็บง๏ผŒไธๅฝฑๅ“้ฆ–ๅฑ๏ผ‰
  โ”‚
  โ”œโ”€โ”€ 2. ่Žทๅ–็ซ™็‚นๅ…ƒๆ•ฐๆฎ
  โ”‚     GET /api/v1/site-meta
  โ”‚     โ†’ ็ซ™็‚นๅใ€ๅคดๅƒใ€ๆ ‡่ฏญใ€ๆ ‡็ญพไบ‘
  โ”‚
  โ”œโ”€โ”€ 3. ้ฆ–้กต โ†’ ๅนถๅ‘่ฏทๆฑ‚
  โ”‚     โ”œโ”€โ”€ GET /api/v1/posts?page=1&limit=6  (ๆœ€ๆ–ฐๆ–‡็ซ )
  โ”‚     โ”œโ”€โ”€ GET /api/v1/projects?featured=true (็ฒพ้€‰้กน็›ฎ)
  โ”‚     โ””โ”€โ”€ GET /api/v1/analytics             (็ปŸ่ฎกๆ•ฐๆฎ)
  โ”‚
  โ””โ”€โ”€ 4. ๆ–‡็ซ ้กต โ†’ ็€‘ๅธƒ่ฏทๆฑ‚
        โ”œโ”€โ”€ GET /api/v1/posts/:slug           (ๆ–‡็ซ ๅ†…ๅฎน)
        โ”œโ”€โ”€ GET /api/v1/comments?postId=xxx   (่ฏ„่ฎบๅˆ—่กจ)
        โ””โ”€โ”€ WebSocket connect                 (ๅฎžๆ—ถๅœจ็บฟ)

ไธƒใ€็Žฏๅขƒ้…็ฝฎ & ้ƒจ็ฝฒ

7.1 ็Žฏๅขƒๅ˜้‡ (.env)

# ๆ•ฐๆฎๅบ“
DATABASE_URL="postgresql://user:pass@localhost:5432/blog"
REDIS_URL="redis://localhost:6379"

# JWT
JWT_SECRET="your-secret-key"
JWT_EXPIRES_IN="7d"

# OAuth
GITHUB_CLIENT_ID="xxx"
GITHUB_CLIENT_SECRET="xxx"

# ๅฏน่ฑกๅญ˜ๅ‚จ
OSS_ENDPOINT="https://oss-cn-hangzhou.aliyuncs.com"
OSS_ACCESS_KEY="xxx"
OSS_SECRET_KEY="xxx"
OSS_BUCKET="blog-assets"

# ๅ‰็ซฏ
VITE_API_BASE_URL="https://api.blog.com/api/v1"
VITE_WS_URL="wss://api.blog.com"

7.2 docker-compose.yml

version: "3.8"
services:
  client:
    build:
      context: ./apps/client
      dockerfile: ../../docker/Dockerfile.client
    ports:
      - "3000:80"
    depends_on:
      - server

  server:
    build:
      context: ./apps/server
      dockerfile: ../../docker/Dockerfile.server
    ports:
      - "4000:4000"
    env_file: .env
    depends_on:
      - postgres
      - redis

  admin:
    build:
      context: ./apps/admin
      dockerfile: ../../docker/Dockerfile.client
    ports:
      - "3001:80"
    depends_on:
      - server

  postgres:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: blog
      POSTGRES_USER: blog
      POSTGRES_PASSWORD: ${DB_PASSWORD}

  redis:
    image: redis:7-alpine
    volumes:
      - redisdata:/data
    command: redis-server --requirepass ${REDIS_PASSWORD}

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/ssl:/etc/nginx/ssl
    depends_on:
      - client
      - server
      - admin

volumes:
  pgdata:
  redisdata:

7.3 ้ƒจ็ฝฒๆžถๆž„

                         Cloudflare
                             โ”‚
                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                    โ”‚    Nginx ็ฝ‘ๅ…ณ    โ”‚
                    โ”‚  โ†’ SSL ็ปˆๆญข      โ”‚
                    โ”‚  โ†’ ้™ๆต / WAF   โ”‚
                    โ”‚  โ†’ ่ทฏ็”ฑๅˆ†ๅ‘      โ”‚
                    โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜
                        โ”‚    โ”‚    โ”‚
                   โ”Œโ”€โ”€โ”€โ”€โ–ผโ” โ”Œโ–ผโ”€โ”€โ”€โ” โ”Œโ–ผโ”€โ”€โ”€โ”
                   โ”‚ๅ‰็ซฏ  โ”‚ โ”‚ๅŽ็ซฏ โ”‚ โ”‚ๅŽๅฐ โ”‚
                   โ”‚:80   โ”‚ โ”‚:4000โ”‚ โ”‚:80  โ”‚
                   โ””โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”˜
                        โ”‚         โ”‚
                   โ”Œโ”€โ”€โ”€โ”€โ–ผโ”  โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”
                   โ”‚CDN   โ”‚  โ”‚PostgreSQLโ”‚
                   โ”‚้™ๆ€  โ”‚  โ”‚+ Redis   โ”‚
                   โ”‚็ผ“ๅญ˜  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ””โ”€โ”€โ”€โ”€โ”€โ”˜

้ƒจ็ฝฒๆ–นๆกˆ้€‰้กน:
  ๆ–นๆกˆ A: ๅ•ๆœบ Docker Compose๏ผˆๆŽจ่่ตทๆญฅ๏ผ‰
  ๆ–นๆกˆ B: K8s ้›†็พค๏ผˆ้ซ˜ๆต้‡ๆ—ถ่ฟ็งป๏ผ‰
  ๆ–นๆกˆ C: ไบ‘็ซฏๆ‰˜็ฎก๏ผˆVercel ๅ‰็ซฏ + Railway ๅŽ็ซฏ + Neon DB๏ผ‰

ๅ…ซใ€่ง†่ง‰่ฏญ่จ€็ณป็ปŸ๏ผˆ็ง‘ๆŠ€ๆ„Ÿ UI๏ผ‰

8.1 ่‰ฒๅฝฉไฝ“็ณป

:root {
  /* ่ƒŒๆ™ฏๅฑ‚็บง */
  --bg-deep:      #06060c;   /* ๆœ€ๆทฑ็ฉบ่ƒŒๆ™ฏ */
  --bg-primary:   #0a0a12;   /* ไธป่ƒŒๆ™ฏ */
  --bg-secondary: #11111a;   /* ๅก็‰‡/ๅŒบๅŸŸ่ƒŒๆ™ฏ */
  --bg-glass:     rgba(255, 255, 255, 0.03);  /* ็Žป็’ƒ่ƒŒๆ™ฏ */

  /* ๅ…‰ๆ•ˆ่‰ฒๅฝฉ */
  --accent-cyan:    #00e5ff;
  --accent-purple:  #b388ff;
  --accent-pink:    #ff2d95;
  --accent-gold:    #ffd54f;

  /* ๆ–‡ๅญ— */
  --text-primary:   #e8eaed;
  --text-secondary: #9aa0a6;
  --text-dim:       #5f6368;

  /* ่พ‰ๅ…‰ */
  --glow-cyan:     0 0 15px #00e5ff, 0 0 30px rgba(0, 229, 255, 0.3);
  --glow-purple:   0 0 15px #b388ff, 0 0 30px rgba(179, 136, 255, 0.3);

  /* ่พนๆก† */
  --border-glass:  1px solid rgba(255, 255, 255, 0.06);
  --border-glow:   1px solid rgba(0, 229, 255, 0.3);
}

8.2 ๅญ—ไฝ“ไฝ“็ณป

็”จ้€” ๅญ—ไฝ“ ๆฅๆบ
Logo / ๅคงๆ ‡้ข˜ Orbitron 700-900 Google Fonts
ๆญฃๆ–‡ๆ ‡้ข˜ Syne 600-700 Google Fonts
ๆญฃๆ–‡ Inter 300-500 Google Fonts
ไปฃ็  JetBrains Mono 400-600 ๆœฌๅœฐๅญ—ไฝ“
่ฃ…้ฅฐๆ–‡ๅญ— Major Mono Display Google Fonts

8.3 ็ง‘ๆŠ€ๆ„Ÿ UI ๅ…ƒ็ด ๆธ…ๅ•

ๅ…ƒ็ด  ๆŠ€ๆœฏๆ–นๆกˆ ๆ•ˆๆžœๆ่ฟฐ
ๅ…จๅฑ€็ฒ’ๅญ่ƒŒๆ™ฏ Three.js + R3F ๅŠจๆ€็ฒ’ๅญๆต๏ผŒ้ผ ๆ ‡ไบคไบ’ไบง็”Ÿๆณข็บน
็ฝ‘ๆ ผๅ…‰ๆ•ˆ Canvas 2D / CSS ็ง‘ๆŠ€็ฝ‘ๆ ผ + ๆธๅ˜ๅ…‰ๆ™•
CRT ๆ‰ซๆ็บฟ CSS pseudo-element ๅคๅคๅฑๅน•ๅ ๅฑ‚๏ผŒๅฏ้€‰ๅผ€ๅ…ณ
็Žป็’ƒๅก็‰‡ backdrop-filter: blur ๆฏ›็Žป็’ƒ + ่พนๆก†ๅ‘ๅ…‰
ๆ•…้šœๆ–‡ๅญ— CSS @keyframes clip ๆ–‡ๅญ—้”™ไฝ + ้ขœ่‰ฒๅ็งป้—ช็ƒ
ๆ‰“ๅญ—ๆœบๆ•ˆๆžœ JS cursor + setInterval Terminal ้ฃŽๆ ผ้€ๅญ—่พ“ๅ‡บ
้œ“่™นๆŒ‰้’ฎ box-shadow + filter ๆ‚ฌๆตฎๆ—ถๅฝฉ่‰ฒ่พ‰ๅ…‰ๆ‰ฉๆ•ฃ
3D ๅ€พๆ–œๅก็‰‡ useMousePosition + transform ้ผ ๆ ‡็งปๅŠจๆ—ถๅก็‰‡ 3D ๆ—‹่ฝฌ
ๆปšๅŠจ่ฟ›ๅบฆๆก CSS linear-gradient + JS ๅ›บๅฎšๅœจ้กถ้ƒจ็š„ๅ…‰ๆ•ˆ่ฟ›ๅบฆๆก
ๅŠ ่ฝฝ้ชจๆžถๅฑ CSS pulse ๅŠจ็”ป ๅ†…ๅฎนๅŠ ่ฝฝๆ—ถ็š„็ง‘ๆŠ€่ฝฎๅป“ๅ ไฝ

ไนใ€ๅ…ณ้”ฎ npm ๅŒ…ๆธ…ๅ•

9.1 ๅ‰็ซฏ (apps/client)

{
  "dependencies": {
    "react": "^19.x",
    "react-dom": "^19.x",
    "react-router-dom": "^7.x",
    "framer-motion": "^11.x",
    "three": "^0.170.x",
    "@react-three/fiber": "^8.x",
    "@react-three/postprocessing": "^2.x",
    "axios": "^1.x",
    "zustand": "^5.x",
    "dayjs": "^1.x",
    "clsx": "^2.x",
    "tailwind-merge": "^2.x",
    "react-markdown": "^9.x",
    "react-syntax-highlighter": "^15.x",
    "react-helmet-async": "^2.x",
    "swr": "^2.x"
  },
  "devDependencies": {
    "typescript": "^5.x",
    "vite": "^6.x",
    "tailwindcss": "^4.x",
    "@tailwindcss/typography": "^0.5.x",
    "postcss": "^8.x",
    "autoprefixer": "^10.x",
    "eslint": "^9.x",
    "prettier": "^3.x"
  }
}

9.2 ๅŽ็ซฏ (apps/server)

{
  "dependencies": {
    "@nestjs/core": "^11.x",
    "@nestjs/common": "^11.x",
    "@nestjs/platform-express": "^11.x",
    "@nestjs/websockets": "^11.x",
    "@nestjs/jwt": "^11.x",
    "@nestjs/passport": "^11.x",
    "@nestjs/throttler": "^6.x",
    "@nestjs/serve-static": "^5.x",
    "@prisma/client": "^6.x",
    "passport": "^0.7.x",
    "passport-jwt": "^5.x",
    "passport-github2": "^0.1.x",
    "bcrypt": "^5.x",
    "class-validator": "^0.14.x",
    "class-transformer": "^0.5.x",
    "redis": "^4.x",
    "ioredis": "^5.x",
    "socket.io": "^4.x",
    "sharp": "^0.33.x",
    "minio": "^8.x",
    "helmet": "^8.x",
    "reflect-metadata": "^0.2.x",
    "rxjs": "^7.x"
  },
  "devDependencies": {
    "@nestjs/cli": "^11.x",
    "@nestjs/testing": "^11.x",
    "@types/node": "^22.x",
    "prisma": "^6.x",
    "ts-node": "^10.x",
    "ts-loader": "^9.x",
    "typescript": "^5.x",
    "jest": "^29.x",
    "supertest": "^7.x"
  }
}

ๅใ€ๅผ€ๅ‘่ทฏ็บฟๅ›พ

Phase 1 โ€” Monorepo ้ชจๆžถ๏ผˆDay 1-2๏ผ‰
  โ–ก ๅˆๅง‹ๅŒ– pnpm workspace + Turborepo
  โ–ก ๆญๅปบ apps/client (Vite + React + TS)
  โ–ก ๆญๅปบ apps/server (NestJS + Prisma)
  โ–ก ๆญๅปบ packages/shared
  โ–ก ้…็ฝฎ ESLint + Prettier ็ปŸไธ€่ง„ๅˆ™

Phase 2 โ€” ๆ•ฐๆฎๅบ“ + API๏ผˆDay 3-6๏ผ‰
  โ–ก ็ผ–ๅ†™ Prisma Schema โ†’ ็”Ÿๆˆ่ฟ็งป
  โ–ก ๅฎž็Žฐ User / Auth ๆจกๅ—๏ผˆJWT + GitHub OAuth๏ผ‰
  โ–ก ๅฎž็Žฐ Post CRUD + ๅˆ†้กต + ็ญ›้€‰
  โ–ก ๅฎž็Žฐ Commentใ€Guestbookใ€Project ๆจกๅ—
  โ–ก ๆทปๅŠ  Redis ็ผ“ๅญ˜ๅฑ‚

Phase 3 โ€” ๅ‰็ซฏๅŸบ็ก€ UI๏ผˆDay 7-12๏ผ‰
  โ–ก ็ง‘ๆŠ€ๆ„Ÿ่‰ฒๅฝฉ็ณป็ปŸ + ๅ…จๅฑ€ๆ ทๅผ
  โ–ก ๅธƒๅฑ€็ป„ไปถ๏ผˆNavbar / Footer / Sidebar๏ผ‰
  โ–ก ็‰นๆ•ˆ็ป„ไปถ๏ผˆ็ฒ’ๅญ่ƒŒๆ™ฏ / ็ฝ‘ๆ ผๅ…‰ๆ•ˆ / ๆ•…้šœๆ–‡ๅญ—๏ผ‰
  โ–ก ๅŸบ็ก€ UI ็ป„ไปถ๏ผˆButton / Card / Input / Modal๏ผ‰
  โ–ก ไธป้ข˜ๅˆ‡ๆข๏ผˆๆš—้ป‘ / ่ต›ๅš / ๆต…่‰ฒ๏ผ‰

Phase 4 โ€” ๅ‰็ซฏไบคไบ’๏ผˆDay 13-18๏ผ‰
  โ–ก API Service ๅฑ‚ + Zustand ็Šถๆ€็ฎก็†
  โ–ก ้ฆ–้กต๏ผˆTerminal ไบคไบ’ + ็ฒพ้€‰ๅ†…ๅฎน๏ผ‰
  โ–ก ๆ–‡็ซ ๅˆ—่กจ้กต๏ผˆ็ฝ‘ๆ ผ/ๆ—ถ้—ด่ฝดๅˆ‡ๆข + 3D ๅก็‰‡๏ผ‰
  โ–ก ๆ–‡็ซ ่ฏฆๆƒ…้กต๏ผˆไปฃ็ ้ซ˜ไบฎ + TOC + ่ฟ›ๅบฆๆก๏ผ‰
  โ–ก ้กน็›ฎๅฑ•็คบๅข™ + ๅ…ณไบŽ้กต

Phase 5 โ€” ๅŠจๆ€ๅŠŸ่ƒฝ๏ผˆDay 19-22๏ผ‰
  โ–ก ่ฏ„่ฎบ็ณป็ปŸ๏ผˆๅตŒๅฅ—ๅ›žๅค + @ๆๅŠ๏ผ‰
  โ–ก ็•™่จ€ๆฟ๏ผˆ็ฒ’ๅญๅข™ๅŠจๆ•ˆ๏ผ‰
  โ–ก ๅ…จ็ซ™ๆœ็ดข + ๆ ‡็ญพไบ‘
  โ–ก WebSocket ๅฎžๆ—ถๅœจ็บฟ + ้˜…่ฏป้‡

Phase 6 โ€” Admin ๅŽๅฐ๏ผˆDay 23-26๏ผ‰
  โ–ก ๅŽๅฐไปช่กจ็›˜
  โ–ก Markdown ็ผ–่พ‘ๅ™จ๏ผˆๆ–‡็ซ ๅ‘ๅธƒ๏ผ‰
  โ–ก ๅ›พ็‰‡ไธŠไผ  + ๆ–‡ไปถ็ฎก็†
  โ–ก ่ฏ„่ฎบๅฎกๆ ธ + ๆ•ฐๆฎ็œ‹ๆฟ

Phase 7 โ€” ้ƒจ็ฝฒไธŠ็บฟ๏ผˆDay 27-30๏ผ‰
  โ–ก Docker Compose ๅ…จๆ ˆ็ผ–ๆŽ’
  โ–ก Nginx ๅๅ‘ไปฃ็† + SSL
  โ–ก CI/CD ๆตๆฐด็บฟ๏ผˆGitHub Actions๏ผ‰
  โ–ก ๆ€ง่ƒฝไผ˜ๅŒ– + Lighthouse 90+
  โ–ก ่‡ชๅฎšไน‰ๅŸŸๅ + CDN

ๅไธ€ใ€ๆ€ง่ƒฝ็ญ–็•ฅ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   ๆ€ง่ƒฝไผ˜ๅŒ–็ญ–็•ฅ                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                        โ”‚
โ”‚  ๅ‰็ซฏไผ˜ๅŒ–                                               โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                                               โ”‚
โ”‚  โ€ข ไปฃ็ ๅˆ†ๅ‰ฒ: React.lazy + Suspense ๆŒ‰่ทฏ็”ฑๅˆ†ๅ‰ฒ            โ”‚
โ”‚  โ€ข ๅ›พ็‰‡ไผ˜ๅŒ–: ๅปถ่ฟŸๅŠ ่ฝฝ + WebP/AVIF ๆ ผๅผ + ๆจก็ณŠๅ ไฝ        โ”‚
โ”‚  โ€ข ๅญ—ไฝ“ไผ˜ๅŒ–: preload + font-display: swap + ๅญ้›†ๅŒ–      โ”‚
โ”‚  โ€ข 3D ไผ˜ๅŒ–: ็งปๅŠจ็ซฏ้™ไฝŽ็ฒ’ๅญๆ•ฐ (2000โ†’500)                 โ”‚
โ”‚  โ€ข ๅŠจ็”ปไผ˜ๅŒ–: ไป…็”จ transform/opacity, avoid ้‡ๆŽ’         โ”‚
โ”‚  โ€ข ่ฏทๆฑ‚ไผ˜ๅŒ–: SWR ็ผ“ๅญ˜ + ไน่ง‚ๆ›ดๆ–ฐ + ้˜ฒๆŠ–                 โ”‚
โ”‚  โ€ข ๆž„ๅปบไผ˜ๅŒ–: Vite ๆ‘‡ๆ ‘ + ๆŒ‰้œ€ polyfill                  โ”‚
โ”‚                                                        โ”‚
โ”‚  ๅŽ็ซฏไผ˜ๅŒ–                                               โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                                               โ”‚
โ”‚  โ€ข ๆ•ฐๆฎๅบ“: ็ดขๅผ•่ฆ†็›– + ๆŸฅ่ฏขไผ˜ๅŒ– + ่ฟžๆŽฅๆฑ                   โ”‚
โ”‚  โ€ข ็ผ“ๅญ˜: Redis Cache-Aside + ็ผ“ๅญ˜้ข„็ƒญ                   โ”‚
โ”‚  โ€ข ้™ๆต: @nestjs/throttler ้˜ฒๆญขๅˆทๆŽฅๅฃ                    โ”‚
โ”‚  โ€ข ๅŽ‹็ผฉ: ๅ“ๅบ” Gzip/Brotli ๅŽ‹็ผฉ                          โ”‚
โ”‚  โ€ข ่ฟžๆŽฅๆฑ : Prisma ่ฟžๆŽฅๆฑ  + ๅˆ็† max_connections          โ”‚
โ”‚                                                        โ”‚
โ”‚  ้ƒจ็ฝฒไผ˜ๅŒ–                                               โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                                               โ”‚
โ”‚  โ€ข CDN: ้™ๆ€่ต„ๆบๅ…จ้‡็ผ“ๅญ˜ๅˆฐ Cloudflare                    โ”‚
โ”‚  โ€ข Nginx: ้™ๆ€ๆ–‡ไปถ็›ดๆŽฅๅ“ๅบ”๏ผŒไธ็ป่ฟ‡ Node                   โ”‚
โ”‚  โ€ข ๆฐดๅนณๆ‰ฉๅฑ•: ๅŽ็ซฏๆ— ็Šถๆ€่ฎพ่ฎก๏ผŒๅฏๅคšๅฎžไพ‹้ƒจ็ฝฒ                 โ”‚
โ”‚  โ€ข ่‡ชๅŠจๅค‡ไปฝ: ๆฏๆ—ฅๆ•ฐๆฎๅบ“ + ๆ–‡ไปถๅฟซ็…ง                        โ”‚
โ”‚                                                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ๆœฌๆ–‡็”ฑ admin ๅŽŸๅˆ›๏ผŒ่ฝฌ่ฝฝ่ฏทๆณจๆ˜Žๅ‡บๅค„ใ€‚

็›ธๅ…ณๆŽจ่

ๆš‚ๆ— ่ฏ„่ฎบ๏ผŒๆฅๅ‘่กจ็ฌฌไธ€ๆก่ฏ„่ฎบๅง

ๅ‘่กจ่ฏ„่ฎบ

็™ปๅฝ• ๅŽๅ‘่กจ่ฏ„่ฎบ

ๅ‘็Žฐๆ›ดๅคš