feat: 允许http下载&添加了多个镜像源

- 在 AutoMode 组件中添加了多个 gh-proxy 镜像选项
- 在 BackendStep 组件中增加了新的镜像选择项
- 修改了 downloadService 中的下载逻辑,支持 http 和 https
- 更新了 gitService 和 pythonService 中的下载 URL
- 在 Home 组件中添加了动态问候语
This commit is contained in:
2025-08-13 20:00:28 +08:00
parent 9aeda23ade
commit 95126a85d8
7 changed files with 182 additions and 155 deletions

View File

@@ -1,6 +1,7 @@
import * as https from 'https'
import * as fs from 'fs'
import { BrowserWindow } from 'electron'
import * as http from 'http'
let mainWindow: BrowserWindow | null = null
@@ -8,51 +9,55 @@ export function setMainWindow(window: BrowserWindow) {
mainWindow = window
}
// 下载文件的通用函数
export function downloadFile(url: string, outputPath: string): Promise<void> {
return new Promise((resolve, reject) => {
console.log(`开始下载文件: ${url}`)
console.log(`保存路径: ${outputPath}`)
const file = fs.createWriteStream(outputPath)
https.get(url, (response) => {
const totalSize = parseInt(response.headers['content-length'] || '0', 10)
let downloadedSize = 0
// 创建HTTP客户端兼容https和http
const client = url.startsWith('https') ? https : http
console.log(`文件大小: ${totalSize} bytes`)
response.pipe(file)
client
.get(url, response => {
const totalSize = parseInt(response.headers['content-length'] || '0', 10)
let downloadedSize = 0
response.on('data', (chunk) => {
downloadedSize += chunk.length
const progress = Math.round((downloadedSize / totalSize) * 100)
console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`)
if (mainWindow) {
mainWindow.webContents.send('download-progress', {
progress,
status: 'downloading',
message: `下载中... ${progress}%`
})
}
console.log(`文件大小: ${totalSize} bytes`)
response.pipe(file)
response.on('data', chunk => {
downloadedSize += chunk.length
const progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0
console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`)
if (mainWindow) {
mainWindow.webContents.send('download-progress', {
progress,
status: 'downloading',
message: `下载中... ${progress}%`,
})
}
})
file.on('finish', () => {
file.close()
console.log(`文件下载完成: ${outputPath}`)
resolve()
})
file.on('error', err => {
console.error(`文件写入错误: ${err.message}`)
fs.unlink(outputPath, () => {}) // 删除不完整的文件
reject(err)
})
})
file.on('finish', () => {
file.close()
console.log(`文件下载完成: ${outputPath}`)
resolve()
})
file.on('error', (err) => {
console.error(`文件写入错误: ${err.message}`)
fs.unlink(outputPath, () => {}) // 删除不完整的文件
.on('error', err => {
console.error(`下载错误: ${err.message}`)
reject(err)
})
}).on('error', (err) => {
console.error(`下载错误: ${err.message}`)
reject(err)
})
})
}
}

View File

@@ -11,7 +11,7 @@ export function setMainWindow(window: BrowserWindow) {
mainWindow = window
}
const gitDownloadUrl = 'https://alist-automaa.fearr.xyz/d/AUTO_MAA/git.zip'
const gitDownloadUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip'
// 递归复制目录,包括文件和隐藏文件
function copyDirSync(src: string, dest: string) {

View File

@@ -64,7 +64,7 @@ async function installPip(pythonPath: string, appRoot: string): Promise<void> {
console.log('pip未安装开始安装...')
const getPipPath = path.join(pythonPath, 'get-pip.py')
const getPipUrl = 'https://alist-automaa.fearr.xyz/d/AUTO_MAA/get-pip.py'
const getPipUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py'
console.log(`Python可执行文件路径: ${pythonExe}`)
console.log(`get-pip.py下载URL: ${getPipUrl}`)

View File

@@ -170,7 +170,12 @@ 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
}

View File

@@ -52,9 +52,14 @@ defineProps<{
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: '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 }
])
const selectedGitMirror = ref('github')
const testingGitSpeed = ref(false)

View File

