From a3ba6c294495718aeb4ce7c11ce7fa838d82e197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BC=96=E7=A0=81=E7=8C=BF?= Date: Tue, 30 Sep 2025 10:00:10 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91=E9=87=8D?= =?UTF-8?q?=E6=9E=84=20=E5=B9=B6=E5=AE=9E=E7=8E=B0=E6=96=B0=E7=9A=84?= =?UTF-8?q?=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back-end/main.ts | 17 ++- docker/nacos-cluster/README.md | 2 +- docker/nginx/docker-compose.yml | 17 +++ docker/nginx/nginx.conf | 61 ++++++++ docker/traefik/docker-compose.yml | 3 +- front-end/app-product/.env.development | 9 +- front-end/app-product/.env.production | 10 +- front-end/app-product/package.json | 2 +- front-end/app-product/src/App.tsx | 7 +- .../product/views/ProductList/ProductList.tsx | 1 - front-end/app-product/vite.config.ts | 16 +- front-end/app-shared/.env.development | 7 +- front-end/app-shared/.env.production | 8 +- front-end/app-shared/package.json | 2 +- front-end/app-shared/vite.config.ts | 12 +- front-end/main-app/.env.development | 16 +- front-end/main-app/.env.production | 7 +- front-end/main-app/package.json | 2 +- front-end/main-app/src/app/main.ts | 5 +- front-end/main-app/vite.config.ts | 42 ++--- front-end/nacos-federation/src/index.ts | 4 - front-end/nacos-federation/src/utils.ts | 36 ----- .../nacos-federation/src/vite-plugin-nacos.ts | 123 --------------- .../README.md | 9 ++ .../package.json | 4 +- front-end/vite-plugin-nacos/src/index.ts | 56 +++++++ .../src/nacosServiceManage.ts | 82 +++++----- front-end/vite-plugin-nacos/src/utils.ts | 52 +++++++ .../src/vite-plugin-nacos.ts | 144 ++++++++++++++++++ front-end/vite-plugin-nacos/test.ts | 5 + .../tsconfig.json | 0 31 files changed, 459 insertions(+), 302 deletions(-) create mode 100644 docker/nginx/docker-compose.yml create mode 100644 docker/nginx/nginx.conf delete mode 100644 front-end/nacos-federation/src/index.ts delete mode 100644 front-end/nacos-federation/src/utils.ts delete mode 100644 front-end/nacos-federation/src/vite-plugin-nacos.ts rename front-end/{nacos-federation => vite-plugin-nacos}/README.md (83%) rename front-end/{nacos-federation => vite-plugin-nacos}/package.json (79%) create mode 100644 front-end/vite-plugin-nacos/src/index.ts rename front-end/{nacos-federation => vite-plugin-nacos}/src/nacosServiceManage.ts (97%) create mode 100644 front-end/vite-plugin-nacos/src/utils.ts create mode 100644 front-end/vite-plugin-nacos/src/vite-plugin-nacos.ts create mode 100644 front-end/vite-plugin-nacos/test.ts rename front-end/{nacos-federation => vite-plugin-nacos}/tsconfig.json (100%) diff --git a/back-end/main.ts b/back-end/main.ts index b4034bb..d081ed3 100644 --- a/back-end/main.ts +++ b/back-end/main.ts @@ -5,9 +5,9 @@ export const app = new App(); app.use(cors({ origin: "*", - allowHeaders: [ "*" ], - allowMethods: [ "POST", "GET", "OPTIONS" ], - exposeHeaders: [ "Content-Length", "X-Kuma-Revision" ], + allowHeaders: ["*"], + allowMethods: ["POST", "GET", "OPTIONS"], + exposeHeaders: ["Content-Length", "X-Kuma-Revision"], maxAge: 600, credentials: true, })) @@ -74,8 +74,17 @@ app.get("/api/productsInfo", (req: Request, ctx: { params: { id: number; }; }) = return new Response(Res(result)); }); + +// test 测试接口 +app.get("/nacos", (ctx: { params: { name: any; }; }) => { + console.log("test 测试接口 被运行"); + return new Response(JSON.stringify({ + title: "test测试接口" + })); +}); + const exampleLoggerMiddleware = define.middleware((ctx: { req: { method: any; url: any; }; next: () => any; }) => { - console.log(`${ ctx.req.method } ${ ctx.req.url }`); + console.log(`${ctx.req.method} ${ctx.req.url}`); return ctx.next(); }); diff --git a/docker/nacos-cluster/README.md b/docker/nacos-cluster/README.md index c6e71dd..5c030f7 100644 --- a/docker/nacos-cluster/README.md +++ b/docker/nacos-cluster/README.md @@ -98,7 +98,7 @@ ufw status ### 运行启动 -将本文件夹内所有文件上传到 1号服务器,然后执行命令: +将本文件夹内所有文件上传到 1号服务器,然后在文件夹内执行命令: ```bash # 部署栈,这个过程有点慢,因为需要拉取镜像,等! diff --git a/docker/nginx/docker-compose.yml b/docker/nginx/docker-compose.yml new file mode 100644 index 0000000..f610431 --- /dev/null +++ b/docker/nginx/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + nginx-proxy: + image: nginx:latest + container_name: nginx-proxy + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + restart: unless-stopped + networks: + - proxy-network + +networks: + proxy-network: + driver: bridge \ No newline at end of file diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..f6d323d --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,61 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # 上游服务器配置 + upstream nacos_cluster { + server 192.168.139.84:8848; + server 192.168.139.15:8848; + server 192.168.139.178:8848; + } + + + upstream nacos_ui { + server 192.168.139.84:8080; + server 192.168.139.15:8080; + server 192.168.139.178:8080; + } + + + server { + listen 80; + server_name cluster.nacos.com; + + # 反向代理配置 + location / { + proxy_pass http://nacos_cluster; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # 超时设置 + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + } + + server { + listen 80; + server_name ui.nacos.com; + + # 反向代理配置 + location / { + proxy_pass http://nacos_ui; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # 超时设置 + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + } +} \ No newline at end of file diff --git a/docker/traefik/docker-compose.yml b/docker/traefik/docker-compose.yml index 2af7328..19f8a87 100644 --- a/docker/traefik/docker-compose.yml +++ b/docker/traefik/docker-compose.yml @@ -8,8 +8,7 @@ services: - traefik-net ports: - "80:80" - - "8880:8080" - # network_mode: "host" # 主机网络模式 + - "8080:8080" volumes: - ./traefik.yml:/etc/traefik/traefik.yml - ./dynamic.yml:/etc/traefik/dynamic.yml # 动态配置文件 diff --git a/front-end/app-product/.env.development b/front-end/app-product/.env.development index 4b4a878..7445d77 100644 --- a/front-end/app-product/.env.development +++ b/front-end/app-product/.env.development @@ -1,15 +1,13 @@ # 请求协议 http or https(建议线上配置证书后,使用https) VITE_PREFIX=http:// -# Nacos 配置中心ip:端口 -VITE_NACOS_ADDRESS=192.168.31.101:8848 +# Nacos 配置中心 ip:端口 | 域名 +#VITE_NACOS_ADDRESS=cluster.nacos.com +VITE_NACOS_ADDRESS=192.168.139.84:8848 -# traefik 网关的地址 -VITE_TRAEFIK_ADDRESS=192.168.31.101 # 注册到 Nacos 的服务名称(微前端模块名称 VITE_SERVER_NAME=app-product - # 注册到 Nacos 的端口,也是前端运行的端口 VITE_SERVER_PORT=1301 @@ -25,3 +23,4 @@ VITE_MODEL_SHARED_NAME=app-shared + diff --git a/front-end/app-product/.env.production b/front-end/app-product/.env.production index 4b4a878..a0e61e6 100644 --- a/front-end/app-product/.env.production +++ b/front-end/app-product/.env.production @@ -1,11 +1,9 @@ # 请求协议 http or https(建议线上配置证书后,使用https) VITE_PREFIX=http:// -# Nacos 配置中心ip:端口 -VITE_NACOS_ADDRESS=192.168.31.101:8848 +# Nacos 配置中心 ip:端口 | 域名 +VITE_NACOS_ADDRESS=192.168.139.84:8848 -# traefik 网关的地址 -VITE_TRAEFIK_ADDRESS=192.168.31.101 # 注册到 Nacos 的服务名称(微前端模块名称 VITE_SERVER_NAME=app-product @@ -13,7 +11,6 @@ VITE_SERVER_NAME=app-product # 注册到 Nacos 的端口,也是前端运行的端口 VITE_SERVER_PORT=1301 - # # 说明:项目要调用哪些 微前端 模块 # 格式:VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供) @@ -22,6 +19,3 @@ VITE_SERVER_PORT=1301 # shared 共享资源模块 VITE_MODEL_SHARED_NAME=app-shared - - - diff --git a/front-end/app-product/package.json b/front-end/app-product/package.json index daec2cf..c357655 100644 --- a/front-end/app-product/package.json +++ b/front-end/app-product/package.json @@ -14,8 +14,8 @@ "type-check": "vue-tsc --build" }, "dependencies": { - "nacos-federation": "link:../nacos-federation", "pinia": "^3.0.3", + "vite-plugin-nacos": "link:../vite-plugin-nacos", "vue": "^3.5.21", "vue-router": "^4.5.1" }, diff --git a/front-end/app-product/src/App.tsx b/front-end/app-product/src/App.tsx index ea9b3f3..bda6020 100644 --- a/front-end/app-product/src/App.tsx +++ b/front-end/app-product/src/App.tsx @@ -1,7 +1,12 @@ -import { defineComponent } from 'vue' +import { defineComponent, onMounted } from 'vue' export default defineComponent({ setup() { + + onMounted(() => { + console.log("import.meta.env: ", import.meta.env) + }) + return () => ( <>
diff --git a/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx b/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx index 71f937d..1c1c862 100644 --- a/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx +++ b/front-end/app-product/src/features/product/views/ProductList/ProductList.tsx @@ -22,7 +22,6 @@ export default defineComponent({ onMounted(() => { - loadProductList() }) diff --git a/front-end/app-product/vite.config.ts b/front-end/app-product/vite.config.ts index 4546e50..e2e50e3 100644 --- a/front-end/app-product/vite.config.ts +++ b/front-end/app-product/vite.config.ts @@ -5,24 +5,18 @@ import vueDevTools from 'vite-plugin-vue-devtools' import federation from '@originjs/vite-plugin-federation'; import { fileURLToPath, URL } from "node:url"; import { visualizer } from 'rollup-plugin-visualizer'; +import { reg, init } from 'vite-plugin-nacos'; export default defineConfig(async ({ mode }) => { const env = loadEnv(mode, process.cwd()); - + const { VITE_MODEL_SHARED_URL } = await init(env) return { - base: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_SERVER_NAME }`, - server: { - port: Number(env.VITE_SERVER_PORT), - host: '0.0.0.0', - strictPort: true, - cors: true - }, plugins: [ - // nacosRegVitePlugin(), + reg(), federation({ name: env.VITE_SERVER_NAME, remotes: { - shared: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_MODEL_SHARED_NAME }/assets/remoteEntry.js` + shared: `${VITE_MODEL_SHARED_URL}/${env.VITE_MODEL_SHARED_NAME}/assets/remoteEntry.js` }, exposes: { // 暴露给主应用的资源(按功能分组) './ProductDomain': './src/domains/product/index.ts', // 领域层(实体、服务、接口等) @@ -50,7 +44,7 @@ export default defineConfig(async ({ mode }) => { cssCodeSplit: true, rollupOptions: { // 关键配置:告诉 Rollup 不要处理模块联邦的导入 - external: [ 'shared/normalize' ], + external: ['shared/normalize'], output: { minifyInternalExports: false } diff --git a/front-end/app-shared/.env.development b/front-end/app-shared/.env.development index 8fc4f74..3452635 100644 --- a/front-end/app-shared/.env.development +++ b/front-end/app-shared/.env.development @@ -1,11 +1,10 @@ # 请求协议 http or https(建议线上配置证书后,使用https) VITE_PREFIX=http:// -# Nacos 配置中心ip:端口 -VITE_NACOS_ADDRESS=192.168.31.101:8848 +# Nacos 配置中心 ip:端口 | 域名 +VITE_NACOS_ADDRESS=cluster.nacos.com +#VITE_NACOS_ADDRESS=192.168.139.84:8848 -# traefik 网关的地址 -VITE_TRAEFIK_ADDRESS=192.168.31.101 # 注册到 Nacos 的服务名称(微前端模块名称) VITE_SERVER_NAME=app-shared diff --git a/front-end/app-shared/.env.production b/front-end/app-shared/.env.production index 8fc4f74..d69ebd5 100644 --- a/front-end/app-shared/.env.production +++ b/front-end/app-shared/.env.production @@ -1,11 +1,9 @@ # 请求协议 http or https(建议线上配置证书后,使用https) VITE_PREFIX=http:// -# Nacos 配置中心ip:端口 -VITE_NACOS_ADDRESS=192.168.31.101:8848 - -# traefik 网关的地址 -VITE_TRAEFIK_ADDRESS=192.168.31.101 +# Nacos 配置中心 ip:端口 | 域名 +#VITE_NACOS_ADDRESS=cluster.nacos.com +VITE_NACOS_ADDRESS=192.168.139.84:8848 # 注册到 Nacos 的服务名称(微前端模块名称) VITE_SERVER_NAME=app-shared diff --git a/front-end/app-shared/package.json b/front-end/app-shared/package.json index 800f685..a868171 100644 --- a/front-end/app-shared/package.json +++ b/front-end/app-shared/package.json @@ -17,7 +17,7 @@ "@originjs/vite-plugin-federation": "1.3.8", "axios": "^1.12.2", "element-plus": "^2.11.3", - "nacos-federation": "link:../nacos-federation", + "vite-plugin-nacos": "link:../vite-plugin-nacos", "vue": "^3.5.21" }, "devDependencies": { diff --git a/front-end/app-shared/vite.config.ts b/front-end/app-shared/vite.config.ts index f687632..9e36aa2 100644 --- a/front-end/app-shared/vite.config.ts +++ b/front-end/app-shared/vite.config.ts @@ -6,21 +6,15 @@ import vueJsx from '@vitejs/plugin-vue-jsx' // @ts-ignore import vue from '@vitejs/plugin-vue'; import visualizer from 'rollup-plugin-visualizer'; +import { reg, init } from 'vite-plugin-nacos'; export default defineConfig(async ({ mode }): Promise => { const env = loadEnv(mode, process.cwd()); - + init(env) return { - base: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_SERVER_NAME }`, - server: { - port: Number(env.VITE_SERVER_PORT), - host: '0.0.0.0', - strictPort: true, - cors: true - }, plugins: [ - // nacosRegVitePlugin(), + reg(), federation({ name: env.VITE_SERVER_NAME, exposes: { diff --git a/front-end/main-app/.env.development b/front-end/main-app/.env.development index 5082c53..f20963d 100644 --- a/front-end/main-app/.env.development +++ b/front-end/main-app/.env.development @@ -1,11 +1,17 @@ # 请求协议 http or https(建议线上配置证书后,使用https) VITE_PREFIX=http:// -# Nacos 配置中心ip:端口 -VITE_NACOS_ADDRESS=192.168.139.84:8848 +# +# Nacos 集群的地址 +# 注意: +# 1: 内网微前端模块,直接填内网Nacos IP:端口 就行 +# 2: main-app位于公网,填 网关的公网地址 +# + +# 网关地址 +VITE_NACOS_ADDRESS=nacos.ddd.com:80 +# VITE_NACOS_ADDRESS=192.168.139.84:8848 -# traefik 网关的地址 -VITE_TRAEFIK_ADDRESS=192.168.31.101 # 注册到 Nacos 的服务名称(微前端模块名称) VITE_SERVER_NAME=main-app @@ -24,4 +30,4 @@ VITE_SERVER_PORT=1300 VITE_MODEL_PRODUCT_NAME=app-product # shared 共享资源模块 -VITE_MODEL_SHARED_NAME=app-shared \ No newline at end of file +VITE_MODEL_SHARED_NAME=app-shared diff --git a/front-end/main-app/.env.production b/front-end/main-app/.env.production index 8e34483..50e20f0 100644 --- a/front-end/main-app/.env.production +++ b/front-end/main-app/.env.production @@ -1,8 +1,9 @@ # 请求协议 http or https(建议线上配置证书后,使用https) VITE_PREFIX=http:// -# Nacos 配置中心ip:端口 -VITE_NACOS_ADDRESS=192.168.31.101:8848 +# Nacos 配置中心 ip:端口 | 域名 +VITE_NACOS_ADDRESS=cluster.nacos.com +#VITE_NACOS_ADDRESS=192.168.139.84:8848 # traefik 网关的地址 VITE_TRAEFIK_ADDRESS=192.168.31.101 @@ -24,4 +25,4 @@ VITE_SERVER_PORT=1300 VITE_MODEL_PRODUCT_NAME=app-product # shared 共享资源模块 -VITE_MODEL_SHARED_NAME=app-shared \ No newline at end of file +VITE_MODEL_SHARED_NAME=app-shared diff --git a/front-end/main-app/package.json b/front-end/main-app/package.json index e3c9408..b8c5b19 100644 --- a/front-end/main-app/package.json +++ b/front-end/main-app/package.json @@ -11,8 +11,8 @@ "type-check": "vue-tsc --build" }, "dependencies": { - "nacos-federation": "link:../nacos-federation", "pinia": "^3.0.3", + "vite-plugin-nacos": "link:../vite-plugin-nacos", "vue": "^3.5.21", "vue-facing-decorator": "^4.0.1", "vue-router": "^4.5.1" diff --git a/front-end/main-app/src/app/main.ts b/front-end/main-app/src/app/main.ts index 5767a31..224fd79 100644 --- a/front-end/main-app/src/app/main.ts +++ b/front-end/main-app/src/app/main.ts @@ -11,10 +11,11 @@ class Main { const app = createApp(App) app.use(createPinia()) - .use(router) - .mount('#app') + .use(router) + .mount('#app') // TODO 需要解决的地方 + console.log("import.meta.env: ", import.meta.env) window.__APP_CONFIG__ = import.meta.env; elementPlus.init(app) diff --git a/front-end/main-app/vite.config.ts b/front-end/main-app/vite.config.ts index f2ae89b..a2321b6 100644 --- a/front-end/main-app/vite.config.ts +++ b/front-end/main-app/vite.config.ts @@ -1,45 +1,22 @@ import path from 'path' -import { defineConfig, loadEnv } from 'vite' +import { defineConfig, loadEnv, ConfigEnv, mergeConfig } from 'vite' import vueJsx from '@vue3-oop/plugin-vue-jsx' import federation from '@originjs/vite-plugin-federation'; -import { nacosRegVitePlugin } from "nacos-federation"; +import { reg, init } from 'vite-plugin-nacos'; import { visualizer } from 'rollup-plugin-visualizer'; -export default defineConfig(async ({ mode }) => { - const env = loadEnv(mode, process.cwd()); - // const nacos-cluster = await nacosServiceManage.create({ - // serverList: env.VITE_NACOS_ADDRESS, - // serviceName: env.VITE_SERVER_NAME, - // port: Number(env.VITE_SERVER_PORT) - // }); - // const productUrl = await nacos-cluster.find(env.VITE_MODEL_PRODUCT_NAME) - // const sharedUrl = await nacos-cluster.find(env.VITE_MODEL_SHARED_NAME) +export default defineConfig(async (config: ConfigEnv) => { + const env = loadEnv(config.mode, process.cwd()); - return { - server: { - port: Number(env.VITE_SERVER_PORT), - host: '::', - strictPort: true, - }, + return mergeConfig({ plugins: [ - nacosRegVitePlugin(), - vueJsx(), + reg(), federation({ name: env.VITE_SERVER_NAME, remotes: { - // 【弃用】基于 Nacos 服务发现获取 微前端模块的地址 192.168.31.101:1301 - // product: `${ productUrl }/assets/remoteEntry.js`, - // shared: `${ sharedUrl }/assets/remoteEntry.js` - - // 实现:限流与熔断,重试,IP 白名单,压缩等 - // 基于 c 网关,由网关去寻找 微前端模块的地址 - // 网关的地址是 nodejs脚本 定时10秒访问Nacos api 后,生成Traefik配置文件dynamic.yml, - // 并把配置实时同步到Traefik中,如果 xxx微前端模块停止运行那么就会从 Nacos服务中注销, - // 那么dynamic.yml配置文件就会自动删减被下线的网关映射 - - product: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_MODEL_PRODUCT_NAME }/assets/remoteEntry.js`, - shared: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_MODEL_SHARED_NAME }/assets/remoteEntry.js` + product: `${env.VITE_PREFIX}${env.VITE_NACOS_ADDRESS}/${env.VITE_MODEL_PRODUCT_NAME}/assets/remoteEntry.js`, + shared: `${env.VITE_PREFIX}${env.VITE_NACOS_ADDRESS}/${env.VITE_MODEL_SHARED_NAME}/assets/remoteEntry.js` }, shared: { vue: { requiredVersion: '^3.5.18' }, @@ -47,6 +24,7 @@ export default defineConfig(async ({ mode }) => { pinia: { requiredVersion: '^3.0.3' } } }), + vueJsx(), visualizer({ title: "主应用依赖分析", filename: 'dist/stats.html', // 分析文件输出路径 @@ -73,5 +51,5 @@ export default defineConfig(async ({ mode }) => { '@domains': path.resolve(__dirname, './src/domains/') }, }, - } + }, await init(env)) }) diff --git a/front-end/nacos-federation/src/index.ts b/front-end/nacos-federation/src/index.ts deleted file mode 100644 index 27486aa..0000000 --- a/front-end/nacos-federation/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -// 从插件实现文件导入 -export * from './vite-plugin-nacos'; -export * from './nacosServiceManage'; -export * from './utils'; \ No newline at end of file diff --git a/front-end/nacos-federation/src/utils.ts b/front-end/nacos-federation/src/utils.ts deleted file mode 100644 index 560b976..0000000 --- a/front-end/nacos-federation/src/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { networkInterfaces } from "os"; - -/** - * 获取本机局域网IPv4地址 - * @returns 局域网IP地址(如 '192.168.1.100') - */ -export function getLocalIP(): string { - - const interfaces = networkInterfaces(); - for (const devName in interfaces) { - const iface = interfaces[devName]; - if (!iface) continue; - - for (const alias of iface) { - if ( - alias.family === 'IPv4' && - alias.address !== '127.0.0.1' && - !alias.internal - ) { - return alias.address; - } - } - } - - return '127.0.0.1'; -} - -/** - * 格式化服务地址 - * @param ip IP地址 - * @param port 端口号 - * @returns 完整服务地址(如 'http://192.168.1.100:1300') - */ -export function formatServiceUrl(ip: string, port: number): string { - return `http://${ ip }:${ port }`; -} diff --git a/front-end/nacos-federation/src/vite-plugin-nacos.ts b/front-end/nacos-federation/src/vite-plugin-nacos.ts deleted file mode 100644 index ade00fb..0000000 --- a/front-end/nacos-federation/src/vite-plugin-nacos.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { loadEnv, Plugin, UserConfig } from 'vite'; -import { getLocalIP, nacosServiceManage, RegisterOptions } from './index'; -import chalk from 'chalk'; - -const log = console.log; - -export function nacosRegVitePlugin(options?: Partial): Plugin { - let nacosInstance: nacosServiceManage | null = null; - - return { - name: 'vite-plugin-nacos', - async config(userConfig: UserConfig, { mode }) { - // 修改base,增加资源来源地址,修复资源错误加载的问题 - const env = loadEnv(mode, process.cwd()); - let baseUrl = "" - // 主模块 main-app 不加 base 前缀,其他模块都加, - // 也是为了命中 traefik 路由代理 - if (env.VITE_SERVER_NAME != "main-app") { - baseUrl = `${ env.VITE_SERVER_NAME }/` - } - return { - // http(https)://前端运行的服务器所在ip/前端模块名 - base: `${ env.VITE_PREFIX }${ getLocalIP() }/${ baseUrl }`, - } - }, - async configResolved(config) { - - const env = config.env; - const registerOptions: RegisterOptions = { - serverList: env.VITE_NACOS_ADDRESS || options?.serverList!, - serviceName: env.VITE_SERVER_NAME || options?.serviceName!, - port: Number(env.VITE_SERVER_PORT || options?.port), - namespace: options?.namespace || 'public' - }; - - // 验证必要配置 - if (!registerOptions.serverList) { - throw new Error('[nacos-cluster-federation] 缺少Nacos服务列表配置,请设置VITE_NACOS_ADDRESS环境变量或插件选项'); - } - - if (!registerOptions.serviceName) { - throw new Error('[nacos-cluster-federation] 缺少服务名称配置,请设置VITE_SERVER_NAME环境变量或插件选项'); - } - if (!registerOptions.port) { - throw new Error('[nacos-cluster-federation] 缺少服务端口配置,请设置VITE_SERVER_PORT环境变量或插件选项'); - } - - // 初始化Nacos实例 - nacosInstance = await nacosServiceManage.create(registerOptions); - - const baseUrl = await nacosInstance.getConfig("baseUrl", "DEFAULT_GROUP") - const baseUrlConfig = baseUrl ? JSON.parse(baseUrl) : {}; - - baseUrlConfig.httpTimeOut = await nacosInstance.getConfig("httpTimeOut", "DEFAULT_GROUP") - - - const customizeEnv: Record = {}; - Object.keys(baseUrlConfig).forEach(key => { - const envKey = `import.meta.env.VITE_${ key.toUpperCase() }`; - customizeEnv[envKey] = JSON.stringify(baseUrlConfig[key]); - }); - - // @ts-ignore - Object.assign(config.define, customizeEnv); - Logs(env) - }, - async closeBundle() { - // // 服务关闭时注销Nacos实例 - // if (nacosInstance) { - // await nacosInstance.destroy(); - // } - }, - } -} - -function Logs(env: Record) { - const moduleName = [] - for (const key in env) { - if (key.includes("VITE_MODEL_")) { - moduleName.push(env[key]); - } - } - moduleName.length == 0 ? moduleName.push('无') : null - const webUI = env.VITE_NACOS_ADDRESS.split(":")[0] - console.log() - - console.log(dedentTemplateString(` - ${ chalk.blue.bold('微服务工具:') } - ${ chalk.green('➜') } Naco 配置中心: ${ env.VITE_PREFIX }${ env.VITE_NACOS_ADDRESS } - ${ chalk.green('➜') } WebUi: ${ env.VITE_PREFIX }${ webUI }:8085 - ${ chalk.green('➜') } Prometheus 监控: ${ env.VITE_PREFIX }${ webUI }:9090 - ${ chalk.green('➜') } Grafana 面板: ${ env.VITE_PREFIX }${ webUI }:3000 - ${ chalk.green('➜') } Traefik 网关: ${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS } - ${ chalk.green('➜') } WebUi: ${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }:8880 - - ${ chalk.blue.bold('微前端信息:') } - ${ chalk.green('➜') } 主模块:${ chalk.green(env.VITE_SERVER_NAME) } - - ${ chalk.blue.bold('调用模块:') } - ${ chalk.green('➜') } [ ${ chalk.yellow(moduleName) } ] - `)) - console.log() -} - -function dedentTemplateString(str: string) { - // 分割成行 - const lines = str.split('\n'); - - // 移除首尾空行 - if (lines[0].trim() === '') lines.shift(); - if (lines[lines.length - 1].trim() === '') lines.pop(); - - // 找到最小缩进量 - const minIndent = lines.reduce((min, line) => { - if (line.trim() === '') return min; // 跳过空行 - // @ts-ignore - const indent = line.match(/^\s*/)[0].length; - return indent < min ? indent : min; - }, Infinity); - - // 移除统一缩进 - return lines.map(line => line.slice(minIndent)).join('\n'); -} \ No newline at end of file diff --git a/front-end/nacos-federation/README.md b/front-end/vite-plugin-nacos/README.md similarity index 83% rename from front-end/nacos-federation/README.md rename to front-end/vite-plugin-nacos/README.md index 2db351e..0abaa39 100644 --- a/front-end/nacos-federation/README.md +++ b/front-end/vite-plugin-nacos/README.md @@ -6,6 +6,12 @@ 主模块:main-app -> Traefik -> ConfigServer(nacos-federation) -> Nacos + main-app 发送 http 请求 + /nacos/reg + 参数: + ip,port + + 2 获取配置 @@ -15,6 +21,9 @@ 主模块: main-app -> Traefik -> ConfigServer(nacos-federation) -> Nacos + 注意: + + 增加功能: 认证授权机制 diff --git a/front-end/nacos-federation/package.json b/front-end/vite-plugin-nacos/package.json similarity index 79% rename from front-end/nacos-federation/package.json rename to front-end/vite-plugin-nacos/package.json index e768c76..e16391e 100644 --- a/front-end/nacos-federation/package.json +++ b/front-end/vite-plugin-nacos/package.json @@ -1,7 +1,7 @@ { - "name": "nacos-federation", + "name": "vite-plugin-nacos", "version": "1.1.0", - "description": "Nacos服务注册与发现插件,用于模块联邦", + "description": "Vite 插件,实现Nacos服务注册与发现插件,用于模块联邦", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ diff --git a/front-end/vite-plugin-nacos/src/index.ts b/front-end/vite-plugin-nacos/src/index.ts new file mode 100644 index 0000000..19c00b1 --- /dev/null +++ b/front-end/vite-plugin-nacos/src/index.ts @@ -0,0 +1,56 @@ +// 从插件实现文件导入 +export * from './vite-plugin-nacos'; +export * from './utils'; +import { log } from 'console'; +import { nacosServiceManage } from './nacosServiceManage'; +import { mergeConfig } from 'vite'; + +export const init = async (env: Record) => { + const nacos = await nacosServiceManage.create({ + serverList: env.VITE_NACOS_ADDRESS, + serviceName: env.VITE_SERVER_NAME, + port: Number(env.VITE_SERVER_PORT) + }); + + if (env.VITE_SERVER_NAME === "main-app") { + log("不需要服务发现,进行配置发现...") + + const baseUrl = await nacos.getConfig("baseUrl", "DEFAULT_GROUP") + const baseUrlConfig = baseUrl ? JSON.parse(baseUrl) : {}; + baseUrlConfig.httpTimeOut = await nacos.getConfig("httpTimeOut", "DEFAULT_GROUP") + + // 将新配置处理一下,需要 JSON.stringify + const customizeEnv: Record> = { define: {} }; + Object.keys(baseUrlConfig).forEach(key => { + customizeEnv.define[`import.meta.env.VITE_${key.toUpperCase()}`] = JSON.stringify(baseUrlConfig[key]); + }); + + // 将老配置处理一下,需要 JSON.stringify + const existingDefine: Record> = { define: {} } + Object.keys(env).forEach(key => { + existingDefine.define[`import.meta.env.${key}`] = JSON.stringify(env[key]) + }) + + return mergeConfig(existingDefine, customizeEnv) + + } else { + log("进行服务发现,获取依赖模块的地址...") + let modelName = [] + for (const key in env) { + if (key.includes("VITE_MODEL_")) modelName.push(env[key]) + } + + const modelUrl: Record = {} + + if (modelName.length !== 0) { + for (const val of modelName) modelUrl[`VITE_MODEL_${val.replace("app-", "").toUpperCase()}_URL`] = await nacos.find(val) + } + + log("modelUrl: ", modelUrl) + return modelUrl + } + + + + +} \ No newline at end of file diff --git a/front-end/nacos-federation/src/nacosServiceManage.ts b/front-end/vite-plugin-nacos/src/nacosServiceManage.ts similarity index 97% rename from front-end/nacos-federation/src/nacosServiceManage.ts rename to front-end/vite-plugin-nacos/src/nacosServiceManage.ts index c8e023b..4650229 100644 --- a/front-end/nacos-federation/src/nacosServiceManage.ts +++ b/front-end/vite-plugin-nacos/src/nacosServiceManage.ts @@ -4,7 +4,7 @@ import chalk from 'chalk'; const logger = console; -export interface RegisterOptions { +interface RegisterOptions { serverList: string; /** 服务名称(如 'main-app') */ serviceName: string; @@ -42,21 +42,13 @@ export class nacosServiceManage { } const instance = new nacosServiceManage(option); - // await instance.initClient(); - // await instance.initConfig(); + await instance.initClient(); + await instance.initConfig(); this.instanceMap.set(configKey, instance); return instance; } - /** - * 生成配置唯一标识:基于 serverList + namespace + serviceName(确保不同配置对应不同实例) - */ - private static getConfigKey(option: RegisterOptions): string { - const namespace = option.namespace || 'public'; - return `${ option.serverList }-${ namespace }-${ option.serviceName }`; - } - // 服务注册 public async initClient() { this.nacosClient = new NacosNamingClient({ @@ -69,15 +61,11 @@ export class nacosServiceManage { } /** - * 获取配置 - * @param dataId - * @param group + * 生成配置唯一标识:基于 serverList + namespace + serviceName(确保不同配置对应不同实例) */ - async getConfig(dataId: string, group: string = "DEFAULT_GROUP") { - const res = await this.configClient.getConfig(dataId, group) - console.log() - console.log(`${ chalk.blue('[nacos-cluster-federation] 配置发现:') }\n ${ chalk.green('➜') } ${ chalk.green(dataId) } 的配置为: ${ chalk.green(res) }`) - return res; + private static getConfigKey(option: RegisterOptions): string { + const namespace = option.namespace || 'public'; + return `${ option.serverList }-${ namespace }-${ option.serviceName }`; } /** @@ -93,16 +81,18 @@ export class nacosServiceManage { }) } - /** - * 初始化配置服务 - * @private - */ - private async initConfig() { - this.configClient = new NacosConfigClient({ - serverAddr: this.initOptions.serverList, - }); - } + /** + * 获取配置 + * @param dataId + * @param group + */ + async getConfig(dataId: string, group: string = "DEFAULT_GROUP") { + const res = await this.configClient.getConfig(dataId, group) + console.log() + console.log(`${ chalk.blue('[nacos-cluster-federation] 配置发现:') }\n ${ chalk.green('➜') } ${ chalk.green(dataId) } 的配置为: ${ chalk.green(res) }`) + return res; + } /** * 从Nacos发现服务地址 @@ -118,6 +108,29 @@ export class nacosServiceManage { return formatServiceUrl(instances[0].ip, instances[0].port); } + /** + * 初始化配置服务 + * @private + */ + private async initConfig() { + this.configClient = new NacosConfigClient({ + serverAddr: this.initOptions.serverList, + }); + } + + /** + * 注册服务到Nacos并返回客户端实例 + */ + private async reg() { + const { serviceName, port } = this.initOptions; + const ip = getLocalIP(); + // @ts-ignore + await this.nacosClient.registerInstance(serviceName, { port, ip }) + logger.log() + logger.log(chalk.blue("[nacos-cluster-federation] 服务注册:")) + logger.log(` ${ chalk.green('➜') } ${ chalk.green(serviceName) } (${ formatServiceUrl(ip, port) }) 已注册到配置中心!`); + } + /** * 手动销毁实例(如服务下线时) */ @@ -134,18 +147,5 @@ export class nacosServiceManage { nacosServiceManage.instanceMap.delete(configKey); } - /** - * 注册服务到Nacos并返回客户端实例 - */ - private async reg() { - const { serviceName, port } = this.initOptions; - const ip = getLocalIP(); - // @ts-ignore - await this.nacosClient.registerInstance(serviceName, { port, ip }) - logger.log() - logger.log(chalk.blue("[nacos-cluster-federation] 服务注册:")) - logger.log(` ${ chalk.green('➜') } ${ chalk.green(serviceName) } (${ formatServiceUrl(ip, port) }) 已注册到配置中心!`); - } - } \ No newline at end of file diff --git a/front-end/vite-plugin-nacos/src/utils.ts b/front-end/vite-plugin-nacos/src/utils.ts new file mode 100644 index 0000000..938bb72 --- /dev/null +++ b/front-end/vite-plugin-nacos/src/utils.ts @@ -0,0 +1,52 @@ +import { networkInterfaces } from "os"; + + +class Utils { + /** + * 获取本机局域网IPv4地址 + * @returns 局域网IP地址(如 '192.168.1.100') + */ + static getLocalIP(): string { + const interfaces = networkInterfaces(); + for (const devName in interfaces) { + const iface = interfaces[devName]; + if (!iface) continue; + + for (const alias of iface) { + if ( + alias.family === 'IPv4' && + alias.address !== '127.0.0.1' && + !alias.internal + ) { + return alias.address; + } + } + } + + return '127.0.0.1'; + } + + /** + * 格式化服务地址 + * @param ip IP地址 + * @param port 端口号 + * @returns 完整服务地址(如 'http://192.168.1.100:1300') + */ + static formatServiceUrl(ip: string, port: number): string { + return `http://${ ip }:${ port }`; + } + + /** + * VITE_DEPEND_MODEL_NAME_LIST 配置 格式化成数组 + * app-shared,app-test + * @param arg ["app-shared", "app-test"] + */ + static dependModel(arg: string): string[] { + return arg.split(',').map(name => name.trim()).filter(Boolean) || []; + } +} + +export const { + getLocalIP, formatServiceUrl, + dependModel +} = Utils \ No newline at end of file diff --git a/front-end/vite-plugin-nacos/src/vite-plugin-nacos.ts b/front-end/vite-plugin-nacos/src/vite-plugin-nacos.ts new file mode 100644 index 0000000..21cd0aa --- /dev/null +++ b/front-end/vite-plugin-nacos/src/vite-plugin-nacos.ts @@ -0,0 +1,144 @@ +import { loadEnv, Plugin, UserConfig } from 'vite'; +import { NacosConfigClient, NacosNamingClient } from 'nacos'; +import { getLocalIP, dependModel, formatServiceUrl } from './utils'; +import chalk from 'chalk'; + +const logger = console; + +export interface RegisterOptions { + serverList: string; + /** 服务名称(如 'main-app') */ + serviceName: string; + /** 服务端口号 */ + port: number; + /** 命名空间(用于环境隔离,默认 'public') */ + namespace?: string; +} + +export function reg(options?: Partial): Plugin { + return { + name: 'vite-plugin-nacos', + async config(config: UserConfig, { mode }) { + // 修改base,增加资源来源地址,修复资源错误加载的问题 + const env = loadEnv(mode, process.cwd()); + let baseUrl = "" + + // 主模块 main-app 不加 base 前缀,其他模块都加, + // 也是为了命中 traefik 路由代理 + if (env.VITE_SERVER_NAME != "main-app") { + baseUrl = `${env.VITE_SERVER_NAME}/` + } + + return { + server: { + allowedHosts: [ + '.ddd.com', + ], + port: Number(env.VITE_SERVER_PORT), + host: '::', + strictPort: true, + cors: true + }, + // TODO dist 上线到服务器后,http://192.168.31.101:1301/ 会出问题 + // http(https)://前端运行的服务器所在ip/前端模块名 + base: `${env.VITE_PREFIX}${getLocalIP()}:${env.VITE_SERVER_PORT}/${baseUrl}` + }; + }, + async configResolved(config) { + const env = config.env; + + // const registerOptions: RegisterOptions = { + // serverList: env.VITE_NACOS_ADDRESS || options?.serverList!, + // serviceName: env.VITE_SERVER_NAME || options?.serviceName!, + // port: Number(env.VITE_SERVER_PORT || options?.port), + // namespace: options?.namespace || 'public' + // }; + // + // // 验证必要配置 + // if (!registerOptions.serverList) { + // throw new Error('[nacos-cluster-federation] 缺少Nacos服务列表配置,请设置VITE_NACOS_ADDRESS环境变量或插件选项'); + // } + // + // if (!registerOptions.serviceName) { + // throw new Error('[nacos-cluster-federation] 缺少服务名称配置,请设置VITE_SERVER_NAME环境变量或插件选项'); + // } + // if (!registerOptions.port) { + // throw new Error('[nacos-cluster-federation] 缺少服务端口配置,请设置VITE_SERVER_PORT环境变量或插件选项'); + // } + + // // 初始化Nacos实例 + // nacosInstance = await nacosServiceManage.create(registerOptions); + // + // const baseUrl = await nacosInstance.getConfig("baseUrl", "DEFAULT_GROUP") + // const baseUrlConfig = baseUrl ? JSON.parse(baseUrl) : {}; + // + // baseUrlConfig.httpTimeOut = await nacosInstance.getConfig("httpTimeOut", "DEFAULT_GROUP") + // + // + // const customizeEnv: Record = {}; + // Object.keys(baseUrlConfig).forEach(key => { + // const envKey = `import.meta.env.VITE_${ key.toUpperCase() }`; + // customizeEnv[envKey] = JSON.stringify(baseUrlConfig[key]); + // }); + // + // // @ts-ignore + // Object.assign(config.define, customizeEnv); + // Logs(env) + }, + async closeBundle() { + // // 服务关闭时注销Nacos实例 + // if (nacosInstance) { + // await nacosInstance.destroy(); + // } + }, + } +} + +function Logs(env: Record) { + const moduleName = [] + for (const key in env) { + if (key.includes("VITE_MODEL_")) { + moduleName.push(env[key]); + } + } + moduleName.length == 0 ? moduleName.push('无') : null + const webUI = env.VITE_NACOS_ADDRESS.split(":")[0] + console.log() + + console.log(dedentTemplateString(` + ${chalk.blue.bold('微服务工具:')} + ${chalk.green('➜')} Naco 配置中心: ${env.VITE_PREFIX}${env.VITE_NACOS_ADDRESS} + ${chalk.green('➜')} WebUi: ${env.VITE_PREFIX}${webUI}:8085 + ${chalk.green('➜')} Prometheus 监控: ${env.VITE_PREFIX}${webUI}:9090 + ${chalk.green('➜')} Grafana 面板: ${env.VITE_PREFIX}${webUI}:3000 + ${chalk.green('➜')} Traefik 网关: ${env.VITE_PREFIX}${env.VITE_TRAEFIK_ADDRESS} + ${chalk.green('➜')} WebUi: ${env.VITE_PREFIX}${env.VITE_TRAEFIK_ADDRESS}:8880 + + ${chalk.blue.bold('微前端信息:')} + ${chalk.green('➜')} 主模块:${chalk.green(env.VITE_SERVER_NAME)} + + ${chalk.blue.bold('调用模块:')} + ${chalk.green('➜')} [ ${chalk.yellow(moduleName)} ] + `)) + console.log() +} + +function dedentTemplateString(str: string) { + // 分割成行 + const lines = str.split('\n'); + + // 移除首尾空行 + if (lines[0].trim() === '') lines.shift(); + if (lines[lines.length - 1].trim() === '') lines.pop(); + + // 找到最小缩进量 + const minIndent = lines.reduce((min, line) => { + if (line.trim() === '') return min; // 跳过空行 + // @ts-ignore + const indent = line.match(/^\s*/)[0].length; + return indent < min ? indent : min; + }, Infinity); + + // 移除统一缩进 + return lines.map(line => line.slice(minIndent)).join('\n'); +} \ No newline at end of file diff --git a/front-end/vite-plugin-nacos/test.ts b/front-end/vite-plugin-nacos/test.ts new file mode 100644 index 0000000..77943e2 --- /dev/null +++ b/front-end/vite-plugin-nacos/test.ts @@ -0,0 +1,5 @@ +import { nacosServiceManage } from "./src"; + +const main = async () => { + const nacos = new nacosServiceManage.create({}) +} diff --git a/front-end/nacos-federation/tsconfig.json b/front-end/vite-plugin-nacos/tsconfig.json similarity index 100% rename from front-end/nacos-federation/tsconfig.json rename to front-end/vite-plugin-nacos/tsconfig.json