实现应用市场从文件URL安装应用的功能

This commit is contained in:
jingrow 2025-10-27 01:58:33 +08:00
parent 42834c913a
commit 4504c3e8bf
2 changed files with 137 additions and 3 deletions

View File

@ -140,6 +140,7 @@ import { NInput, NButton, NIcon, NSpin, NEmpty, NSelect, NPagination, useMessage
import { Icon } from '@iconify/vue'
import axios from 'axios'
import { t } from '@/shared/i18n'
import { get_session_api_headers } from '@/shared/api/auth'
const message = useMessage()
const router = useRouter()
@ -212,9 +213,33 @@ function viewAppDetail(app: any) {
router.push(`/app-marketplace/${app.name}`)
}
function installApp(_app: any) {
// TODO:
message.info(t('Install feature coming soon'))
async function installApp(app: any) {
try {
if (!app.file_url) {
message.error(t('应用文件URL不存在'))
return
}
const response = await axios.post('/jingrow/install-from-url', new URLSearchParams({
url: app.file_url
}), {
headers: {
...get_session_api_headers(),
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (response.data.success) {
message.success(t('应用安装成功'))
//
router.push('/installed-apps')
} else {
message.error(response.data.error || t('安装失败'))
}
} catch (error: any) {
console.error('Install app error:', error)
message.error(error.response?.data?.detail || t('安装失败'))
}
}
function getImageUrl(imageUrl: string): string {

View File

@ -441,6 +441,115 @@ async def get_installed_apps(request: Request):
raise HTTPException(status_code=500, detail=str(e))
@router.post("/jingrow/install-from-url")
async def install_from_url(url: str = Form(...)):
"""从URL安装应用或扩展包"""
try:
# 下载文件
import tempfile
import requests
current = Path(__file__).resolve()
root = current.parents[4]
tmp_dir = root / "tmp"
tmp_dir.mkdir(parents=True, exist_ok=True)
# 创建临时文件
temp_filename = f"download_{uuid.uuid4().hex[:8]}{Path(url).suffix}"
temp_file_path = tmp_dir / temp_filename
# 下载文件
response = requests.get(url, stream=True, timeout=300)
response.raise_for_status()
with open(temp_file_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
# 使用现有的 install_app 函数安装
try:
result = install_app(str(temp_file_path), None)
if result.get('success'):
app_name_result = result.get('app_name')
backend_result = result.get('backend_result', {})
app_dir = backend_result.get('app_dir')
# 扩展包不添加到 Local Installed Apps返回时没有 app_dir
if not app_dir:
return result
# 对齐扫描安装的执行链
try:
# 1. 添加到 Local Installed Apps PageType
from jingrow.utils.jingrow_api import get_single_pagetype
pagetype_result = get_single_pagetype("Local Installed Apps")
if pagetype_result.get('success'):
config = pagetype_result.get('config', {})
apps_list = config.get('local_installed_apps', [])
else:
apps_list = []
# 检查是否已存在,如果存在则更新,否则添加
app_exists = False
for app in apps_list:
if app.get('app_name', '') == app_name_result:
app['app_version'] = '1.0.0'
app['git_branch'] = 'main'
app_exists = True
break
if not app_exists:
apps_list.append({
'app_name': app_name_result,
'app_version': '1.0.0',
'git_branch': 'main'
})
# 更新数据库
await _update_local_installed_apps(apps_list)
# 2. 调用 sync_app_files 同步文件到数据库
if app_dir:
current = Path(__file__).resolve()
root = current.parents[4]
apps_dir = root / "apps"
backend_dir = apps_dir / app_name_result / app_name_result
if not backend_dir.exists():
backend_dir = apps_dir / app_name_result
if backend_dir.exists():
try:
api_url = f"{Config.jingrow_server_url}/api/action/jingrow.ai.utils.jlocal.sync_app_files"
requests.post(
api_url,
json={'app_name': app_name_result, 'app_path': str(backend_dir), 'force': True},
headers=get_jingrow_api_headers(),
timeout=60
)
except Exception:
pass
except Exception:
pass
return result
else:
raise HTTPException(status_code=400, detail=result.get('error', '安装失败'))
finally:
# 清理下载的文件
try:
if temp_file_path.exists():
os.unlink(temp_file_path)
except:
pass
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/jingrow/uninstall-extension/{package_name}")
async def uninstall_extension(request: Request, package_name: str):
"""卸载扩展包 - 先获取模块列表,卸载数据库,再删除本地目录"""