refactor: split monolith into 3 microservices (frontend, backend, discord-gateway)

- Extract services into services/{frontend,backend,discord-gateway}
- Create packages/shared/ for shared logger, errors, utils, types
- Setup Modular MVC pattern in backend (controller→service→repository)
- Setup event-driven architecture in discord-gateway with Redis pub/sub
- Move Docker files to infra/docker/ with per-service Dockerfiles
- Update docker-compose.yml to use Traefik-only routing (no port exposes)
- Update GitHub Actions deploy workflow for multi-service matrix build
- Fix all import paths and resolve type errors across all services
- All 3 services pass tsc --noEmit clean

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-01 21:44:29 +07:00
co-authored by Claude Opus 4.8
parent bda8304bb9
commit c48a0c5e3b
193 changed files with 16879 additions and 1158 deletions
+300 -6
View File
@@ -166,6 +166,226 @@ importers:
specifier: latest
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(vite@8.0.13(@types/node@25.9.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0))
packages/shared:
dependencies:
pino:
specifier: ^9.0.0
version: 9.14.0
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies:
'@types/node':
specifier: ^25.9.0
version: 25.9.0
typescript:
specifier: ^5.9.3
version: 5.9.3
services/backend:
dependencies:
'@discordjs/voice':
specifier: ^0.19.2
version: 0.19.2(@discordjs/opus@0.10.0)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(opusscript@0.0.8)
'@types/pg':
specifier: ^8.20.0
version: 8.20.0
axios:
specifier: ^1.16.1
version: 1.16.1
dotenv:
specifier: ^17.4.2
version: 17.4.2
drizzle-orm:
specifier: ^0.45.2
version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)
express:
specifier: ^5.2.1
version: 5.2.1
helmet:
specifier: ^8.1.0
version: 8.1.0
ioredis:
specifier: ^5.11.0
version: 5.11.0
pg:
specifier: ^8.21.0
version: 8.21.0
pino:
specifier: ^9.6.0
version: 9.14.0
pino-http:
specifier: ^10.3.0
version: 10.5.0
prom-client:
specifier: ^15.1.3
version: 15.1.3
ws:
specifier: ^8.20.1
version: 8.20.1
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies:
'@biomejs/biome':
specifier: latest
version: 2.4.16
'@types/express':
specifier: ^5.0.6
version: 5.0.6
'@types/node':
specifier: ^25.9.0
version: 25.9.0
'@types/ws':
specifier: ^8.18.1
version: 8.18.1
tsx:
specifier: ^4.22.2
version: 4.22.2
typescript:
specifier: ^5.9.3
version: 5.9.3
vitest:
specifier: latest
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(vite@8.0.13(@types/node@25.9.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0))
services/discord-gateway:
dependencies:
'@discordjs/opus':
specifier: ^0.10.0
version: 0.10.0
'@discordjs/voice':
specifier: ^0.19.2
version: 0.19.2(@discordjs/opus@0.10.0)(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(opusscript@0.0.8)
'@snazzah/davey':
specifier: ^0.1.11
version: 0.1.11(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
discord.js-selfbot-v13:
specifier: workspace:*
version: link:../../vendor/discord.js-selfbot-v13
dotenv:
specifier: ^17.4.2
version: 17.4.2
drizzle-orm:
specifier: ^0.45.2
version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)
ioredis:
specifier: ^5.11.0
version: 5.11.0
libsodium-wrappers:
specifier: ^0.8.4
version: 0.8.4
openai:
specifier: ^6.38.0
version: 6.38.0(ws@8.20.1)(zod@4.4.3)
opusscript:
specifier: ^0.0.8
version: 0.0.8
p-limit:
specifier: ^7.3.0
version: 7.3.0
p-retry:
specifier: ^8.0.0
version: 8.0.0
pg:
specifier: ^8.21.0
version: 8.21.0
piscina:
specifier: ^5.1.4
version: 5.1.4
prism-media:
specifier: 2.0.0-alpha.0
version: 2.0.0-alpha.0
sharp:
specifier: ^0.34.5
version: 0.34.5
winston:
specifier: ^3.19.0
version: 3.19.0
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies:
'@biomejs/biome':
specifier: latest
version: 2.4.16
'@types/node':
specifier: ^25.9.0
version: 25.9.0
drizzle-kit:
specifier: ^0.31.10
version: 0.31.10
tsx:
specifier: ^4.22.2
version: 4.22.2
typescript:
specifier: ^5.9.3
version: 5.9.3
vitest:
specifier: latest
version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(vite@8.0.13(@types/node@25.9.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0))
services/frontend:
dependencies:
'@radix-ui/react-scroll-area':
specifier: ^1.2.10
version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@radix-ui/react-slot':
specifier: ^1.2.4
version: 1.2.4(@types/react@19.2.14)(react@19.2.6)
'@radix-ui/react-tabs':
specifier: ^1.1.13
version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-query':
specifier: ^5.100.14
version: 5.100.14(react@19.2.6)
clsx:
specifier: ^2.1.1
version: 2.1.1
lucide-react:
specifier: ^1.16.0
version: 1.16.0(react@19.2.6)
react:
specifier: ^19.2.6
version: 19.2.6
react-dom:
specifier: ^19.2.6
version: 19.2.6(react@19.2.6)
tailwind-merge:
specifier: ^3.6.0
version: 3.6.0
devDependencies:
'@biomejs/biome':
specifier: latest
version: 2.4.16
'@tailwindcss/postcss':
specifier: ^4.3.0
version: 4.3.0
'@types/react':
specifier: ^19.2.14
version: 19.2.14
'@types/react-dom':
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react':
specifier: ^6.0.2
version: 6.0.2(vite@8.0.13(@types/node@25.9.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0))
autoprefixer:
specifier: ^10.5.0
version: 10.5.0(postcss@8.5.14)
postcss:
specifier: ^8.5.14
version: 8.5.14
tailwindcss:
specifier: ^4.3.0
version: 4.3.0
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^8.0.13
version: 8.0.13(@types/node@25.9.0)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.2)(yaml@2.9.0)
vendor/discord-video-stream:
dependencies:
'@lng2004/node-datachannel':
@@ -1405,6 +1625,9 @@ packages:
resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==}
engines: {node: '>=20.0.0'}
'@pinojs/redact@0.4.0':
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
'@radix-ui/number@1.1.1':
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
@@ -3750,6 +3973,10 @@ packages:
obug@2.1.1:
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
on-exit-leak-free@2.1.2:
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
engines: {node: '>=14.0.0'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
@@ -3932,6 +4159,19 @@ packages:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
pino-abstract-transport@2.0.0:
resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==}
pino-http@10.5.0:
resolution: {integrity: sha512-hD91XjgaKkSsdn8P7LaebrNzhGTdB086W3pyPihX0EzGPjq5uBJBXo4N5guqNaK6mUjg9aubMF7wDViYek9dRA==}
pino-std-serializers@7.1.0:
resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==}
pino@9.14.0:
resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==}
hasBin: true
piscina@5.1.4:
resolution: {integrity: sha512-7uU4ZnKeQq22t9AsmHGD2w4OYQGonwFnTypDypaWi7Qr2EvQIFVtG8J5D/3bE7W123Wdc9+v4CZDu5hJXVCtBg==}
engines: {node: '>=20.x'}
@@ -4022,6 +4262,9 @@ packages:
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
process-warning@5.0.0:
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
@@ -4072,6 +4315,9 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
quick-format-unescaped@4.0.4:
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
quick-lru@4.0.1:
resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
engines: {node: '>=8'}
@@ -4119,6 +4365,10 @@ packages:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
real-require@0.2.0:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
redent@3.0.0:
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
engines: {node: '>=8'}
@@ -4523,6 +4773,9 @@ packages:
resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==}
engines: {node: '>=0.2.6'}
thread-stream@3.1.0:
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
@@ -5880,6 +6133,8 @@ snapshots:
tslib: 2.8.1
tsyringe: 4.10.0
'@pinojs/redact@0.4.0': {}
'@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.1.3': {}
@@ -6286,7 +6541,7 @@ snapshots:
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.38
'@types/node': 25.8.0
'@types/node': 25.9.0
'@types/chai@5.2.3':
dependencies:
@@ -6295,7 +6550,7 @@ snapshots:
'@types/connect@3.4.38':
dependencies:
'@types/node': 25.8.0
'@types/node': 25.9.0
'@types/debug@4.1.13':
dependencies:
@@ -6312,7 +6567,7 @@ snapshots:
'@types/express-serve-static-core@5.1.1':
dependencies:
'@types/node': 25.8.0
'@types/node': 25.9.0
'@types/qs': 6.15.1
'@types/range-parser': 1.2.7
'@types/send': 1.2.1
@@ -6325,7 +6580,7 @@ snapshots:
'@types/fluent-ffmpeg@2.1.28':
dependencies:
'@types/node': 25.8.0
'@types/node': 25.9.0
'@types/http-errors@2.0.5': {}
@@ -6374,12 +6629,12 @@ snapshots:
'@types/send@1.2.1':
dependencies:
'@types/node': 25.8.0
'@types/node': 25.9.0
'@types/serve-static@2.2.0':
dependencies:
'@types/http-errors': 2.0.5
'@types/node': 25.8.0
'@types/node': 25.9.0
'@types/triple-beam@1.3.5': {}
@@ -8079,6 +8334,8 @@ snapshots:
obug@2.1.1: {}
on-exit-leak-free@2.1.2: {}
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
@@ -8245,6 +8502,33 @@ snapshots:
picomatch@4.0.4: {}
pino-abstract-transport@2.0.0:
dependencies:
split2: 4.2.0
pino-http@10.5.0:
dependencies:
get-caller-file: 2.0.5
pino: 9.14.0
pino-std-serializers: 7.1.0
process-warning: 5.0.0
pino-std-serializers@7.1.0: {}
pino@9.14.0:
dependencies:
'@pinojs/redact': 0.4.0
atomic-sleep: 1.0.0
on-exit-leak-free: 2.1.2
pino-abstract-transport: 2.0.0
pino-std-serializers: 7.1.0
process-warning: 5.0.0
quick-format-unescaped: 4.0.4
real-require: 0.2.0
safe-stable-stringify: 2.5.0
sonic-boom: 4.2.1
thread-stream: 3.1.0
piscina@5.1.4:
optionalDependencies:
'@napi-rs/nice': 1.1.1
@@ -8334,6 +8618,8 @@ snapshots:
process-nextick-args@2.0.1: {}
process-warning@5.0.0: {}
progress@2.0.3: {}
prom-client@15.1.3:
@@ -8388,6 +8674,8 @@ snapshots:
queue-microtask@1.2.3: {}
quick-format-unescaped@4.0.4: {}
quick-lru@4.0.1: {}
quick-lru@7.3.0: {}
@@ -8446,6 +8734,8 @@ snapshots:
string_decoder: 1.3.0
util-deprecate: 1.0.2
real-require@0.2.0: {}
redent@3.0.0:
dependencies:
indent-string: 4.0.0
@@ -8887,6 +9177,10 @@ snapshots:
thirty-two@1.0.2: {}
thread-stream@3.1.0:
dependencies:
real-require: 0.2.0
through@2.3.8: {}
thunky@1.1.0: {}