feat : 跨域问题,前后端本地开发模式,环境配置调整

This commit is contained in:
翟二远 2026-04-08 16:52:28 +08:00
parent 2a3e269218
commit 1cfd18420e
20 changed files with 116 additions and 48 deletions

View file

@ -1,11 +1,11 @@
# SkillHub production draft for bare-metal deployment.
# Copy this to .env.release and replace every value marked TODO.
# Copy this to .env.dev.release and replace every value marked TODO.
#
# Recommended workflow:
# 1. cp .env.release.draft .env.release
# 1. cp .env.dev.release.draft .env.dev.release
# 2. Edit TODO values
# 3. make validate-release-config
# 4. docker compose --env-file .env.release -f compose.release.yml up -d
# 4. docker compose --env-file .env.dev.release -f compose.release.yml up -d
# Pin a released image tag in production.
SKILLHUB_VERSION=v0.1.0-beta.5

3
.gitignore vendored
View file

@ -28,6 +28,9 @@ yarn-error.log*
.env.*
!.env.example
!.env.*.example
!web/.env.dev
!web/.env.fat
!web/.env.production
.dev/
.memory/
*.local.json

View file

@ -225,9 +225,12 @@ web-deps: ## 确保前端依赖可用(本地开发优先复用现有 node_modu
web-install-ci: ## 以 CI 方式安装前端依赖
cd web && CI=true pnpm install --frozen-lockfile
dev-web: ## 启动前端开发服务器
local-web: ## 启动前端开发服务器
cd web && pnpm run dev
dev-web: ## 启动前端开发服务器
cd web && pnpm run fat
build-frontend: web-deps ## 构建前端
cd web && pnpm run build

View file

@ -211,7 +211,7 @@ curl -fsSL https://imageless.oss-cn-beijing.aliyuncs.com/runtime.sh | sh -s -- u
3. Start the stack with Docker Compose.
```bash
cp .env.release.example .env.release
cp .env.dev.release.example .env.dev.release
```
Recommended image tags:
@ -223,7 +223,7 @@ Start the runtime:
```bash
make validate-release-config
docker compose --env-file .env.release -f compose.release.yml up -d
docker compose --env-file .env.dev.release -f compose.release.yml up -d
```
Then open:
@ -234,7 +234,7 @@ Then open:
Stop it with:
```bash
docker compose --env-file .env.release -f compose.release.yml down
docker compose --env-file .env.dev.release -f compose.release.yml down
```
The runtime stack uses its own Compose project name, so it does not

View file

@ -97,9 +97,9 @@ make dev-all-reset
### 5.1 启动
```bash
cp .env.release.example .env.release
cp .env.dev.release.example .env.dev.release
make validate-release-config
docker compose --env-file .env.release -f compose.release.yml up -d
docker compose --env-file .env.dev.release -f compose.release.yml up -d
```
默认访问地址:

View file

@ -23,16 +23,16 @@ git clone https://github.com/iflytek/skillhub.git
cd skillhub
# 2. 复制环境变量模板
cp .env.release.example .env.release
cp .env.dev.release.example .env.dev.release
# 3. 编辑配置
# 修改 .env.release 中的配置项,特别是密码和公网地址
# 修改 .env.dev.release 中的配置项,特别是密码和公网地址
# 4. 验证配置
make validate-release-config
# 5. 启动服务
docker compose --env-file .env.release -f compose.release.yml up -d
docker compose --env-file .env.dev.release -f compose.release.yml up -d
```
## 配置说明
@ -43,7 +43,7 @@ docker compose --env-file .env.release -f compose.release.yml up -d
```bash
# 检查容器状态
docker compose --env-file .env.release -f compose.release.yml ps
docker compose --env-file .env.dev.release -f compose.release.yml ps
# 检查后端健康状态
curl -i http://127.0.0.1:8080/actuator/health

View file

@ -23,16 +23,16 @@ git clone https://github.com/iflytek/skillhub.git
cd skillhub
# 2. Copy environment variable template
cp .env.release.example .env.release
cp .env.dev.release.example .env.dev.release
# 3. Edit configuration
# Modify configuration items in .env.release, especially passwords and public URLs
# Modify configuration items in .env.dev.release, especially passwords and public URLs
# 4. Validate configuration
make validate-release-config
# 5. Start services
docker compose --env-file .env.release -f compose.release.yml up -d
docker compose --env-file .env.dev.release -f compose.release.yml up -d
```
## Configuration
@ -43,7 +43,7 @@ See [Configuration](./configuration) documentation for details.
```bash
# Check container status
docker compose --env-file .env.release -f compose.release.yml ps
docker compose --env-file .env.dev.release -f compose.release.yml ps
# Check backend health
curl -i http://127.0.0.1:8080/actuator/health

View file

@ -1,7 +1,7 @@
#!/bin/sh
set -eu
ENV_FILE="${1:-.env.release}"
ENV_FILE="${1:-.env.dev.release}"
if [ ! -f "$ENV_FILE" ]; then
echo "ERROR: env file not found: $ENV_FILE" >&2

