# ๐Ÿ—๏ธ Whale Town ้กน็›ฎๆžถๆž„่ฎพ่ฎก > ๅŸบไบŽไธšๅŠกๅŠŸ่ƒฝๆจกๅ—ๅŒ–็š„็ŽฐไปฃๅŒ–ๅŽ็ซฏๆžถๆž„๏ผŒๆ”ฏๆŒๅŒๆจกๅผ่ฟ่กŒ๏ผŒๅผ€ๅ‘ๆต‹่ฏ•้›ถไพ่ต–๏ผŒ็”Ÿไบง้ƒจ็ฝฒ้ซ˜ๆ€ง่ƒฝใ€‚ ## ๐Ÿ“‹ ็›ฎๅฝ• - [๐ŸŽฏ ๆžถๆž„ๆฆ‚่ฟฐ](#-ๆžถๆž„ๆฆ‚่ฟฐ) - [๐Ÿ“ ็›ฎๅฝ•็ป“ๆž„่ฏฆ่งฃ](#-็›ฎๅฝ•็ป“ๆž„่ฏฆ่งฃ) - [๐Ÿ—๏ธ ๅˆ†ๅฑ‚ๆžถๆž„่ฎพ่ฎก](#๏ธ-ๅˆ†ๅฑ‚ๆžถๆž„่ฎพ่ฎก) - [๐Ÿ”„ ๅŒๆจกๅผๆžถๆž„](#-ๅŒๆจกๅผๆžถๆž„) - [๐Ÿ“ฆ ๆจกๅ—ไพ่ต–ๅ…ณ็ณป](#-ๆจกๅ—ไพ่ต–ๅ…ณ็ณป) - [๐Ÿš€ ๆ‰ฉๅฑ•ๆŒ‡ๅ—](#-ๆ‰ฉๅฑ•ๆŒ‡ๅ—) --- ## ๐ŸŽฏ ๆžถๆž„ๆฆ‚่ฟฐ Whale Town ้‡‡็”จ**ไธšๅŠกๅŠŸ่ƒฝๆจกๅ—ๅŒ–ๆžถๆž„**๏ผŒๅฐ†ไปฃ็ ๆŒ‰ไธšๅŠกๅŠŸ่ƒฝ่€Œ้žๆŠ€ๆœฏ็ป„ไปถ็ป„็ป‡๏ผŒ็กฎไฟ้ซ˜ๅ†…่šใ€ไฝŽ่€ฆๅˆ็š„่ฎพ่ฎกๅŽŸๅˆ™ใ€‚ ### ๐ŸŒŸ ๆ ธๅฟƒ่ฎพ่ฎก็†ๅฟต - **ไธšๅŠก้ฉฑๅŠจ** - ๆŒ‰ไธšๅŠกๅŠŸ่ƒฝ็ป„็ป‡ไปฃ็ ๏ผŒ่€Œ้žๆŠ€ๆœฏๅˆ†ๅฑ‚ - **ๅŒๆจกๅผๆ”ฏๆŒ** - ๅผ€ๅ‘ๆต‹่ฏ•้›ถไพ่ต–๏ผŒ็”Ÿไบง้ƒจ็ฝฒ้ซ˜ๆ€ง่ƒฝ - **ๆธ…ๆ™ฐๅˆ†ๅฑ‚** - ไธšๅŠกๅฑ‚ โ†’ ๆ ธๅฟƒๅฑ‚ โ†’ ๆ•ฐๆฎๅฑ‚๏ผŒ่Œ่ดฃๆ˜Ž็กฎ - **ๆจกๅ—ๅŒ–่ฎพ่ฎก** - ๆฏไธชๆจกๅ—็‹ฌ็ซ‹ๅฎŒๆ•ด๏ผŒๅฏๅ•็‹ฌๆต‹่ฏ•ๅ’Œ้ƒจ็ฝฒ - **้…็ฝฎ้ฉฑๅŠจ** - ้€š่ฟ‡็Žฏๅขƒๅ˜้‡ๆŽงๅˆถ่ฟ่กŒๆจกๅผๅ’Œ่กŒไธบ ### ๐Ÿ› ๏ธ ๆŠ€ๆœฏๆ ˆ #### ๅŽ็ซฏๆŠ€ๆœฏๆ ˆ - **ๆก†ๆžถ**: NestJS 11.x (ๅŸบไบŽExpress) - **่ฏญ่จ€**: TypeScript 5.x - **ๆ•ฐๆฎๅบ“**: MySQL + TypeORM (็”Ÿไบง) / ๅ†…ๅญ˜ๆ•ฐๆฎๅบ“ (ๅผ€ๅ‘) - **็ผ“ๅญ˜**: Redis + IORedis (็”Ÿไบง) / ๆ–‡ไปถๅญ˜ๅ‚จ (ๅผ€ๅ‘) - **่ฎค่ฏ**: JWT + bcrypt - **้ชŒ่ฏ**: class-validator + class-transformer - **ๆ–‡ๆกฃ**: Swagger/OpenAPI - **ๆต‹่ฏ•**: Jest + Supertest - **ๆ—ฅๅฟ—**: Pino + nestjs-pino - **WebSocket**: Socket.IO - **้‚ฎไปถ**: Nodemailer - **้›†ๆˆ**: Zulip API #### ๅ‰็ซฏๆŠ€ๆœฏๆ ˆ - **ๆก†ๆžถ**: React 18.x - **ๆž„ๅปบๅทฅๅ…ท**: Vite 7.x - **UIๅบ“**: Ant Design 5.x - **่ทฏ็”ฑ**: React Router DOM 6.x - **่ฏญ่จ€**: TypeScript 5.x ### ๐Ÿ“Š ๆ•ดไฝ“ๆžถๆž„ๅ›พ ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐ŸŒ APIๆŽฅๅฃๅฑ‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๐Ÿ”— REST API โ”‚ โ”‚ ๐Ÿ”Œ WebSocket โ”‚ โ”‚ ๐Ÿ“„ Swagger UI โ”‚ โ”‚ โ”‚ โ”‚ (HTTPๆŽฅๅฃ) โ”‚ โ”‚ (ๅฎžๆ—ถ้€šไฟก) โ”‚ โ”‚ (APIๆ–‡ๆกฃ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โฌ‡๏ธ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐ŸŽฏ ไธšๅŠกๅŠŸ่ƒฝๆจกๅ—ๅฑ‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๐Ÿ” ็”จๆˆท่ฎค่ฏ โ”‚ โ”‚ ๐Ÿ‘ฅ ็”จๆˆท็ฎก็† โ”‚ โ”‚ ๐Ÿ›ก๏ธ ็ฎก็†ๅ‘˜ โ”‚ โ”‚ โ”‚ โ”‚ (auth) โ”‚ โ”‚ (user_mgmt) โ”‚ โ”‚ (admin) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๐Ÿ’ฌ Zulip้›†ๆˆ โ”‚ โ”‚ ๐Ÿ”— ๅ…ฑไบซ็ป„ไปถ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (zulip) โ”‚ โ”‚ (shared) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โฌ‡๏ธ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โš™๏ธ ๆ ธๅฟƒๆŠ€ๆœฏๆœๅŠกๅฑ‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๐Ÿ”‘ ็™ปๅฝ•ๆ ธๅฟƒ โ”‚ โ”‚ ๐Ÿ‘‘ ็ฎก็†ๅ‘˜ๆ ธๅฟƒ โ”‚ โ”‚ ๐Ÿ’ฌ Zulipๆ ธๅฟƒ โ”‚ โ”‚ โ”‚ โ”‚ (auth_core) โ”‚ โ”‚ (admin_core) โ”‚ โ”‚ (zulip) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๐Ÿ›ก๏ธ ๅฎ‰ๅ…จๆ ธๅฟƒ โ”‚ โ”‚ ๐Ÿ› ๏ธ ๅทฅๅ…ทๆœๅŠก โ”‚ โ”‚ ๐Ÿ“ง ้‚ฎไปถๆœๅŠก โ”‚ โ”‚ โ”‚ โ”‚ (security_core)โ”‚ โ”‚ (utils) โ”‚ โ”‚ (email) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โฌ‡๏ธ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ—„๏ธ ๆ•ฐๆฎๅญ˜ๅ‚จๅฑ‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๐Ÿ—ƒ๏ธ ๆ•ฐๆฎๅบ“ โ”‚ โ”‚ ๐Ÿ”ด Redis็ผ“ๅญ˜ โ”‚ โ”‚ ๐Ÿ“ ๆ–‡ไปถๅญ˜ๅ‚จ โ”‚ โ”‚ โ”‚ โ”‚ (MySQL/ๅ†…ๅญ˜) โ”‚ โ”‚ (Redis/ๆ–‡ไปถ) โ”‚ โ”‚ (logs/data) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ๐Ÿ“ ็›ฎๅฝ•็ป“ๆž„่ฏฆ่งฃ ### ๐ŸŽฏ ไธšๅŠกๅŠŸ่ƒฝๆจกๅ— (`src/business/`) > **่ฎพ่ฎกๅŽŸๅˆ™**: ๆŒ‰ไธšๅŠกๅŠŸ่ƒฝ็ป„็ป‡๏ผŒๆฏไธชๆจกๅ—ๅŒ…ๅซๅฎŒๆ•ด็š„ไธšๅŠก้€ป่พ‘ ``` src/business/ โ”œโ”€โ”€ ๐Ÿ“‚ auth/ # ๐Ÿ” ็”จๆˆท่ฎค่ฏๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ auth.module.ts # ๆจกๅ—ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ controllers/ # ๆŽงๅˆถๅ™จ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ login.controller.ts # ็™ปๅฝ•ๆŽฅๅฃๆŽงๅˆถๅ™จ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ services/ # ไธšๅŠกๆœๅŠก โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ login.service.ts # ็™ปๅฝ•ไธšๅŠก้€ป่พ‘ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ login.service.spec.ts # ็™ปๅฝ•ๆœๅŠกๆต‹่ฏ• โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ dto/ # ๆ•ฐๆฎไผ ่พ“ๅฏน่ฑก โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ login.dto.ts # ็™ปๅฝ•่ฏทๆฑ‚DTO โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ login_response.dto.ts # ็™ปๅฝ•ๅ“ๅบ”DTO โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ guards/ # ๆƒ้™ๅฎˆๅซ๏ผˆ้ข„็•™๏ผ‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ user-mgmt/ # ๐Ÿ‘ฅ ็”จๆˆท็ฎก็†ๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ user-mgmt.module.ts # ๆจกๅ—ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ controllers/ # ๆŽงๅˆถๅ™จ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ user-status.controller.ts # ็”จๆˆท็Šถๆ€็ฎก็†ๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ services/ # ไธšๅŠกๆœๅŠก โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ user-management.service.ts # ็”จๆˆท็ฎก็†้€ป่พ‘ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ dto/ # ๆ•ฐๆฎไผ ่พ“ๅฏน่ฑก โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ user-status.dto.ts # ็”จๆˆท็Šถๆ€DTO โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ user-status-response.dto.ts # ็Šถๆ€ๅ“ๅบ”DTO โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ enums/ # ๆžšไธพๅฎšไน‰ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ user-status.enum.ts # ็”จๆˆท็Šถๆ€ๆžšไธพ โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ tests/ # ๆต‹่ฏ•ๆ–‡ไปถ๏ผˆ้ข„็•™๏ผ‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ admin/ # ๐Ÿ›ก๏ธ ็ฎก็†ๅ‘˜ๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ admin.controller.ts # ็ฎก็†ๅ‘˜ๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ admin.service.ts # ็ฎก็†ๅ‘˜ไธšๅŠก้€ป่พ‘ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ admin.module.ts # ๆจกๅ—ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ admin.service.spec.ts # ็ฎก็†ๅ‘˜ๆœๅŠกๆต‹่ฏ• โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ dto/ # ๆ•ฐๆฎไผ ่พ“ๅฏน่ฑก โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ guards/ # ๆƒ้™ๅฎˆๅซ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ zulip/ # ๐Ÿ’ฌ Zulip้›†ๆˆๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ zulip.service.ts # ZulipไธšๅŠกๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ zulip_websocket.gateway.ts # WebSocket็ฝ‘ๅ…ณ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ zulip.module.ts # ๆจกๅ—ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ interfaces/ # ๆŽฅๅฃๅฎšไน‰ โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ services/ # ๅญๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ message_filter.service.ts # ๆถˆๆฏ่ฟ‡ๆปค โ”‚ โ””โ”€โ”€ ๐Ÿ“„ session_cleanup.service.ts # ไผš่ฏๆธ…็† โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ shared/ # ๐Ÿ”— ๅ…ฑไบซไธšๅŠก็ป„ไปถ โ”œโ”€โ”€ ๐Ÿ“‚ dto/ # ๅ…ฑไบซๆ•ฐๆฎไผ ่พ“ๅฏน่ฑก โ””โ”€โ”€ ๐Ÿ“„ index.ts # ๅฏผๅ‡บๆ–‡ไปถ ``` ### โš™๏ธ ๆ ธๅฟƒๆŠ€ๆœฏๆœๅŠก (`src/core/`) > **่ฎพ่ฎกๅŽŸๅˆ™**: ๆไพ›ๆŠ€ๆœฏๅŸบ็ก€่ฎพๆ–ฝ๏ผŒๆ”ฏๆŒไธšๅŠกๆจกๅ—่ฟ่กŒ ``` src/core/ โ”œโ”€โ”€ ๐Ÿ“‚ db/ # ๐Ÿ—„๏ธ ๆ•ฐๆฎๅบ“ๅฑ‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ users/ # ็”จๆˆทๆ•ฐๆฎๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ users.service.ts # MySQLๆ•ฐๆฎๅบ“ๅฎž็Žฐ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ users_memory.service.ts # ๅ†…ๅญ˜ๆ•ฐๆฎๅบ“ๅฎž็Žฐ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ users.dto.ts # ็”จๆˆทๆ•ฐๆฎไผ ่พ“ๅฏน่ฑก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ users.entity.ts # ็”จๆˆทๅฎžไฝ“ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ users.module.ts # ็”จๆˆทๆ•ฐๆฎๆจกๅ— โ”‚ โ””โ”€โ”€ ๐Ÿ“„ users.service.spec.ts # ็”จๆˆทๆœๅŠกๆต‹่ฏ• โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ redis/ # ๐Ÿ”ด Redis็ผ“ๅญ˜ๅฑ‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ redis.module.ts # Redisๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ real_redis.service.ts # Redis็œŸๅฎžๅฎž็Žฐ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ file_redis.service.ts # ๆ–‡ไปถๅญ˜ๅ‚จๅฎž็Žฐ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ redis.interface.ts # RedisๆœๅŠกๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ login_core/ # ๐Ÿ”‘ ็™ปๅฝ•ๆ ธๅฟƒๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ login_core.service.ts # ็™ปๅฝ•ๆ ธๅฟƒ้€ป่พ‘ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ login_core.module.ts # ๆจกๅ—ๅฎšไน‰ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ login_core.service.spec.ts # ็™ปๅฝ•ๆ ธๅฟƒๆต‹่ฏ• โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ admin_core/ # ๐Ÿ‘‘ ็ฎก็†ๅ‘˜ๆ ธๅฟƒๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ admin_core.service.ts # ็ฎก็†ๅ‘˜ๆ ธๅฟƒ้€ป่พ‘ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ admin_core.module.ts # ๆจกๅ—ๅฎšไน‰ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ admin_core.service.spec.ts # ็ฎก็†ๅ‘˜ๆ ธๅฟƒๆต‹่ฏ• โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ zulip_core/ # ๐Ÿ’ฌ Zulipๆ ธๅฟƒๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ zulip_core.module.ts # Zulipๆ ธๅฟƒๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ config/ # ้…็ฝฎๆ–‡ไปถ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ interfaces/ # ๆŽฅๅฃๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ services/ # ๆ ธๅฟƒๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ types/ # ็ฑปๅž‹ๅฎšไน‰ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ index.ts # ๅฏผๅ‡บๆ–‡ไปถ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ security_core/ # ๐Ÿ›ก๏ธ ๅฎ‰ๅ…จๆ ธๅฟƒๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ security_core.module.ts # ๅฎ‰ๅ…จๆจกๅ—ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ guards/ # ๅฎ‰ๅ…จๅฎˆๅซ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ throttle.guard.ts # ้ข‘็އ้™ๅˆถๅฎˆๅซ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ interceptors/ # ๆ‹ฆๆˆชๅ™จ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ timeout.interceptor.ts # ่ถ…ๆ—ถๆ‹ฆๆˆชๅ™จ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ middleware/ # ไธญ้—ดไปถ โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ maintenance.middleware.ts # ็ปดๆŠคๆจกๅผไธญ้—ดไปถ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ content_type.middleware.ts # ๅ†…ๅฎน็ฑปๅž‹ไธญ้—ดไปถ โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ decorators/ # ่ฃ…้ฅฐๅ™จ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ throttle.decorator.ts # ้ข‘็އ้™ๅˆถ่ฃ…้ฅฐๅ™จ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ timeout.decorator.ts # ่ถ…ๆ—ถ่ฃ…้ฅฐๅ™จ โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ utils/ # ๐Ÿ› ๏ธ ๅทฅๅ…ทๆœๅŠก โ”œโ”€โ”€ ๐Ÿ“‚ email/ # ๐Ÿ“ง ้‚ฎไปถๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ email.service.ts # ้‚ฎไปถๅ‘้€ๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ email.module.ts # ้‚ฎไปถๆจกๅ— โ”‚ โ””โ”€โ”€ ๐Ÿ“„ email.service.spec.ts # ้‚ฎไปถๆœๅŠกๆต‹่ฏ• โ”œโ”€โ”€ ๐Ÿ“‚ verification/ # ๐Ÿ”ข ้ชŒ่ฏ็ ๆœๅŠก โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ verification.service.ts # ้ชŒ่ฏ็ ็”Ÿๆˆ้ชŒ่ฏ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ verification.module.ts # ้ชŒ่ฏ็ ๆจกๅ— โ”‚ โ””โ”€โ”€ ๐Ÿ“„ verification.service.spec.ts # ้ชŒ่ฏ็ ๆœๅŠกๆต‹่ฏ• โ””โ”€โ”€ ๐Ÿ“‚ logger/ # ๐Ÿ“ ๆ—ฅๅฟ—ๆœๅŠก โ”œโ”€โ”€ ๐Ÿ“„ logger.service.ts # ๆ—ฅๅฟ—่ฎฐๅฝ•ๆœๅŠก โ”œโ”€โ”€ ๐Ÿ“„ logger.module.ts # ๆ—ฅๅฟ—ๆจกๅ— โ”œโ”€โ”€ ๐Ÿ“„ logger.config.ts # ๆ—ฅๅฟ—้…็ฝฎ โ””โ”€โ”€ ๐Ÿ“„ log_management.service.ts # ๆ—ฅๅฟ—็ฎก็†ๆœๅŠก ``` ### ๐ŸŽจ ๅ‰็ซฏ็ฎก็†็•Œ้ข (`client/`) > **่ฎพ่ฎกๅŽŸๅˆ™**: ็‹ฌ็ซ‹็š„ๅ‰็ซฏ้กน็›ฎ๏ผŒๆไพ›็ฎก็†ๅ‘˜ๅŽๅฐๅŠŸ่ƒฝ๏ผŒๅŸบไบŽReact + Vite + Ant Design ``` client/ โ”œโ”€โ”€ ๐Ÿ“‚ src/ # ๅ‰็ซฏๆบ็  โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ app/ # ๅบ”็”จ็ป„ไปถ โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ App.tsx # ๅบ”็”จไธป็ป„ไปถ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ AdminLayout.tsx # ็ฎก็†ๅ‘˜ๅธƒๅฑ€็ป„ไปถ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ pages/ # ้กต้ข็ป„ไปถ โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ LoginPage.tsx # ็™ปๅฝ•้กต้ข โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ UsersPage.tsx # ็”จๆˆท็ฎก็†้กต้ข โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ LogsPage.tsx # ๆ—ฅๅฟ—็ฎก็†้กต้ข โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ lib/ # ๅทฅๅ…ทๅบ“ โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ api.ts # APIๅฎขๆˆท็ซฏ โ”‚ โ”‚ โ””โ”€โ”€ ๐Ÿ“„ adminAuth.ts # ็ฎก็†ๅ‘˜่ฎค่ฏๆœๅŠก โ”‚ โ””โ”€โ”€ ๐Ÿ“„ main.tsx # ๅบ”็”จๅ…ฅๅฃ โ”œโ”€โ”€ ๐Ÿ“‚ dist/ # ๆž„ๅปบไบง็‰ฉ โ”œโ”€โ”€ ๐Ÿ“„ package.json # ๅ‰็ซฏไพ่ต– โ”œโ”€โ”€ ๐Ÿ“„ vite.config.ts # Vite้…็ฝฎ โ””โ”€โ”€ ๐Ÿ“„ tsconfig.json # TypeScript้…็ฝฎ ``` ### ๐Ÿ“š ๆ–‡ๆกฃไธญๅฟƒ (`docs/`) > **่ฎพ่ฎกๅŽŸๅˆ™**: ๅฎŒๆ•ด็š„้กน็›ฎๆ–‡ๆกฃ๏ผŒๆ”ฏๆŒๅผ€ๅ‘่€…ๅฟซ้€ŸไธŠๆ‰‹ ``` docs/ โ”œโ”€โ”€ ๐Ÿ“„ README.md # ๐Ÿ“– ๆ–‡ๆกฃๅฏผ่ˆชไธญๅฟƒ โ”œโ”€โ”€ ๐Ÿ“„ ARCHITECTURE.md # ๐Ÿ—๏ธ ๆžถๆž„่ฎพ่ฎกๆ–‡ๆกฃ โ”œโ”€โ”€ ๐Ÿ“„ CONTRIBUTORS.md # ๐Ÿค ่ดก็Œฎ่€…ๆŒ‡ๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ api/ # ๐Ÿ”Œ APIๆŽฅๅฃๆ–‡ๆกฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ README.md # APIๆ–‡ๆกฃไฝฟ็”จๆŒ‡ๅ— โ”‚ โ””โ”€โ”€ ๐Ÿ“„ api-documentation.md # ๅฎŒๆ•ดAPIๆŽฅๅฃๆ–‡ๆกฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“‚ development/ # ๐Ÿ’ป ๅผ€ๅ‘ๆŒ‡ๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ backend_development_guide.md # ๅŽ็ซฏๅผ€ๅ‘่ง„่Œƒ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ git_commit_guide.md # Gitๆไบค่ง„่Œƒ โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ AI่พ…ๅŠฉๅผ€ๅ‘่ง„่ŒƒๆŒ‡ๅ—.md # AI่พ…ๅŠฉๅผ€ๅ‘ๆŒ‡ๅ— โ”‚ โ””โ”€โ”€ ๐Ÿ“„ TESTING.md # ๆต‹่ฏ•ๆŒ‡ๅ— โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ deployment/ # ๐Ÿš€ ้ƒจ็ฝฒๆ–‡ๆกฃ โ””โ”€โ”€ ๐Ÿ“„ DEPLOYMENT.md # ็”Ÿไบง็Žฏๅขƒ้ƒจ็ฝฒๆŒ‡ๅ— ``` ### ๐Ÿงช ๆต‹่ฏ•ๆ–‡ไปถ (`test/`) > **่ฎพ่ฎกๅŽŸๅˆ™**: ๅฎŒๆ•ด็š„ๆต‹่ฏ•่ฆ†็›–๏ผŒ็กฎไฟไปฃ็ ่ดจ้‡ ``` test/ โ”œโ”€โ”€ ๐Ÿ“‚ unit/ # ๅ•ๅ…ƒๆต‹่ฏ• โ”œโ”€โ”€ ๐Ÿ“‚ integration/ # ้›†ๆˆๆต‹่ฏ• โ”œโ”€โ”€ ๐Ÿ“‚ e2e/ # ็ซฏๅˆฐ็ซฏๆต‹่ฏ• โ””โ”€โ”€ ๐Ÿ“‚ fixtures/ # ๆต‹่ฏ•ๆ•ฐๆฎ ``` ### โš™๏ธ ้…็ฝฎๆ–‡ไปถ > **่ฎพ่ฎกๅŽŸๅˆ™**: ๆธ…ๆ™ฐ็š„้…็ฝฎ็ฎก็†๏ผŒๆ”ฏๆŒๅคš็Žฏๅขƒ้ƒจ็ฝฒ ``` ้กน็›ฎๆ น็›ฎๅฝ•/ โ”œโ”€โ”€ ๐Ÿ“„ .env # ๐Ÿ”ง ็Žฏๅขƒๅ˜้‡้…็ฝฎ โ”œโ”€โ”€ ๐Ÿ“„ .env.example # ๐Ÿ”ง ็Žฏๅขƒๅ˜้‡็คบไพ‹ โ”œโ”€โ”€ ๐Ÿ“„ .env.production.example # ๐Ÿ”ง ็”Ÿไบง็Žฏๅขƒ็คบไพ‹ โ”œโ”€โ”€ ๐Ÿ“„ package.json # ๐Ÿ“‹ ๅŽ็ซฏ้กน็›ฎไพ่ต–้…็ฝฎ โ”œโ”€โ”€ ๐Ÿ“„ pnpm-workspace.yaml # ๐Ÿ“ฆ pnpmๅทฅไฝœ็ฉบ้—ด้…็ฝฎ โ”œโ”€โ”€ ๐Ÿ“„ tsconfig.json # ๐Ÿ“˜ TypeScript้…็ฝฎ โ”œโ”€โ”€ ๐Ÿ“„ jest.config.js # ๐Ÿงช Jestๆต‹่ฏ•้…็ฝฎ โ”œโ”€โ”€ ๐Ÿ“„ nest-cli.json # ๐Ÿ  NestJS CLI้…็ฝฎ โ””โ”€โ”€ ๐Ÿ“„ ecosystem.config.js # ๐Ÿš€ PM2่ฟ›็จ‹็ฎก็†้…็ฝฎ client/ โ”œโ”€โ”€ ๐Ÿ“„ package.json # ๐Ÿ“‹ ๅ‰็ซฏ้กน็›ฎไพ่ต–้…็ฝฎ โ”œโ”€โ”€ ๐Ÿ“„ vite.config.ts # โšก Viteๆž„ๅปบ้…็ฝฎ โ””โ”€โ”€ ๐Ÿ“„ tsconfig.json # ๐Ÿ“˜ ๅ‰็ซฏTypeScript้…็ฝฎ ``` --- ## ๐Ÿ—๏ธ ๅˆ†ๅฑ‚ๆžถๆž„่ฎพ่ฎก ### ๐Ÿ“Š ๆžถๆž„ๅˆ†ๅฑ‚่ฏดๆ˜Ž ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐ŸŒ ่กจ็Žฐๅฑ‚ (Presentation) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Controllers โ”‚ โ”‚ WebSocket โ”‚ โ”‚ Swagger UI โ”‚ โ”‚ โ”‚ โ”‚ (HTTPๆŽฅๅฃ) โ”‚ โ”‚ Gateways โ”‚ โ”‚ (APIๆ–‡ๆกฃ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โฌ‡๏ธ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐ŸŽฏ ไธšๅŠกๅฑ‚ (Business) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Auth Module โ”‚ โ”‚ UserMgmt โ”‚ โ”‚ Admin Module โ”‚ โ”‚ โ”‚ โ”‚ (็”จๆˆท่ฎค่ฏ) โ”‚ โ”‚ Module โ”‚ โ”‚ (็ฎก็†ๅ‘˜) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (็”จๆˆท็ฎก็†) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Security Module โ”‚ โ”‚ Zulip Module โ”‚ โ”‚ Shared Module โ”‚ โ”‚ โ”‚ โ”‚ (ๅฎ‰ๅ…จ้˜ฒๆŠค) โ”‚ โ”‚ (Zulip้›†ๆˆ) โ”‚ โ”‚ (ๅ…ฑไบซ็ป„ไปถ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โฌ‡๏ธ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โš™๏ธ ๆœๅŠกๅฑ‚ (Service) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Login Core โ”‚ โ”‚ Admin Core โ”‚ โ”‚ Zulip Core โ”‚ โ”‚ โ”‚ โ”‚ (็™ปๅฝ•ๆ ธๅฟƒ) โ”‚ โ”‚ (็ฎก็†ๅ‘˜ๆ ธๅฟƒ) โ”‚ โ”‚ (Zulipๆ ธๅฟƒ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Email Service โ”‚ โ”‚ Verification โ”‚ โ”‚ Logger Service โ”‚ โ”‚ โ”‚ โ”‚ (้‚ฎไปถๆœๅŠก) โ”‚ โ”‚ Service โ”‚ โ”‚ (ๆ—ฅๅฟ—ๆœๅŠก) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ (้ชŒ่ฏ็ ๆœๅŠก) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โฌ‡๏ธ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ—„๏ธ ๆ•ฐๆฎๅฑ‚ (Data) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Users Service โ”‚ โ”‚ Redis Service โ”‚ โ”‚ File Storage โ”‚ โ”‚ โ”‚ โ”‚ (็”จๆˆทๆ•ฐๆฎ) โ”‚ โ”‚ (็ผ“ๅญ˜ๆœๅŠก) โ”‚ โ”‚ (ๆ–‡ไปถๅญ˜ๅ‚จ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ MySQL/Memory โ”‚ โ”‚ Redis/File โ”‚ โ”‚ Logs/Data โ”‚ โ”‚ โ”‚ โ”‚ (ๆ•ฐๆฎๅบ“) โ”‚ โ”‚ (็ผ“ๅญ˜ๅฎž็Žฐ) โ”‚ โ”‚ (ๆ—ฅๅฟ—ๆ•ฐๆฎ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### ๐Ÿ”„ ๆ•ฐๆฎๆตๅ‘ #### ็”จๆˆท็™ปๅฝ•ๆต็จ‹็คบไพ‹ ``` 1. ๐Ÿ“ฑ ็”จๆˆท่ฏทๆฑ‚ โ†’ LoginController.login() 2. ๐Ÿ” ๅ‚ๆ•ฐ้ชŒ่ฏ โ†’ class-validator่ฃ…้ฅฐๅ™จ 3. ๐ŸŽฏ ไธšๅŠก้€ป่พ‘ โ†’ LoginService.login() 4. โš™๏ธ ๆ ธๅฟƒๆœๅŠก โ†’ LoginCoreService.validateUser() 5. ๐Ÿ“ง ๅ‘้€้ชŒ่ฏ็  โ†’ VerificationService.generate() 6. ๐Ÿ’พ ๅญ˜ๅ‚จๆ•ฐๆฎ โ†’ UsersService.findByEmail() + RedisService.set() 7. ๐Ÿ“ ่ฎฐๅฝ•ๆ—ฅๅฟ— โ†’ LoggerService.log() 8. โœ… ่ฟ”ๅ›žๅ“ๅบ” โ†’ ็”จๆˆทๆ”ถๅˆฐ็™ปๅฝ•็ป“ๆžœ ``` #### ็ฎก็†ๅ‘˜ๆ“ไฝœๆต็จ‹็คบไพ‹ ``` 1. ๐Ÿ›ก๏ธ ็ฎก็†ๅ‘˜่ฏทๆฑ‚ โ†’ AdminController.resetUserPassword() 2. ๐Ÿ” ๆƒ้™้ชŒ่ฏ โ†’ AdminGuard.canActivate() 3. ๐ŸŽฏ ไธšๅŠก้€ป่พ‘ โ†’ AdminService.resetPassword() 4. โš™๏ธ ๆ ธๅฟƒๆœๅŠก โ†’ AdminCoreService.resetUserPassword() 5. ๐Ÿ”‘ ๅฏ†็ ๅŠ ๅฏ† โ†’ bcrypt.hash() 6. ๐Ÿ’พ ๆ›ดๆ–ฐๆ•ฐๆฎ โ†’ UsersService.update() 7. ๐Ÿ“ง ้€š็Ÿฅ็”จๆˆท โ†’ EmailService.sendPasswordReset() 8. ๐Ÿ“ ๅฎก่ฎกๆ—ฅๅฟ— โ†’ LoggerService.audit() 9. โœ… ่ฟ”ๅ›žๅ“ๅบ” โ†’ ็ฎก็†ๅ‘˜ๆ”ถๅˆฐๆ“ไฝœ็ป“ๆžœ ``` --- ## ๐Ÿ”„ ๅŒๆจกๅผๆžถๆž„ ### ๐ŸŽฏ ่ฎพ่ฎก็›ฎๆ ‡ - **ๅผ€ๅ‘ๆต‹่ฏ•**: ้›ถไพ่ต–ๅฟซ้€ŸๅฏๅŠจ๏ผŒๆ— ้œ€ๅฎ‰่ฃ…MySQLใ€Redis็ญ‰ๅค–้ƒจๆœๅŠก - **็”Ÿไบง้ƒจ็ฝฒ**: ้ซ˜ๆ€ง่ƒฝใ€้ซ˜ๅฏ็”จ๏ผŒๆ”ฏๆŒ้›†็พคๅ’Œ่ดŸ่ฝฝๅ‡่กก ### ๐Ÿ“Š ๆจกๅผๅฏนๆฏ” | ๅŠŸ่ƒฝๆจกๅ— | ๐Ÿงช ๅผ€ๅ‘ๆต‹่ฏ•ๆจกๅผ | ๐Ÿš€ ็”Ÿไบง้ƒจ็ฝฒๆจกๅผ | |----------|----------------|----------------| | **ๆ•ฐๆฎๅบ“** | ๅ†…ๅญ˜ๅญ˜ๅ‚จ (UsersMemoryService) | MySQL (UsersService + TypeORM) | | **็ผ“ๅญ˜** | ๆ–‡ไปถๅญ˜ๅ‚จ (FileRedisService) | Redis (RealRedisService + IORedis) | | **้‚ฎไปถ** | ๆŽงๅˆถๅฐ่พ“ๅ‡บ (ๆต‹่ฏ•ๆจกๅผ) | SMTPๆœๅŠกๅ™จ (็”Ÿไบงๆจกๅผ) | | **ๆ—ฅๅฟ—** | ๆŽงๅˆถๅฐ + ๆ–‡ไปถ | ็ป“ๆž„ๅŒ–ๆ—ฅๅฟ— + ๆ—ฅๅฟ—่ฝฎ่ฝฌ | | **้…็ฝฎ** | `.env` ้ป˜่ฎค้…็ฝฎ | ็Žฏๅขƒๅ˜้‡ + ้…็ฝฎไธญๅฟƒ | ### โš™๏ธ ๆจกๅผๅˆ‡ๆข้…็ฝฎ #### ๅผ€ๅ‘ๆต‹่ฏ•ๆจกๅผ (.env) ```bash # ๆ•ฐๆฎๅญ˜ๅ‚จๆจกๅผ USE_FILE_REDIS=true # ไฝฟ็”จๆ–‡ไปถๅญ˜ๅ‚จไปฃๆ›ฟRedis NODE_ENV=development # ๅผ€ๅ‘็Žฏๅขƒ # ๆ•ฐๆฎๅบ“้…็ฝฎ๏ผˆๆณจ้‡Šๆމ๏ผŒไฝฟ็”จๅ†…ๅญ˜ๆ•ฐๆฎๅบ“๏ผ‰ # DB_HOST=localhost # DB_USERNAME=root # DB_PASSWORD=password # ้‚ฎไปถ้…็ฝฎ๏ผˆๆณจ้‡Šๆމ๏ผŒไฝฟ็”จๆต‹่ฏ•ๆจกๅผ๏ผ‰ # EMAIL_HOST=smtp.gmail.com # EMAIL_USER=your_email@gmail.com # EMAIL_PASS=your_password ``` #### ็”Ÿไบง้ƒจ็ฝฒๆจกๅผ (.env.production) ```bash # ๆ•ฐๆฎๅญ˜ๅ‚จๆจกๅผ USE_FILE_REDIS=false # ไฝฟ็”จ็œŸๅฎžRedis NODE_ENV=production # ็”Ÿไบง็Žฏๅขƒ # ๆ•ฐๆฎๅบ“้…็ฝฎ DB_HOST=your_mysql_host DB_PORT=3306 DB_USERNAME=your_username DB_PASSWORD=your_password DB_DATABASE=whale_town # Redis้…็ฝฎ REDIS_HOST=your_redis_host REDIS_PORT=6379 REDIS_PASSWORD=your_redis_password # ้‚ฎไปถ้…็ฝฎ EMAIL_HOST=smtp.gmail.com EMAIL_PORT=587 EMAIL_USER=your_email@gmail.com EMAIL_PASS=your_app_password ``` ### ๐Ÿ”ง ๅฎž็Žฐๆœบๅˆถ #### ไพ่ต–ๆณจๅ…ฅๅˆ‡ๆข ```typescript // redis.module.ts @Module({ providers: [ { provide: 'IRedisService', useFactory: (configService: ConfigService) => { const useFileRedis = configService.get('USE_FILE_REDIS'); return useFileRedis ? new FileRedisService() : new RealRedisService(configService); }, inject: [ConfigService], }, ], }) export class RedisModule {} ``` #### ้…็ฝฎ้ฉฑๅŠจๆœๅŠก้€‰ๆ‹ฉ ```typescript // users.module.ts @Module({ providers: [ { provide: 'IUsersService', useFactory: (configService: ConfigService) => { const dbHost = configService.get('DB_HOST'); return dbHost ? new UsersService() : new UsersMemoryService(); }, inject: [ConfigService], }, ], }) export class UsersModule {} ``` --- ## ๐Ÿ“ฆ ๆจกๅ—ไพ่ต–ๅ…ณ็ณป ### ๐Ÿ—๏ธ ๆจกๅ—ไพ่ต–ๅ›พ ``` AppModule (ๅบ”็”จไธปๆจกๅ—) โ”œโ”€โ”€ ๐Ÿ“Š ConfigModule (ๅ…จๅฑ€้…็ฝฎ) โ”œโ”€โ”€ ๐Ÿ“ LoggerModule (ๆ—ฅๅฟ—็ณป็ปŸ) โ”œโ”€โ”€ ๐Ÿ”ด RedisModule (็ผ“ๅญ˜ๆœๅŠก) โ”‚ โ”œโ”€โ”€ RealRedisService (็œŸๅฎžRedis) โ”‚ โ””โ”€โ”€ FileRedisService (ๆ–‡ไปถๅญ˜ๅ‚จ) โ”œโ”€โ”€ ๐Ÿ—„๏ธ UsersModule (็”จๆˆทๆ•ฐๆฎ) โ”‚ โ”œโ”€โ”€ UsersService (MySQLๆ•ฐๆฎๅบ“) โ”‚ โ””โ”€โ”€ UsersMemoryService (ๅ†…ๅญ˜ๆ•ฐๆฎๅบ“) โ”œโ”€โ”€ ๐Ÿ“ง EmailModule (้‚ฎไปถๆœๅŠก) โ”œโ”€โ”€ ๐Ÿ”ข VerificationModule (้ชŒ่ฏ็ ๆœๅŠก) โ”œโ”€โ”€ ๐Ÿ”‘ LoginCoreModule (็™ปๅฝ•ๆ ธๅฟƒ) โ”œโ”€โ”€ ๐Ÿ‘‘ AdminCoreModule (็ฎก็†ๅ‘˜ๆ ธๅฟƒ) โ”œโ”€โ”€ ๐Ÿ’ฌ ZulipCoreModule (Zulipๆ ธๅฟƒ) โ”œโ”€โ”€ ๐Ÿ”’ SecurityCoreModule (ๅฎ‰ๅ…จๆ ธๅฟƒ) โ”‚ โ”œโ”€โ”€ ๐ŸŽฏ ไธšๅŠกๅŠŸ่ƒฝๆจกๅ— โ”‚ โ”œโ”€โ”€ ๐Ÿ” AuthModule (็”จๆˆท่ฎค่ฏ) โ”‚ โ”‚ โ””โ”€โ”€ ไพ่ต–: LoginCoreModule, EmailModule, VerificationModule, SecurityCoreModule โ”‚ โ”œโ”€โ”€ ๐Ÿ‘ฅ UserMgmtModule (็”จๆˆท็ฎก็†) โ”‚ โ”‚ โ””โ”€โ”€ ไพ่ต–: UsersModule, LoggerModule, SecurityCoreModule โ”‚ โ”œโ”€โ”€ ๐Ÿ›ก๏ธ AdminModule (็ฎก็†ๅ‘˜) โ”‚ โ”‚ โ””โ”€โ”€ ไพ่ต–: AdminCoreModule, UsersModule, SecurityCoreModule โ”‚ โ”œโ”€โ”€ ๐Ÿ’ฌ ZulipModule (Zulip้›†ๆˆ) โ”‚ โ”‚ โ””โ”€โ”€ ไพ่ต–: ZulipCoreModule, RedisModule โ”‚ โ””โ”€โ”€ ๐Ÿ”— SharedModule (ๅ…ฑไบซ็ป„ไปถ) ``` ### ๐Ÿ”„ ๆจกๅ—ไบคไบ’ๆต็จ‹ #### ็”จๆˆท่ฎค่ฏๆต็จ‹ ``` AuthController โ†’ LoginService โ†’ LoginCoreService โ†“ EmailService โ† VerificationService โ† RedisService โ†“ UsersService ``` #### ็ฎก็†ๅ‘˜ๆ“ไฝœๆต็จ‹ ``` AdminController โ†’ AdminService โ†’ AdminCoreService โ†“ LoggerService โ† UsersService โ† RedisService ``` #### ๅฎ‰ๅ…จ้˜ฒๆŠคๆต็จ‹ ``` SecurityGuard โ†’ RedisService (้ข‘็އ้™ๅˆถ) โ†’ LoggerService (ๅฎก่ฎกๆ—ฅๅฟ—) โ†’ ConfigService (็ปดๆŠคๆจกๅผ) ``` --- ## ๐Ÿš€ ๆ‰ฉๅฑ•ๆŒ‡ๅ— ### ๐Ÿ“ ๆทปๅŠ ๆ–ฐ็š„ไธšๅŠกๆจกๅ— #### 1. ๅˆ›ๅปบไธšๅŠกๆจกๅ—็ป“ๆž„ ```bash # ๅˆ›ๅปบๆจกๅ—็›ฎๅฝ• mkdir -p src/business/game/{dto,enums,guards,interfaces} # ็”ŸๆˆNestJSๆจกๅ—ๆ–‡ไปถ nest g module business/game nest g controller business/game nest g service business/game ``` #### 2. ๅฎž็ŽฐไธšๅŠก้€ป่พ‘ ```typescript // src/business/game/game.module.ts @Module({ imports: [ GameCoreModule, # ไพ่ต–ๆ ธๅฟƒๆœๅŠก UsersModule, # ไพ่ต–็”จๆˆทๆ•ฐๆฎ RedisModule, # ไพ่ต–็ผ“ๅญ˜ๆœๅŠก ], controllers: [GameController], providers: [GameService], exports: [GameService], }) export class GameModule {} ``` #### 3. ๅˆ›ๅปบๅฏนๅบ”็š„ๆ ธๅฟƒๆœๅŠก ```bash # ๅˆ›ๅปบๆ ธๅฟƒๆœๅŠก mkdir -p src/core/game_core nest g module core/game_core nest g service core/game_core ``` #### 4. ๆ›ดๆ–ฐไธปๆจกๅ— ```typescript // src/app.module.ts @Module({ imports: [ // ... ๅ…ถไป–ๆจกๅ— GameModule, # ๆทปๅŠ ๆ–ฐ็š„ไธšๅŠกๆจกๅ— ], }) export class AppModule {} ``` ### ๐Ÿ› ๏ธ ๆทปๅŠ ๆ–ฐ็š„ๅทฅๅ…ทๆœๅŠก #### 1. ๅˆ›ๅปบๅทฅๅ…ทๆœๅŠก ```bash mkdir -p src/core/utils/notification nest g module core/utils/notification nest g service core/utils/notification ``` #### 2. ๅฎšไน‰ๆœๅŠกๆŽฅๅฃ ```typescript // src/core/utils/notification/notification.interface.ts export interface INotificationService { sendPush(userId: string, message: string): Promise; sendSMS(phone: string, message: string): Promise; } ``` #### 3. ๅฎž็ŽฐๆœๅŠก ```typescript // src/core/utils/notification/notification.service.ts @Injectable() export class NotificationService implements INotificationService { async sendPush(userId: string, message: string): Promise { // ๅฎž็ŽฐๆŽจ้€้€š็Ÿฅ้€ป่พ‘ } async sendSMS(phone: string, message: string): Promise { // ๅฎž็Žฐ็Ÿญไฟกๅ‘้€้€ป่พ‘ } } ``` #### 4. ้…็ฝฎไพ่ต–ๆณจๅ…ฅ ```typescript // src/core/utils/notification/notification.module.ts @Module({ providers: [ { provide: 'INotificationService', useClass: NotificationService, }, ], exports: ['INotificationService'], }) export class NotificationModule {} ``` ### ๐Ÿ”Œ ๆทปๅŠ ๆ–ฐ็š„APIๆŽฅๅฃ #### 1. ๅฎšไน‰DTO ```typescript // src/business/game/dto/create-game.dto.ts export class CreateGameDto { @IsString() @IsNotEmpty() name: string; @IsString() @IsOptional() description?: string; } ``` #### 2. ๅฎž็ŽฐController ```typescript // src/business/game/game.controller.ts @Controller('game') @ApiTags('ๆธธๆˆ็ฎก็†') export class GameController { constructor(private readonly gameService: GameService) {} @Post() @ApiOperation({ summary: 'ๅˆ›ๅปบๆธธๆˆ' }) async createGame(@Body() createGameDto: CreateGameDto) { return this.gameService.create(createGameDto); } } ``` #### 3. ๅฎž็ŽฐService ```typescript // src/business/game/game.service.ts @Injectable() export class GameService { constructor( @Inject('IGameCoreService') private readonly gameCoreService: IGameCoreService, ) {} async create(createGameDto: CreateGameDto) { return this.gameCoreService.createGame(createGameDto); } } ``` #### 4. ๆทปๅŠ ๆต‹่ฏ•็”จไพ‹ ```typescript // src/business/game/game.service.spec.ts describe('GameService', () => { let service: GameService; let gameCoreService: jest.Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ GameService, { provide: 'IGameCoreService', useValue: { createGame: jest.fn(), }, }, ], }).compile(); service = module.get(GameService); gameCoreService = module.get('IGameCoreService'); }); it('should create game', async () => { const createGameDto = { name: 'Test Game' }; const expectedResult = { id: 1, ...createGameDto }; gameCoreService.createGame.mockResolvedValue(expectedResult); const result = await service.create(createGameDto); expect(result).toEqual(expectedResult); expect(gameCoreService.createGame).toHaveBeenCalledWith(createGameDto); }); }); ``` ### ๐Ÿ“Š ๆ€ง่ƒฝไผ˜ๅŒ–ๅปบ่ฎฎ #### 1. ็ผ“ๅญ˜็ญ–็•ฅ - **Redis็ผ“ๅญ˜**: ็”จๆˆทไผš่ฏใ€้ชŒ่ฏ็ ใ€้ข‘็นๆŸฅ่ฏขๆ•ฐๆฎ - **ๅ†…ๅญ˜็ผ“ๅญ˜**: ้…็ฝฎไฟกๆฏใ€้™ๆ€ๆ•ฐๆฎ - **CDN็ผ“ๅญ˜**: ้™ๆ€่ต„ๆบๆ–‡ไปถ #### 2. ๆ•ฐๆฎๅบ“ไผ˜ๅŒ– - **่ฟžๆŽฅๆฑ **: ๅค็”จๆ•ฐๆฎๅบ“่ฟžๆŽฅ๏ผŒๅ‡ๅฐ‘่ฟžๆŽฅๅผ€้”€ - **็ดขๅผ•ไผ˜ๅŒ–**: ไธบๆŸฅ่ฏขๅญ—ๆฎตๅปบ็ซ‹ๅˆ้€‚็š„็ดขๅผ• - **ๆŸฅ่ฏขไผ˜ๅŒ–**: ้ฟๅ…N+1ๆŸฅ่ฏข๏ผŒไฝฟ็”จJOINไผ˜ๅŒ–ๅ…ณ่”ๆŸฅ่ฏข #### 3. ๆ—ฅๅฟ—ไผ˜ๅŒ– - **ๅผ‚ๆญฅๆ—ฅๅฟ—**: ไฝฟ็”จPino็š„ๅผ‚ๆญฅๅ†™ๅ…ฅๅŠŸ่ƒฝ - **ๆ—ฅๅฟ—ๅˆ†็บง**: ็”Ÿไบง็Žฏๅขƒๅช่ฎฐๅฝ•ERRORๅ’ŒWARN็บงๅˆซ - **ๆ—ฅๅฟ—่ฝฎ่ฝฌ**: ่‡ชๅŠจๆธ…็†่ฟ‡ๆœŸๆ—ฅๅฟ—ๆ–‡ไปถ ### ๐Ÿ”’ ๅฎ‰ๅ…จๅŠ ๅ›บๅปบ่ฎฎ #### 1. ๆ•ฐๆฎ้ชŒ่ฏ - **่พ“ๅ…ฅ้ชŒ่ฏ**: ไฝฟ็”จclass-validator่ฟ›่กŒไธฅๆ ผ้ชŒ่ฏ - **็ฑปๅž‹ๆฃ€ๆŸฅ**: TypeScript้™ๆ€็ฑปๅž‹ๆฃ€ๆŸฅ - **SQLๆณจๅ…ฅ้˜ฒๆŠค**: TypeORMๅ‚ๆ•ฐๅŒ–ๆŸฅ่ฏข #### 2. ่ฎค่ฏๆŽˆๆƒ - **ๅฏ†็ ๅฎ‰ๅ…จ**: bcryptๅŠ ๅฏ†๏ผŒๅผบๅฏ†็ ็ญ–็•ฅ - **ไผš่ฏ็ฎก็†**: JWT + Redisไผš่ฏๅญ˜ๅ‚จ - **ๆƒ้™ๆŽงๅˆถ**: ๅŸบไบŽ่ง’่‰ฒ็š„่ฎฟ้—ฎๆŽงๅˆถ(RBAC) #### 3. ้€šไฟกๅฎ‰ๅ…จ - **HTTPS**: ็”Ÿไบง็ŽฏๅขƒๅผบๅˆถHTTPS - **CORS**: ไธฅๆ ผ็š„่ทจๅŸŸ่ฏทๆฑ‚ๆŽงๅˆถ - **Rate Limiting**: API่ฏทๆฑ‚้ข‘็އ้™ๅˆถ --- **๐Ÿ—๏ธ ้€š่ฟ‡ๆธ…ๆ™ฐ็š„ๆžถๆž„่ฎพ่ฎก๏ผŒWhale Town ๅฎž็Žฐไบ†้ซ˜ๅ†…่šใ€ไฝŽ่€ฆๅˆ็š„ๆจกๅ—ๅŒ–ๆžถๆž„๏ผŒๆ”ฏๆŒๅฟซ้€Ÿๅผ€ๅ‘ๅ’Œ็ตๆดป้ƒจ็ฝฒ๏ผ**