feat(config): 重构镜像源配置管理
This commit is contained in:
@@ -203,7 +203,7 @@ export async function downloadPython(appRoot: string, mirror = 'ustc'): Promise<
|
||||
// Python 3.12.0嵌入式版本应该大约30MB,如果小于5MB可能是无效文件
|
||||
if (stats.size < 5 * 1024 * 1024) { // 5MB
|
||||
fs.unlinkSync(zipPath) // 删除无效文件
|
||||
throw new Error(`Python下载文件大小异常: ${stats.size} bytes (${(stats.size / 1024).toFixed(2)} KB),可能是镜像站返回的错误页面或无效文件`)
|
||||
throw new Error(`Python下载文件大小异常: ${stats.size} bytes (${(stats.size / 1024).toFixed(2)} KB),可能是镜像站返回的错误页面或无效文件。请选择一个其他可用镜像源进行下载!`)
|
||||
}
|
||||
|
||||
if (mainWindow) {
|
||||
|
||||
100
frontend/src/api/mirrors.ts
Normal file
100
frontend/src/api/mirrors.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 镜像源 API 接口
|
||||
* 用于从后端获取最新的镜像源配置
|
||||
*/
|
||||
|
||||
import { OpenAPI } from '@/api'
|
||||
import type { MirrorConfig } from '@/config/mirrors'
|
||||
|
||||
export interface MirrorApiResponse {
|
||||
git?: MirrorConfig[]
|
||||
python?: MirrorConfig[]
|
||||
pip?: MirrorConfig[]
|
||||
apiEndpoints?: {
|
||||
local?: string
|
||||
production?: string
|
||||
proxy?: string
|
||||
}
|
||||
downloadLinks?: {
|
||||
[category: string]: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取镜像源配置
|
||||
*/
|
||||
export async function fetchMirrorConfig(): Promise<MirrorApiResponse> {
|
||||
try {
|
||||
const response = await fetch(`${OpenAPI.BASE}/api/mirrors`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
return await response.json()
|
||||
} catch (error) {
|
||||
console.warn('获取镜像源配置失败,使用默认配置:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试镜像源连通性
|
||||
*/
|
||||
export async function testMirrorConnectivity(url: string, timeout: number = 5000): Promise<{
|
||||
success: boolean
|
||||
speed: number
|
||||
error?: string
|
||||
}> {
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
signal: controller.signal,
|
||||
cache: 'no-cache',
|
||||
mode: 'no-cors' // 避免 CORS 问题
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
const speed = Date.now() - startTime
|
||||
|
||||
return {
|
||||
success: true,
|
||||
speed
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
speed: 9999,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量测试镜像源
|
||||
*/
|
||||
export async function batchTestMirrors(mirrors: MirrorConfig[]): Promise<MirrorConfig[]> {
|
||||
const promises = mirrors.map(async (mirror) => {
|
||||
const result = await testMirrorConnectivity(mirror.url)
|
||||
return {
|
||||
...mirror,
|
||||
speed: result.speed
|
||||
}
|
||||
})
|
||||
|
||||
const results = await Promise.all(promises)
|
||||
|
||||
// 按速度排序
|
||||
return results.sort((a, b) => (a.speed || 9999) - (b.speed || 9999))
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { createComponentLogger } from '@/utils/logger'
|
||||
import { getConfig } from '@/utils/config'
|
||||
import { getMirrorUrl } from '@/config/mirrors'
|
||||
import router from '@/router'
|
||||
|
||||
const logger = createComponentLogger('AutoMode')
|
||||
@@ -58,6 +59,9 @@ const progress = ref(0)
|
||||
const progressText = ref('')
|
||||
const progressStatus = ref<'normal' | 'exception' | 'success'>('normal')
|
||||
|
||||
// 状态:控制是否取消自动启动
|
||||
const aborted = ref(false)
|
||||
|
||||
// 状态:控制弹窗显隐
|
||||
const forceEnterVisible = ref(false)
|
||||
|
||||
@@ -74,6 +78,7 @@ function handleForceEnterConfirm() {
|
||||
|
||||
// 事件处理
|
||||
function handleSwitchToManual() {
|
||||
aborted.value = true // 设置中断
|
||||
props.onSwitchToManual()
|
||||
}
|
||||
|
||||
@@ -82,12 +87,14 @@ async function startAutoProcess() {
|
||||
try {
|
||||
// 获取配置中保存的镜像源设置
|
||||
const config = await getConfig()
|
||||
if (aborted.value) return
|
||||
|
||||
progressText.value = '检查Git仓库更新...'
|
||||
progress.value = 20
|
||||
|
||||
// 检查Git仓库是否有更新
|
||||
const hasUpdate = await checkGitUpdate()
|
||||
if (aborted.value) return
|
||||
|
||||
if (hasUpdate) {
|
||||
progressText.value = '发现更新,正在更新代码...'
|
||||
@@ -96,6 +103,7 @@ async function startAutoProcess() {
|
||||
// 使用配置中保存的Git镜像源
|
||||
const gitMirrorUrl = getGitMirrorUrl(config.selectedGitMirror)
|
||||
const result = await window.electronAPI.updateBackend(gitMirrorUrl)
|
||||
if (aborted.value) return
|
||||
if (!result.success) {
|
||||
throw new Error(`代码更新失败: ${result.error}`)
|
||||
}
|
||||
@@ -108,6 +116,7 @@ async function startAutoProcess() {
|
||||
// 先尝试使用初始化时的镜像源
|
||||
let pipMirror = config.selectedPipMirror || 'tsinghua'
|
||||
let pipResult = await window.electronAPI.installDependencies(pipMirror)
|
||||
if (aborted.value) return
|
||||
|
||||
// 如果初始化时的镜像源不通,让用户重新选择
|
||||
if (!pipResult.success) {
|
||||
@@ -127,6 +136,7 @@ async function startAutoProcess() {
|
||||
progressText.value = '启动后端服务...'
|
||||
progress.value = 80
|
||||
await startBackendService()
|
||||
if (aborted.value) return
|
||||
|
||||
progressText.value = '启动完成!'
|
||||
progress.value = 100
|
||||
@@ -167,16 +177,7 @@ async function checkGitUpdate(): Promise<boolean> {
|
||||
|
||||
// 根据镜像源key获取对应的URL
|
||||
function getGitMirrorUrl(mirrorKey: string): string {
|
||||
const mirrors = {
|
||||
github: 'https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
ghfast: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
ghproxy_cloudflare: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git',
|
||||
ghproxy_hongkong: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git',
|
||||
ghproxy_fastly: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git',
|
||||
ghproxy_edgeone: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git'
|
||||
}
|
||||
|
||||
return mirrors[mirrorKey as keyof typeof mirrors] || mirrors.github
|
||||
return getMirrorUrl('git', mirrorKey)
|
||||
}
|
||||
|
||||
// 启动后端服务
|
||||
@@ -189,6 +190,7 @@ async function startBackendService() {
|
||||
|
||||
// 组件挂载时开始自动流程
|
||||
onMounted(() => {
|
||||
aborted.value = false
|
||||
startAutoProcess()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -50,14 +50,9 @@ defineProps<{
|
||||
backendExists: boolean
|
||||
}>()
|
||||
|
||||
const gitMirrors = ref<Mirror[]>([
|
||||
{ key: 'github', name: 'GitHub 官方', url: 'https://github.com/DLmaster361/AUTO_MAA.git', speed: null },
|
||||
{ key: 'ghfast', name: 'ghfast 镜像', url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git', speed: null },
|
||||
{ key: 'ghproxy_cloudflare', name: 'gh-proxy Cloudflare 全球加速', url: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null },
|
||||
{ key: 'ghproxy_hongkong', name: 'gh-proxy 香港节点(大陆优化)', url: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null },
|
||||
{ key: 'ghproxy_fastly', name: 'gh-proxy Fastly CDN', url: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null },
|
||||
{ key: 'ghproxy_edgeone', name: 'gh-proxy EdgeOne 全球加速', url: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git.git', speed: null }
|
||||
])
|
||||
import { GIT_MIRRORS } from '@/config/mirrors'
|
||||
|
||||
const gitMirrors = ref<Mirror[]>(GIT_MIRRORS)
|
||||
|
||||
|
||||
const selectedGitMirror = ref('github')
|
||||
|
||||
@@ -46,12 +46,9 @@ interface Mirror {
|
||||
speed: number | null
|
||||
}
|
||||
|
||||
const pipMirrors = ref<Mirror[]>([
|
||||
{ key: 'official', name: 'PyPI 官方', url: 'https://pypi.org/simple/', speed: null },
|
||||
{ key: 'tsinghua', name: '清华大学', url: 'https://pypi.tuna.tsinghua.edu.cn/simple/', speed: null },
|
||||
{ key: 'aliyun', name: '阿里云', url: 'https://mirrors.aliyun.com/pypi/simple/', speed: null },
|
||||
{ key: 'ustc', name: '中科大', url: 'https://pypi.mirrors.ustc.edu.cn/simple/', speed: null },
|
||||
])
|
||||
import { PIP_MIRRORS } from '@/config/mirrors'
|
||||
|
||||
const pipMirrors = ref<Mirror[]>(PIP_MIRRORS)
|
||||
|
||||
const selectedPipMirror = ref('tsinghua')
|
||||
const testingPipSpeed = ref(false)
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
</div>
|
||||
<div v-else class="already-installed">
|
||||
<a-result status="success" title="Git已成功安装,无需继续安装" />
|
||||
<div class="reinstall-section">
|
||||
<a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">
|
||||
{{ reinstalling ? '正在重新安装...' : '强制重新安装' }}
|
||||
</a-button>
|
||||
<p class="reinstall-note">点击此按钮将删除现有Git环境并重新安装</p>
|
||||
</div>
|
||||
<!-- <div class="reinstall-section">-->
|
||||
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
|
||||
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
|
||||
<!-- </a-button>-->
|
||||
<!-- <p class="reinstall-note">点击此按钮将删除现有Git环境并重新安装</p>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -28,15 +28,15 @@
|
||||
<a-step title="启动服务" description="启动后端服务" />
|
||||
</a-steps>
|
||||
|
||||
<!-- 全局进度条 -->
|
||||
<div v-if="isProcessing" class="global-progress">
|
||||
<a-progress
|
||||
:percent="globalProgress"
|
||||
:status="globalProgressStatus"
|
||||
:show-info="true"
|
||||
/>
|
||||
<div class="progress-text">{{ progressText }}</div>
|
||||
</div>
|
||||
<!-- <!– 全局进度条 –>-->
|
||||
<!-- <div v-if="isProcessing" class="global-progress">-->
|
||||
<!-- <a-progress -->
|
||||
<!-- :percent="globalProgress" -->
|
||||
<!-- :status="globalProgressStatus"-->
|
||||
<!-- :show-info="true"-->
|
||||
<!-- />-->
|
||||
<!-- <div class="progress-text">{{ progressText }}</div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="step-content">
|
||||
<!-- 步骤 0: 主题设置 -->
|
||||
@@ -94,20 +94,21 @@
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage" class="error-message">
|
||||
<a-alert
|
||||
:message="errorMessage"
|
||||
type="error"
|
||||
show-icon
|
||||
closable
|
||||
@close="errorMessage = ''"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div v-if="errorMessage" class="error-message">-->
|
||||
<!-- <a-alert -->
|
||||
<!-- :message="errorMessage" -->
|
||||
<!-- type="error" -->
|
||||
<!-- show-icon -->
|
||||
<!-- closable-->
|
||||
<!-- @close="errorMessage = ''"-->
|
||||
<!-- />-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { createComponentLogger } from '@/utils/logger'
|
||||
import { saveConfig } from '@/utils/config'
|
||||
import ThemeStep from './ThemeStep.vue'
|
||||
@@ -473,6 +474,15 @@ defineExpose({
|
||||
currentStep,
|
||||
handleDownloadProgress
|
||||
})
|
||||
|
||||
// 监听 errorMessage,一旦有内容就弹窗
|
||||
watch(errorMessage, (val) => {
|
||||
if (val) {
|
||||
message.error(val)
|
||||
// 弹窗后可选:自动清空 errorMessage
|
||||
// errorMessage.value = ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
</div>
|
||||
<div v-else class="already-installed">
|
||||
<a-result status="success" title="pip已成功安装,无需继续安装" />
|
||||
<div class="reinstall-section">
|
||||
<a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">
|
||||
{{ reinstalling ? '正在重新安装...' : '强制重新安装' }}
|
||||
</a-button>
|
||||
<p class="reinstall-note">点击此按钮将删除现有pip环境并重新安装</p>
|
||||
</div>
|
||||
<!-- <div class="reinstall-section">-->
|
||||
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
|
||||
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
|
||||
<!-- </a-button>-->
|
||||
<!-- <p class="reinstall-note">点击此按钮将删除现有pip环境并重新安装</p>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
</div>
|
||||
<div v-else class="already-installed">
|
||||
<a-result status="success" title="Python已成功安装,无需继续安装" />
|
||||
<div class="reinstall-section">
|
||||
<a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">
|
||||
{{ reinstalling ? '正在重新安装...' : '强制重新安装' }}
|
||||
</a-button>
|
||||
<p class="reinstall-note">点击此按钮将删除现有Python环境并重新安装</p>
|
||||
</div>
|
||||
<!-- <div class="reinstall-section">-->
|
||||
<!-- <a-button type="primary" danger @click="handleForceReinstall" :loading="reinstalling">-->
|
||||
<!-- {{ reinstalling ? '正在重新安装...' : '强制重新安装' }}-->
|
||||
<!-- </a-button>-->
|
||||
<!-- <p class="reinstall-note">点击此按钮将删除现有Python环境并重新安装</p>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -54,13 +54,9 @@ const props = defineProps<{
|
||||
pythonInstalled: boolean
|
||||
}>()
|
||||
|
||||
const pythonMirrors = ref<Mirror[]>([
|
||||
{ key: 'official', name: 'Python 官方', url: 'https://www.python.org/ftp/python/3.13.0/', speed: null },
|
||||
{ key: 'tsinghua', name: '清华 TUNA 镜像', url: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.13.0/', speed: null },
|
||||
{ key: 'ustc', name: '中科大镜像', url: 'https://mirrors.ustc.edu.cn/python/3.13.0/', speed: null },
|
||||
{ key: 'huawei', name: '华为云镜像', url: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.13.0/', speed: null },
|
||||
{ key: 'aliyun', name: '阿里云镜像', url: 'https://mirrors.aliyun.com/python-release/windows/', speed: null }
|
||||
])
|
||||
import { PYTHON_MIRRORS } from '@/config/mirrors'
|
||||
|
||||
const pythonMirrors = ref<Mirror[]>(PYTHON_MIRRORS)
|
||||
|
||||
const selectedPythonMirror = ref('tsinghua')
|
||||
const testingSpeed = ref(false)
|
||||
|
||||
216
frontend/src/config/mirrors.ts
Normal file
216
frontend/src/config/mirrors.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 镜像源和下载链接配置文件
|
||||
* 集中管理所有下载用到的链接,方便后续通过接口动态配置
|
||||
*/
|
||||
|
||||
export interface MirrorConfig {
|
||||
key: string
|
||||
name: string
|
||||
url: string
|
||||
speed?: number | null
|
||||
}
|
||||
|
||||
export interface MirrorCategory {
|
||||
[key: string]: MirrorConfig[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Git 仓库镜像源配置
|
||||
*/
|
||||
export const GIT_MIRRORS: MirrorConfig[] = [
|
||||
{
|
||||
key: 'github',
|
||||
name: 'GitHub 官方',
|
||||
url: 'https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ghfast',
|
||||
name: 'ghfast 镜像',
|
||||
url: 'https://ghfast.top/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_cloudflare',
|
||||
name: 'gh-proxy (Cloudflare加速)',
|
||||
url: 'https://gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_hongkong',
|
||||
name: 'gh-proxy (香港节点加速)',
|
||||
url: 'https://hk.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_fastly',
|
||||
name: 'gh-proxy (Fastly CDN加速)',
|
||||
url: 'https://cdn.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ghproxy_edgeone',
|
||||
name: 'gh-proxy (EdgeOne加速)',
|
||||
url: 'https://edgeone.gh-proxy.com/https://github.com/DLmaster361/AUTO_MAA.git',
|
||||
speed: null
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* Python 下载镜像源配置(3.12.0 embed版本)
|
||||
*/
|
||||
export const PYTHON_MIRRORS: MirrorConfig[] = [
|
||||
{
|
||||
key: 'official',
|
||||
name: 'Python 官方',
|
||||
url: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'tsinghua',
|
||||
name: '清华 TUNA 镜像',
|
||||
url: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ustc',
|
||||
name: '中科大镜像',
|
||||
url: 'https://mirrors.ustc.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'huawei',
|
||||
name: '华为云镜像',
|
||||
url: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'aliyun',
|
||||
name: '阿里云镜像',
|
||||
url: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip',
|
||||
speed: null
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* PyPI pip 镜像源配置
|
||||
*/
|
||||
export const PIP_MIRRORS: MirrorConfig[] = [
|
||||
{
|
||||
key: 'official',
|
||||
name: 'PyPI 官方',
|
||||
url: 'https://pypi.org/simple/',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'tsinghua',
|
||||
name: '清华大学',
|
||||
url: 'https://pypi.tuna.tsinghua.edu.cn/simple/',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'ustc',
|
||||
name: '中科大',
|
||||
url: 'https://pypi.mirrors.ustc.edu.cn/simple/',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'aliyun',
|
||||
name: '阿里云',
|
||||
url: 'https://mirrors.aliyun.com/pypi/simple/',
|
||||
speed: null
|
||||
},
|
||||
{
|
||||
key: 'douban',
|
||||
name: '豆瓣',
|
||||
url: 'https://pypi.douban.com/simple/',
|
||||
speed: null
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* API 服务端点配置
|
||||
*/
|
||||
export const API_ENDPOINTS = {
|
||||
// 本地开发服务器
|
||||
local: 'http://localhost:8000',
|
||||
// WebSocket连接基础URL
|
||||
websocket: 'ws://localhost:8000',
|
||||
// 代理服务器示例
|
||||
proxy: 'http://127.0.0.1:7890'
|
||||
}
|
||||
|
||||
/**
|
||||
* 自建下载站链接配置
|
||||
*/
|
||||
export const DOWNLOAD_LINKS = {
|
||||
// get-pip.py 下载链接
|
||||
getPip: 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py',
|
||||
|
||||
// Git 客户端下载链接
|
||||
git: 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip'
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有镜像源配置的集合
|
||||
*/
|
||||
export const ALL_MIRRORS: MirrorCategory = {
|
||||
git: GIT_MIRRORS,
|
||||
python: PYTHON_MIRRORS,
|
||||
pip: PIP_MIRRORS
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型获取镜像源配置
|
||||
*/
|
||||
export function getMirrorsByType(type: keyof MirrorCategory): MirrorConfig[] {
|
||||
return ALL_MIRRORS[type] || []
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型和key获取特定镜像源URL
|
||||
*/
|
||||
export function getMirrorUrl(type: keyof MirrorCategory, key: string): string {
|
||||
const mirrors = getMirrorsByType(type)
|
||||
const mirror = mirrors.find(m => m.key === key)
|
||||
return mirror?.url || mirrors[0]?.url || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认镜像源(通常是第一个)
|
||||
*/
|
||||
export function getDefaultMirror(type: keyof MirrorCategory): MirrorConfig | null {
|
||||
const mirrors = getMirrorsByType(type)
|
||||
return mirrors.length > 0 ? mirrors[0] : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新镜像源速度测试结果
|
||||
*/
|
||||
export function updateMirrorSpeed(type: keyof MirrorCategory, key: string, speed: number): void {
|
||||
const mirrors = getMirrorsByType(type)
|
||||
const mirror = mirrors.find(m => m.key === key)
|
||||
if (mirror) {
|
||||
mirror.speed = speed
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据速度排序镜像源
|
||||
*/
|
||||
export function sortMirrorsBySpeed(mirrors: MirrorConfig[]): MirrorConfig[] {
|
||||
return [...mirrors].sort((a, b) => {
|
||||
const speedA = a.speed === null ? 9999 : a.speed
|
||||
const speedB = b.speed === null ? 9999 : b.speed
|
||||
return speedA - speedB
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最快的镜像源
|
||||
*/
|
||||
export function getFastestMirror(type: keyof MirrorCategory): MirrorConfig | null {
|
||||
const mirrors = getMirrorsByType(type)
|
||||
const sortedMirrors = sortMirrorsBySpeed(mirrors)
|
||||
return sortedMirrors.find(m => m.speed !== null && m.speed !== 9999) || null
|
||||
}
|
||||
@@ -13,8 +13,10 @@ import 'dayjs/locale/zh-cn'
|
||||
// 配置dayjs中文本地化
|
||||
dayjs.locale('zh-cn')
|
||||
|
||||
import { API_ENDPOINTS } from '@/config/mirrors'
|
||||
|
||||
// 配置API基础URL
|
||||
OpenAPI.BASE = 'http://localhost:8000'
|
||||
OpenAPI.BASE = API_ENDPOINTS.local
|
||||
|
||||
// 创建应用实例
|
||||
const app = createApp(App)
|
||||
|
||||
187
frontend/src/utils/mirrorManager.ts
Normal file
187
frontend/src/utils/mirrorManager.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* 镜像源管理工具
|
||||
* 提供动态获取和更新镜像源配置的功能
|
||||
*/
|
||||
|
||||
import {
|
||||
ALL_MIRRORS,
|
||||
API_ENDPOINTS,
|
||||
DOWNLOAD_LINKS,
|
||||
type MirrorConfig,
|
||||
type MirrorCategory,
|
||||
getMirrorUrl,
|
||||
updateMirrorSpeed,
|
||||
sortMirrorsBySpeed,
|
||||
getFastestMirror
|
||||
} from '@/config/mirrors'
|
||||
|
||||
/**
|
||||
* 镜像源管理器类
|
||||
*/
|
||||
export class MirrorManager {
|
||||
private static instance: MirrorManager
|
||||
private mirrorConfigs: MirrorCategory = { ...ALL_MIRRORS }
|
||||
private apiEndpoints = { ...API_ENDPOINTS }
|
||||
private downloadLinks = { ...DOWNLOAD_LINKS }
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
static getInstance(): MirrorManager {
|
||||
if (!MirrorManager.instance) {
|
||||
MirrorManager.instance = new MirrorManager()
|
||||
}
|
||||
return MirrorManager.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的镜像源列表
|
||||
*/
|
||||
getMirrors(type: keyof MirrorCategory): MirrorConfig[] {
|
||||
return this.mirrorConfigs[type] || []
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取镜像源URL
|
||||
*/
|
||||
getMirrorUrl(type: keyof MirrorCategory, key: string): string {
|
||||
return getMirrorUrl(type, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新镜像源速度
|
||||
*/
|
||||
updateMirrorSpeed(type: keyof MirrorCategory, key: string, speed: number): void {
|
||||
updateMirrorSpeed(type, key, speed)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最快的镜像源
|
||||
*/
|
||||
getFastestMirror(type: keyof MirrorCategory): MirrorConfig | null {
|
||||
return getFastestMirror(type)
|
||||
}
|
||||
|
||||
/**
|
||||
* 按速度排序镜像源
|
||||
*/
|
||||
sortMirrorsBySpeed(mirrors: MirrorConfig[]): MirrorConfig[] {
|
||||
return sortMirrorsBySpeed(mirrors)
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试镜像源速度
|
||||
*/
|
||||
async testMirrorSpeed(url: string, timeout: number = 5000): Promise<number> {
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'HEAD',
|
||||
signal: controller.signal,
|
||||
cache: 'no-cache'
|
||||
})
|
||||
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (response.ok) {
|
||||
return Date.now() - startTime
|
||||
} else {
|
||||
return 9999 // 请求失败
|
||||
}
|
||||
} catch (error) {
|
||||
return 9999 // 超时或网络错误
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量测试镜像源速度
|
||||
*/
|
||||
async testAllMirrorSpeeds(type: keyof MirrorCategory): Promise<MirrorConfig[]> {
|
||||
const mirrors = this.getMirrors(type)
|
||||
const promises = mirrors.map(async (mirror) => {
|
||||
const speed = await this.testMirrorSpeed(mirror.url)
|
||||
this.updateMirrorSpeed(type, mirror.key, speed)
|
||||
return { ...mirror, speed }
|
||||
})
|
||||
|
||||
const results = await Promise.all(promises)
|
||||
return this.sortMirrorsBySpeed(results)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取API端点
|
||||
*/
|
||||
getApiEndpoint(key: keyof typeof API_ENDPOINTS): string {
|
||||
return this.apiEndpoints[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载链接
|
||||
*/
|
||||
getDownloadLink(key: keyof typeof DOWNLOAD_LINKS): string {
|
||||
return this.downloadLinks[key] || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态更新镜像源配置(用于从API获取最新配置)
|
||||
*/
|
||||
updateMirrorConfig(type: keyof MirrorCategory, mirrors: MirrorConfig[]): void {
|
||||
this.mirrorConfigs[type] = mirrors
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态更新API端点配置
|
||||
*/
|
||||
updateApiEndpoints(endpoints: Partial<typeof API_ENDPOINTS>): void {
|
||||
this.apiEndpoints = { ...this.apiEndpoints, ...endpoints }
|
||||
}
|
||||
|
||||
/**
|
||||
* 从远程API获取镜像源配置
|
||||
*/
|
||||
async fetchMirrorConfigFromApi(apiUrl: string): Promise<void> {
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/api/mirrors`)
|
||||
if (response.ok) {
|
||||
const config = await response.json()
|
||||
|
||||
// 更新各类镜像源配置
|
||||
if (config.git) this.updateMirrorConfig('git', config.git)
|
||||
if (config.python) this.updateMirrorConfig('python', config.python)
|
||||
if (config.pip) this.updateMirrorConfig('pip', config.pip)
|
||||
|
||||
// 更新API端点
|
||||
if (config.apiEndpoints) this.updateApiEndpoints(config.apiEndpoints)
|
||||
|
||||
console.log('镜像源配置已从API更新')
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('从API获取镜像源配置失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有镜像源配置(用于导出或备份)
|
||||
*/
|
||||
getAllConfigs() {
|
||||
return {
|
||||
mirrors: this.mirrorConfigs,
|
||||
apiEndpoints: this.apiEndpoints,
|
||||
downloadLinks: this.downloadLinks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例实例
|
||||
export const mirrorManager = MirrorManager.getInstance()
|
||||
|
||||
// 导出便捷函数
|
||||
export const getMirrors = (type: keyof MirrorCategory) => mirrorManager.getMirrors(type)
|
||||
export const getMirrorUrlByManager = (type: keyof MirrorCategory, key: string) => mirrorManager.getMirrorUrl(type, key)
|
||||
export const testMirrorSpeed = (url: string, timeout?: number) => mirrorManager.testMirrorSpeed(url, timeout)
|
||||
export const testAllMirrorSpeeds = (type: keyof MirrorCategory) => mirrorManager.testAllMirrorSpeeds(type)
|
||||
Reference in New Issue
Block a user