Some checks failed
CI / build (18.x) (push) Failing after 40m25s
CI / build (20.x) (push) Failing after 36m6s
CI / deploy-preview (push) Has been skipped
CI / lint (push) Failing after 53m38s
CI / test (push) Successful in 1h9m7s
CI / security (push) Failing after 16m54s
262 lines
11 KiB
JavaScript
262 lines
11 KiB
JavaScript
const { defineConfig } = require('vite')
|
||
const vue = require('@vitejs/plugin-vue').default
|
||
const path = require('path')
|
||
const { spawn } = require('child_process')
|
||
|
||
// https://vitejs.dev/config/
|
||
module.exports = defineConfig({
|
||
css: {
|
||
preprocessorOptions: {
|
||
scss: {
|
||
silenceDeprecations: ['legacy-js-api'],
|
||
api: 'modern-compiler'
|
||
}
|
||
}
|
||
},
|
||
plugins: [
|
||
vue(),
|
||
// 孔夫子旧书网登录代理(避免浏览器跨域限制)
|
||
{
|
||
name: 'kongfz-login-proxy',
|
||
configureServer(server) {
|
||
server.middlewares.use('/kongfz-login', async (req, res) => {
|
||
if (req.method !== 'POST') {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
res.end(JSON.stringify({ code: 405, message: '仅支持 POST 请求' }))
|
||
return
|
||
}
|
||
|
||
let body = ''
|
||
req.on('data', chunk => { body += chunk.toString() })
|
||
req.on('end', async () => {
|
||
try {
|
||
const params = new URLSearchParams(body)
|
||
const username = params.get('loginName') || ''
|
||
const password = params.get('loginPass') || ''
|
||
|
||
if (!username || !password) {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
res.end(JSON.stringify({ code: 400, message: '请输入用户名和密码!' }))
|
||
return
|
||
}
|
||
|
||
const https = require('https')
|
||
const http = require('http')
|
||
|
||
function requestWithRedirect(options, bodyData, redirectCount = 0) {
|
||
return new Promise((resolve, reject) => {
|
||
if (redirectCount > 5) {
|
||
reject(new Error('重定向次数过多'))
|
||
return
|
||
}
|
||
const mod = options.port === 443 ? https : http
|
||
const req = mod.request(options, (response) => {
|
||
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
||
const redirectUrl = new URL(response.headers.location, `https://${options.hostname}`)
|
||
const redirectOpts = {
|
||
hostname: redirectUrl.hostname,
|
||
port: redirectUrl.port || (redirectUrl.protocol === 'https:' ? 443 : 80),
|
||
path: redirectUrl.pathname + redirectUrl.search,
|
||
method: 'GET',
|
||
headers: {
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
|
||
},
|
||
timeout: 15000
|
||
}
|
||
if (response.headers['set-cookie']) {
|
||
const cookies = Array.isArray(response.headers['set-cookie'])
|
||
? response.headers['set-cookie'].join('; ')
|
||
: response.headers['set-cookie']
|
||
redirectOpts.headers['Cookie'] = cookies.split(';')[0].trim()
|
||
}
|
||
resolve(requestWithRedirect(redirectOpts, null, redirectCount + 1))
|
||
return
|
||
}
|
||
let responseData = ''
|
||
response.on('data', chunk => { responseData += chunk })
|
||
response.on('end', () => resolve({ statusCode: response.statusCode, headers: response.headers, body: responseData }))
|
||
})
|
||
req.on('error', err => reject(new Error(`登录请求失败: ${err.message}`)))
|
||
req.on('timeout', () => { req.destroy(); reject(new Error('登录请求超时')) })
|
||
if (bodyData) req.write(bodyData)
|
||
req.end()
|
||
})
|
||
}
|
||
|
||
const formData = new URLSearchParams()
|
||
formData.append('loginName', username)
|
||
formData.append('loginPass', password)
|
||
formData.append('returnUrl', 'http://user.kongfz.com/')
|
||
const formDataStr = formData.toString()
|
||
|
||
const loginResponse = await requestWithRedirect({
|
||
hostname: 'login.kongfz.com',
|
||
port: 443,
|
||
path: '/Pc/Login/account',
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded',
|
||
'Content-Length': Buffer.byteLength(formDataStr),
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
|
||
},
|
||
timeout: 15000
|
||
}, formDataStr)
|
||
|
||
if (loginResponse.statusCode !== 200) {
|
||
throw new Error(`登录失败(HTTP状态码: ${loginResponse.statusCode})`)
|
||
}
|
||
|
||
const setCookie = loginResponse.headers['set-cookie'] || []
|
||
const cookieStr = Array.isArray(setCookie) ? setCookie.join('; ') : setCookie || ''
|
||
|
||
if (loginResponse.body.includes("window.location.href='https://login.kongfz.cn/Pc/Session/rsync")) {
|
||
if (!cookieStr) throw new Error('登录成功但未获取到Cookie')
|
||
if (!cookieStr.includes('PHPSESSID=')) throw new Error('登录失败: 未找到 PHPSESSID')
|
||
|
||
const token = cookieStr.split('PHPSESSID=')[1].split(';')[0]
|
||
|
||
const userInfoResponse = await requestWithRedirect({
|
||
hostname: 'user.kongfz.com',
|
||
port: 443,
|
||
path: '/User/Index/getUserInfo/',
|
||
method: 'GET',
|
||
headers: {
|
||
'Cookie': `PHPSESSID=${token}`,
|
||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||
'Accept': 'application/json, text/plain, */*',
|
||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
|
||
},
|
||
timeout: 15000
|
||
}, null)
|
||
|
||
let displayName = username
|
||
try {
|
||
const userJson = JSON.parse(userInfoResponse.body)
|
||
if (userJson.status && userJson.data) displayName = userJson.data.nickname || username
|
||
} catch { }
|
||
|
||
res.setHeader('Content-Type', 'application/json')
|
||
res.end(JSON.stringify({
|
||
code: 200, message: '登录成功',
|
||
data: { token, username: displayName, nickname: displayName }
|
||
}))
|
||
} else {
|
||
try {
|
||
const errJson = JSON.parse(loginResponse.body)
|
||
if (errJson.errCode === 1001 || errJson.errCode === 1005) throw new Error('账号或密码错误')
|
||
throw new Error(errJson.errInfo || '登录失败,未知错误!')
|
||
} catch (parseErr) {
|
||
if (parseErr instanceof SyntaxError) throw new Error('登录失败,未知错误!')
|
||
throw parseErr
|
||
}
|
||
}
|
||
} catch (error) {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
res.end(JSON.stringify({ code: 500, message: error.message }))
|
||
}
|
||
})
|
||
})
|
||
}
|
||
},
|
||
// 本地程序启动器(无需协议注册/脚本/额外服务,Vite 服务器直接 spawn exe)
|
||
{
|
||
name: 'local-launcher',
|
||
configureServer(server) {
|
||
server.middlewares.use('/api/local/launch-exe', (req, res) => {
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
||
|
||
if (req.method === 'OPTIONS') {
|
||
res.statusCode = 204
|
||
return res.end()
|
||
}
|
||
|
||
if (req.method !== 'POST') {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
return res.end(JSON.stringify({ code: 405, msg: '仅支持 POST' }))
|
||
}
|
||
|
||
let body = ''
|
||
req.on('data', chunk => { body += chunk.toString() })
|
||
req.on('end', () => {
|
||
try {
|
||
const { exe_path } = JSON.parse(body)
|
||
if (!exe_path) {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
return res.end(JSON.stringify({ code: 400, msg: '缺少 exe_path' }))
|
||
}
|
||
|
||
const fs = require('fs')
|
||
if (!fs.existsSync(exe_path)) {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
return res.end(JSON.stringify({ code: 404, msg: `文件不存在: ${exe_path}` }))
|
||
}
|
||
|
||
const exeDir = path.dirname(exe_path)
|
||
const proc = spawn(exe_path, [], {
|
||
cwd: exeDir,
|
||
detached: true,
|
||
stdio: 'ignore',
|
||
windowsHide: false
|
||
})
|
||
proc.unref()
|
||
|
||
res.setHeader('Content-Type', 'application/json')
|
||
res.end(JSON.stringify({ code: 0, msg: '已启动', pid: proc.pid }))
|
||
} catch (err) {
|
||
res.setHeader('Content-Type', 'application/json')
|
||
res.end(JSON.stringify({ code: 500, msg: err.message }))
|
||
}
|
||
})
|
||
})
|
||
|
||
server.middlewares.use('/api/local/exe-config', (req, res) => {
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
res.setHeader('Content-Type', 'application/json')
|
||
if (req.method !== 'GET') return res.end(JSON.stringify({ code: 405 }))
|
||
|
||
try {
|
||
const { execSync } = require('child_process')
|
||
const cmd = 'powershell -NoProfile -Command "(Get-ItemProperty -Path \'Registry::HKEY_CLASSES_ROOT\\kfzprice\\shell\\open\\command\' -Name \'(default)\').\'(default)\'"'
|
||
const raw = execSync(cmd, { encoding: 'utf8', timeout: 5000 }).trim()
|
||
const match = raw.match(/"([^"]+\.exe)"/i)
|
||
res.end(JSON.stringify({ code: 0, data: { exe_path: match ? match[1] : '' } }))
|
||
} catch (err) {
|
||
res.end(JSON.stringify({ code: 500, msg: err.message }))
|
||
}
|
||
})
|
||
}
|
||
}
|
||
],
|
||
resolve: {
|
||
alias: {
|
||
'@': path.resolve(__dirname, './src')
|
||
}
|
||
},
|
||
define: {
|
||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
|
||
},
|
||
server: {
|
||
port: 5174,
|
||
host: '0.0.0.0',
|
||
historyApiFallback: true,
|
||
proxy: {
|
||
'/api': {
|
||
// target: 'http://127.0.0.1:9090',
|
||
// target: 'http://192.168.101.213:9090',
|
||
target: 'https://psi.api.buzhiyushu.cn',
|
||
// target: 'http://127.0.0.1:9090',
|
||
changeOrigin: true
|
||
},
|
||
'/api/print': {
|
||
// target: 'http://192.168.101.127:8075',
|
||
target: 'https://print.buzhiyushu.cn',
|
||
changeOrigin: true
|
||
}
|
||
}
|
||
}
|
||
})
|