View file

@ -0,0 +1,31 @@
package com.iflytek.skillhub.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
@Configuration
public class CorsConfig {
@Value("${skillhub.cors.allowed-origins:http://localhost:3000,http://localhost:3001}")
private List<String> allowedOrigins;
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(allowedOrigins);
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

View file

@ -63,6 +63,8 @@ spring:
max-request-size: 100MB
skillhub:
cors:
allowed-origins: ${SKILLHUB_CORS_ALLOWED_ORIGINS:http://localhost:3000,http://localhost:3001,http://sz-api.indusmind.local}
auth:
mock:
enabled: ${SKILLHUB_AUTH_MOCK_ENABLED:false}

View file

@ -101,6 +101,8 @@ public class SecurityConfig {
};
http
.cors(cors -> cors.configurationSource(http.getSharedObject(org.springframework.context.ApplicationContext.class)
.getBean(org.springframework.web.cors.CorsConfigurationSource.class)))
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.csrfTokenRequestHandler(csrfHandler)

6
web/.env.dev Normal file
View file

@ -0,0 +1,6 @@
# 本地开发环境(pnpm run dev)
# API 走 vite proxy 代理到本地后端,无需设置 base URL
VITE_API_BASE_URL=
VITE_APP_BASE_URL=
VITE_BASE_PATH=/
VITE_DEV_PROXY_TARGET=http://localhost:8000

5
web/.env.fat Normal file
View file

@ -0,0 +1,5 @@
# FAT 环境(本地前端 + 远程后端联调)
VITE_API_BASE_URL=/skillhub-server
VITE_APP_BASE_URL=
VITE_BASE_PATH=/
VITE_DEV_PROXY_TARGET=http://sz-api.indusmind.local

4
web/.env.production Normal file
View file

@ -0,0 +1,4 @@
# 生产/云效部署环境(pnpm build)
VITE_API_BASE_URL=/skillhub-server/
VITE_APP_BASE_URL=/skillhub-web/index.html
VITE_BASE_PATH=/skillhub-web/

View file

@ -4,7 +4,6 @@ WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
ENV VITE_BASE_PATH=/skillhub-web/
RUN pnpm build
FROM jsessh-registry.cn-shanghai.cr.aliyuncs.com/base-images/nginx-skillhub:alpine

View file

@ -5,8 +5,10 @@
"type": "module",
"scripts": {
"install:ci": "pnpm install --frozen-lockfile",
"dev": "vite",
"fat": "vite --mode fat",
"dev": "vite --mode dev",
"build": "tsc -b && vite build",
"build:production": "tsc -b && vite build --mode production",
"preview": "vite preview",
"test": "vitest run",
"test:e2e": "playwright test",

View file

@ -76,7 +76,8 @@ function getRuntimeConfig(): RuntimeConfig {
}
function getApiBaseUrl(): string {
return getRuntimeConfig().apiBaseUrl || '/skillhub-server/'
console.log('RuntimeConfig', getRuntimeConfig())
return getRuntimeConfig().apiBaseUrl || import.meta.env.VITE_API_BASE_URL || ''
}
function parseBooleanFlag(value: string | undefined): boolean {

View file

@ -17,8 +17,8 @@ async function loadRuntimeConfig() {
function ensureRuntimeConfigFallback() {
window.__SKILLHUB_RUNTIME_CONFIG__ ??= {
apiBaseUrl: '/skillhub-server/',
appBaseUrl: '/skillhub-web/',
apiBaseUrl: import.meta.env.VITE_API_BASE_URL || '',
appBaseUrl: import.meta.env.VITE_APP_BASE_URL || '',
authDirectEnabled: 'false',
authDirectProvider: '',
authSessionBootstrapEnabled: 'false',

View file

@ -78,7 +78,7 @@ export function UserMenu({ user, triggerClassName }: UserMenuProps) {
// Always clear cache and redirect, even if API call fails
clearSessionScopedQueries(queryClient)
queryClient.setQueryData(['auth', 'me'], null)
window.location.href = '/skillhub-web/index.html'
window.location.href = import.meta.env.BASE_URL || '/'
}
}

View file

@ -1,30 +1,40 @@
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import * as path from 'path'
export default defineConfig({
base: process.env.VITE_BASE_PATH || '/',
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
watch: {
usePolling: true,
interval: 150,
},
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
'/oauth2': {
target: 'http://localhost:8080',
changeOrigin: true,
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
const proxyTarget = env.VITE_DEV_PROXY_TARGET || 'http://localhost:8080'
return {
base: env.VITE_BASE_PATH || '/',
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
},
server: {
port: 3000,
watch: {
usePolling: true,
interval: 150,
},
proxy: {
'/skillhub-server': {
target: proxyTarget,
changeOrigin: true,
},
'/api': {
target: proxyTarget,
changeOrigin: true,
},
'/oauth2': {
target: proxyTarget,
changeOrigin: true,
},
},
},
}
})