@@ -90,23 +90,23 @@ const router = createRouter({
})
// // 添加路由守卫,确保在生产环境中也能正确进入初始化页面
// router.beforeEach(async (to, from, next) => {
// console.log('路由守卫:', { to: to.path, from: from.path })
//
// // 如果访问的不是初始化页面,且没有初始化标记,则重定向到初始化页面
// if (to.path !== '/initialization') {
// const initialized = await isAppInitialized()
// console.log('检查初始化状态:', initialized)
//
// if (!initialized) {
// console.log('应用未初始化,重定向到初始化页面')
// next('/initialization')
// return
// }
// }
//
// next()
// })
// 添加路由守卫,确保在生产环境中也能正确进入初始化页面
router.beforeEach(async (to, from, next) => {
console.log('路由守卫:', { to: to.path, from: from.path })
// 如果访问的不是初始化页面,且没有初始化标记,则重定向到初始化页面
if (to.path !== '/initialization') {
const initialized = await isAppInitialized()
console.log('检查初始化状态:', initialized)
if (!initialized) {
console.log('应用未初始化,重定向到初始化页面')
next('/initialization')
return
}
}
next()
})
export default router

View File

@@ -1,101 +1,101 @@
<template>
<div class="header">
<a-typography-title>{{ greeting }}</a-typography-title>
</div>
<div class="header">
<a-typography-title>主人早上好喵~</a-typography-title>
</div>
<div class="content">
<!-- 当期活动关卡 -->
<a-card title="当期活动关卡" class="activity-card" :loading="loading">
<template #extra>
<a-button type="text" @click="refreshActivity" :loading="loading">
<template #icon>
<ReloadOutlined />
</template>
刷新
</a-button>
</template>
<div v-if="error" class="error-message">
<a-alert
:message="error"
type="error"
show-icon
closable
@close="error = ''"
/>
</div>
<!-- 活动信息展示 -->
<div v-if="currentActivity && !loading" class="activity-info">
<div class="activity-header">
<div class="activity-left">
<div class="activity-name">
<span class="activity-title">{{ currentActivity.StageName }}</span>
<a-tag color="blue" class="activity-tip">{{ currentActivity.Tip }}</a-tag>
</div>
<div class="activity-end-time">
<ClockCircleOutlined class="time-icon" />
<span class="time-label">结束时间</span>
<span class="time-value">{{ formatTime(currentActivity.UtcExpireTime, currentActivity.TimeZone) }}</span>
</div>
<div class="content">
<!-- 当期活动关卡 -->
<a-card title="当期活动关卡" class="activity-card" :loading="loading">
<template #extra>
<a-button type="text" @click="refreshActivity" :loading="loading">
<template #icon>
<ReloadOutlined />
</template>
刷新
</a-button>
</template>
<div v-if="error" class="error-message">
<a-alert :message="error" type="error" show-icon closable @close="error = ''" />
</div>
<!-- 活动信息展示 -->
<div v-if="currentActivity && !loading" class="activity-info">
<div class="activity-header">
<div class="activity-left">
<div class="activity-name">
<span class="activity-title">{{ currentActivity.StageName }}</span>
<a-tag color="blue" class="activity-tip">{{ currentActivity.Tip }}</a-tag>
</div>
<div class="activity-right">
<a-statistic-countdown
title="当期活动剩余时间"
:value="getCountdownValue(currentActivity.UtcExpireTime)"
format="D 天 H 时 m 分"
:value-style="getCountdownStyle(currentActivity.UtcExpireTime)"
@finish="onCountdownFinish"
<div class="activity-end-time">
<ClockCircleOutlined class="time-icon" />
<span class="time-label">结束时间</span>
<span class="time-value">{{
formatTime(currentActivity.UtcExpireTime, currentActivity.TimeZone)
}}</span>
</div>
</div>
<div class="activity-right">
<a-statistic-countdown
title="当期活动剩余时间"
:value="getCountdownValue(currentActivity.UtcExpireTime)"
format="D 天 H 时 m 分"
:value-style="getCountdownStyle(currentActivity.UtcExpireTime)"
@finish="onCountdownFinish"
/>
</div>
</div>
</div>
<div v-if="activityData?.length" class="activity-list">
<div v-for="item in activityData" :key="item.Value" class="activity-item">
<div class="stage-info">
<div class="stage-name">{{ item.Display }}</div>
<!-- <div class="stage-value">{{ item.Value }}</div>-->
</div>
<div class="drop-info">
<div class="drop-image">
<img
:src="
item.DropName.startsWith('DESC:')
? getMaterialImage('固源岩')
: getMaterialImage(item.DropName)
"
:alt="item.DropName.startsWith('DESC:') ? '固源岩' : item.DropName"
@error="handleImageError"
/>
</div>
</div>
</div>
<div v-if="activityData?.length" class="activity-list">
<div
v-for="item in activityData"
:key="item.Value"
class="activity-item"
>
<div class="stage-info">
<div class="stage-name">{{ item.Display }}</div>
<!-- <div class="stage-value">{{ item.Value }}</div>-->
</div>
<div class="drop-info">
<div class="drop-image">
<img
:src="item.DropName.startsWith('DESC:') ? getMaterialImage('固源岩') : getMaterialImage(item.DropName)"
:alt="item.DropName.startsWith('DESC:') ? '固源岩' : item.DropName"
@error="handleImageError"
/>
</div>
<div class="drop-details">
<div class="drop-name">
{{ item.DropName.startsWith('DESC:') ? item.DropName.substring(5) : item.DropName }}
</div>
<!-- <div v-if="item.Drop && !item.DropName.startsWith('DESC:')" class="drop-id">-->
<!-- ID: {{ item.Drop }}-->
<!-- </div>-->
<div class="drop-details">
<div class="drop-name">
{{ item.DropName.startsWith('DESC:') ? item.DropName.substring(5) : item.DropName }}
</div>
<!-- <div v-if="item.Drop && !item.DropName.startsWith('DESC:')" class="drop-id">-->
<!-- ID: {{ item.Drop }}-->
<!-- </div>-->
</div>
</div>
</div>
<div v-else-if="!loading" class="empty-state">
<a-empty description="暂无活动关卡数据" />
</div>
</a-card>
</div>
</div>
<div v-else-if="!loading" class="empty-state">
<a-empty description="暂无活动关卡数据" />
</div>
</a-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { message } from 'ant-design-vue'
import { ReloadOutlined, InfoCircleOutlined, CalendarOutlined, ClockCircleOutlined } from '@ant-design/icons-vue'
import {
ReloadOutlined,
InfoCircleOutlined,
CalendarOutlined,
ClockCircleOutlined,
} from '@ant-design/icons-vue'
import { Service } from '@/api'
interface ActivityInfo {
@@ -134,7 +134,7 @@ const formatTime = (timeString: string, timeZone: number) => {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
minute: '2-digit',
})
} catch {
return timeString
@@ -157,25 +157,25 @@ const getCountdownStyle = (expireTime: string) => {
const now = new Date()
const remaining = expire.getTime() - now.getTime()
const twoDaysInMs = 2 * 24 * 60 * 60 * 1000
if (remaining <= twoDaysInMs) {
return {
color: '#ff4d4f',
fontWeight: 'bold',
fontSize: '18px'
fontSize: '18px',
}
}
return {
color: 'var(--ant-color-text)',
fontWeight: '600',
fontSize: '20px'
fontSize: '20px',
}
} catch {
return {
color: 'var(--ant-color-text)',
fontWeight: '600',
fontSize: '20px'
fontSize: '20px',
}
}
}
@@ -203,10 +203,10 @@ const handleImageError = (event: Event) => {
const fetchActivityData = async () => {
loading.value = true
error.value = ''
try {
const response = await Service.addOverviewApiInfoGetOverviewPost()
if (response.code === 200 && response.data?.ALL) {
activityData.value = response.data.ALL
} else {
@@ -227,13 +227,27 @@ const refreshActivity = async () => {
}
}
const greeting = computed(() => {
const hour = new Date().getHours()
if (hour >= 5 && hour < 11) {
return '主人早上好喵~'
} else if (hour >= 11 && hour < 14) {
return '主人中午好喵~'
} else if (hour >= 14 && hour < 18) {
return '主人下午好喵~'
} else if (hour >= 18 && hour < 23) {
return '主人晚上好喵~'
} else {
return '主人夜深了喵~早点休息喵~'
}
})
onMounted(() => {
fetchActivityData()
})
</script>
<style scoped>
.header {
margin-bottom: 24px;
}
@@ -245,8 +259,6 @@ onMounted(() => {
font-weight: 600;
}
.activity-card {
margin-bottom: 24px;
}
@@ -458,15 +470,15 @@ onMounted(() => {
.page-container {
padding: 16px;
}
.activity-list {
grid-template-columns: 1fr;
}
.activity-item {
padding: 12px;
}
.drop-image {
width: 40px;
height: 40px;