实现应用市场从文件URL安装应用的功能
This commit is contained in:
parent
42834c913a
commit
4504c3e8bf
@ -140,6 +140,7 @@ import { NInput, NButton, NIcon, NSpin, NEmpty, NSelect, NPagination, useMessage
|
|||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { t } from '@/shared/i18n'
|
import { t } from '@/shared/i18n'
|
||||||
|
import { get_session_api_headers } from '@/shared/api/auth'
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -212,9 +213,33 @@ function viewAppDetail(app: any) {
|
|||||||
router.push(`/app-marketplace/${app.name}`)
|
router.push(`/app-marketplace/${app.name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function installApp(_app: any) {
|
async function installApp(app: any) {
|
||||||
// TODO: 实现安装应用
|
try {
|
||||||
message.info(t('Install feature coming soon'))
|
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 {
|
function getImageUrl(imageUrl: string): string {
|
||||||
|
|||||||
@ -441,6 +441,115 @@ async def get_installed_apps(request: Request):
|
|||||||
raise HTTPException(status_code=500, detail=str(e))
|
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}")
|
@router.post("/jingrow/uninstall-extension/{package_name}")
|
||||||
async def uninstall_extension(request: Request, package_name: str):
|
async def uninstall_extension(request: Request, package_name: str):
|
||||||
"""卸载扩展包 - 先获取模块列表,卸载数据库,再删除本地目录"""
|
"""卸载扩展包 - 先获取模块列表,卸载数据库,再删除本地目录"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user