提交 bfe4669f authored 作者: 王鹏飞's avatar 王鹏飞

refactor: serve admin app from admin subpath

上级 265c9f40
VITE_API_URL = https://zijingebook.ezijing.com/api
VITE_API_PREFIX = /api
# VITE_API_WEBSOCKET_URL = wss://zijingebook.ezijing.com
VITE_API_WEBSOCKET_URL = wss://zijingebook.ezijing.com/ws
VITE_API_URL=https://zijingebook.ezijing.com/api/admin
VITE_API_PREFIX=/api/admin
VITE_ADMIN_API_TARGET=http://127.0.0.1:7419
VITE_API_WEBSOCKET_URL=wss://zijingebook.ezijing.com/ws
VITE_API_URL = https://zijingebook.ezijing.com/api
VITE_API_PREFIX = /api
# VITE_API_WEBSOCKET_URL = wss://zijingebook.ezijing.com
VITE_API_WEBSOCKET_URL = wss://zijingebook.ezijing.com/ws
VITE_API_URL=https://zijingebook.ezijing.com/api/admin
VITE_API_PREFIX=/api/admin
VITE_ADMIN_API_TARGET=http://127.0.0.1:7419
VITE_API_WEBSOCKET_URL=wss://zijingebook.ezijing.com/ws
......@@ -8,7 +8,7 @@
</head>
<body>
<div id="root"></div>
<script src="/formula-editor/dist/formula-editor.min.js"></script>
<script src="%BASE_URL%formula-editor/dist/formula-editor.min.js"></script>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
......@@ -9,7 +9,7 @@
"build:dev": "vite build --mode development",
"preview": "vite preview",
"lint": "eslint --ext .js,.jsx,.ts,.tsx --fix --ignore-path .gitignore ./src",
"test": "node --test src/store/storage-import.test.mjs"
"test": "node --test src/store/storage-import.test.mjs src/utils/*.test.js"
},
"dependencies": {
"@ant-design/icons": "^5.4.0",
......
......@@ -18,7 +18,7 @@ createRoot(document.getElementById('root')).render(
<PersistGate loading={null} persistor={persistor}>
<ConfigProvider locale={zhCN} theme={{ token: { colorPrimary: '#ab1941' } }}>
<App>
<BrowserRouter>
<BrowserRouter basename="/admin">
<MyApp />
</BrowserRouter>
</App>
......
export const ADMIN_BASE_PATH = '/admin'
export const ADMIN_LOGIN_PATH = `${ADMIN_BASE_PATH}/login`
export function normalizeAdminApiPath(url = '') {
if (url === '/api/admin') return '/'
if (url.startsWith('/api/admin/')) return url.slice('/api/admin'.length)
if (url === '/api') return '/'
if (url.startsWith('/api/')) return url.slice('/api'.length)
return url
}
export function stripAdminGatewayPrefix(url = '') {
return url.replace('/api/admin', '')
}
import test from 'node:test'
import assert from 'node:assert/strict'
import {
ADMIN_LOGIN_PATH,
normalizeAdminApiPath,
stripAdminGatewayPrefix,
} from './adminPaths.js'
test('admin login stays under the admin base path', () => {
assert.equal(ADMIN_LOGIN_PATH, '/admin/login')
})
test('legacy /api requests are normalized for the admin gateway', () => {
assert.equal(normalizeAdminApiPath('/api/admin/user/login'), '/user/login')
assert.equal(normalizeAdminApiPath('/api/user/login'), '/user/login')
assert.equal(normalizeAdminApiPath('/user/login'), '/user/login')
})
test('gateway prefix is removed before backend signature calculation', () => {
assert.equal(
stripAdminGatewayPrefix('https://zijingebook.ezijing.com/api/admin/user/login'),
'https://zijingebook.ezijing.com/user/login'
)
})
......@@ -2,6 +2,7 @@ import axios from 'axios'
import md5 from 'js-md5'
import qs from 'qs'
import { Modal, notification } from 'antd'
import { ADMIN_LOGIN_PATH, normalizeAdminApiPath } from '@/utils/adminPaths.js'
// import { sm4 as Sm4js } from 'evit-gm-crypt'
// const sm4 = new Sm4js({
......@@ -20,7 +21,7 @@ function showLoginModal() {
title: '登录状态已过期,请重新登录',
onOk() {
localStorage.removeItem('kiwi.gpt.token')
location.href = '/login'
location.href = ADMIN_LOGIN_PATH
},
})
}
......@@ -31,14 +32,15 @@ function alphabeticalSort(a, b) {
}
const httpRequest = axios.create({
// baseURL: import.meta.env.VITE_API_PREFIX,
// timeout: 60000,
baseURL: import.meta.env.VITE_API_PREFIX,
withCredentials: true,
})
// 请求拦截器
httpRequest.interceptors.request.use(
(config) => {
config.url = normalizeAdminApiPath(config.url)
const token = getToken()
// 应用设置
const appId = 'TzEU5jPk2tu80266'
......@@ -85,7 +87,7 @@ httpRequest.interceptors.response.use(
(response) => {
const { data } = response
if (data.code === 401 || data.code === 403) {
if (location.pathname !== '/login') {
if (location.pathname !== ADMIN_LOGIN_PATH) {
showLoginModal()
}
return Promise.reject(data)
......
......@@ -3,8 +3,9 @@ import Axios from 'axios'
import { message, Modal, notification } from 'antd'
import md5 from 'js-md5'
import { get } from 'lodash-es'
import { sortObjASCII } from '@/utils/common.js'
import qs from 'qs'
import { sortObjASCII } from '@/utils/common.js'
import { ADMIN_LOGIN_PATH, normalizeAdminApiPath, stripAdminGatewayPrefix } from '@/utils/adminPaths.js'
import qs from 'qs'
// import { sm4 as Sm4js } from 'evit-gm-crypt'
// const sm4 = new Sm4js({
......@@ -37,7 +38,9 @@ function getToken() {
}
// 请求拦截器
axios.interceptors.request.use(
(config) => {
(config) => {
config.url = normalizeAdminApiPath(config.url)
// 登录流程控制中,根据本地是否存在token判断用户的登录情况
// 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
// 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
......@@ -68,8 +71,7 @@ axios.interceptors.request.use(
if (config.url.indexOf('.tiangong.cn') > -1) {
s = config.url
} else {
s = import.meta.env.VITE_API_URL + config.url
s = s.replace('/api', '')
s = stripAdminGatewayPrefix(import.meta.env.VITE_API_URL + config.url)
}
signData.url = decodeURIComponent(s)
// signData.url = 'http: //zijing.com/index.php/user/user/getList'
......@@ -130,7 +132,7 @@ axios.interceptors.response.use(
if (code === 401 || code === 403) {
if (!axios.errorNotified) {
axios.errorNotified = true // 添加标志,表示通知已经弹出
if (pathname === '/login') {
if (pathname === ADMIN_LOGIN_PATH) {
return
}
Modal.warning({
......@@ -138,7 +140,7 @@ axios.interceptors.response.use(
// content: '请重新登录',
onOk() {
localStorage.removeItem('kiwi.gpt.token')
window.location.pathname = '/login'
window.location.pathname = ADMIN_LOGIN_PATH
},
})
cancelPending(response.config) // 取消后续请求
......@@ -163,7 +165,7 @@ axios.interceptors.response.use(
content: '请重新登录',
onOk() {
localStorage.removeItem('kiwi.gpt.token')
window.location.href = '/login'
window.location.href = ADMIN_LOGIN_PATH
},
})
}
......@@ -178,7 +180,7 @@ axios.interceptors.response.use(
content: '请重新登录',
onOk() {
localStorage.removeItem('kiwi.gpt.token')
window.location.href = '/login'
window.location.href = ADMIN_LOGIN_PATH
},
})
}
......
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import mkcert from 'vite-plugin-mkcert'
// https://vitejs.dev/config/
export default defineConfig(() => {
return {
plugins: [react(), mkcert()],
server: {
open: true,
host: 'dev.ezijing.com',
proxy: {
// '/api/tiangong': {
// target: 'https://api.singularity-ai.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/tiangong/, ''),
// },
// '/api/deepseek': {
// target: 'https://api.deepseek.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/deepseek/, ''),
// },
// '/api/qwen': {
// target: 'https://dashscope.aliyuncs.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/qwen/, ''),
// },
// '/api/qianfan': {
// target: 'https://aip.baidubce.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/qianfan/, ''),
// },
// '/api/wenku': {
// target: 'https://wenchain.baidu.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/wenku/, '/'),
// },
// '/api/qianfan': {
// target: 'https://qianfan.baidubce.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/qianfan/, ''),
// },
// '/api/volcano': {
// target: 'https://ark.cn-beijing.volces.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/volcano/, ''),
// },
// '/api/xiling': {
// target: 'https://open.xiling.baidu.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/xiling/, ''),
// },
// '/api/volcano_file': {
// target: 'https://ark-content-generation-cn-beijing.tos-cn-beijing.volces.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/volcano_file/, ''),
// },
'/api': {
// target: 'http://localhost:7419',
target: 'https://zijingebook.ezijing.com',
changeOrigin: true,
secure: false,
},
},
},
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
const adminApiTarget = env.VITE_ADMIN_API_TARGET || 'http://127.0.0.1:7419'
return {
base: '/admin/',
plugins: [react(), mkcert()],
server: {
open: '/admin/',
host: 'dev.ezijing.com',
port: 5174,
strictPort: true,
proxy: {
'/api/admin': {
target: adminApiTarget,
changeOrigin: true,
secure: false,
rewrite: path => path.replace(/^\/api\/admin/, ''),
},
},
},
build: {
outDir: '../../dist/admin',
emptyOutDir: true,
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
......@@ -72,9 +37,7 @@ export default defineConfig(() => {
},
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
less: { javascriptEnabled: true },
},
},
}
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论