first-commit
This commit is contained in:
338
routers/web/user/setting/appstore.go
Normal file
338
routers/web/user/setting/appstore.go
Normal file
@@ -0,0 +1,338 @@
|
||||
// Copyright 2024 The Devstar Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
appstore_model "code.gitea.io/gitea/models/appstore"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/services/appstore"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
const (
|
||||
tplSettingsAppStore templates.TplName = "user/settings/appstore"
|
||||
)
|
||||
|
||||
// AppStore displays the app store page
|
||||
func AppStore(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings.appstore")
|
||||
ctx.Data["PageIsSettingsAppStore"] = true
|
||||
ctx.HTML(http.StatusOK, tplSettingsAppStore)
|
||||
}
|
||||
|
||||
// AppStoreAPI handles API requests for app store data
|
||||
func AppStoreAPI(ctx *context.Context) {
|
||||
action := ctx.PathParam("action")
|
||||
|
||||
switch action {
|
||||
case "apps":
|
||||
handleGetApps(ctx)
|
||||
case "categories":
|
||||
handleGetCategories(ctx)
|
||||
case "tags":
|
||||
handleGetTags(ctx)
|
||||
default:
|
||||
ctx.JSON(http.StatusBadRequest, map[string]interface{}{
|
||||
"error": "Invalid action",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// handleGetApps returns the list of apps from database
|
||||
func handleGetApps(ctx *context.Context) {
|
||||
// Get query parameters
|
||||
category := ctx.FormString("category")
|
||||
tag := ctx.FormString("tag")
|
||||
search := ctx.FormString("search")
|
||||
deployment := ctx.FormString("deployment") // docker, kubernetes, all
|
||||
source := ctx.FormString("source") // local | devstar
|
||||
|
||||
manager := appstore.NewManager(ctx)
|
||||
|
||||
var apps []appstore.App
|
||||
var err error
|
||||
|
||||
if source == "devstar" {
|
||||
apps, err = manager.ListAppsFromDevstar()
|
||||
if err == nil {
|
||||
// 与本地一致的过滤逻辑(先拿全量,再在服务端筛选)
|
||||
if category != "" || tag != "" || search != "" || deployment != "" {
|
||||
var filtered []appstore.App
|
||||
for _, a := range apps {
|
||||
if category != "" && a.Category != category {
|
||||
continue
|
||||
}
|
||||
if tag != "" {
|
||||
matched := false
|
||||
for _, t := range a.Tags {
|
||||
if t == tag {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if deployment != "" && deployment != "all" {
|
||||
// 处理部署类型过滤,包括 'both' 类型
|
||||
appDeployment := a.DeploymentType
|
||||
if appDeployment == "" {
|
||||
// 如果没有 deployment_type 字段,尝试从其他字段获取
|
||||
if a.Deploy.Type != "" {
|
||||
appDeployment = a.Deploy.Type
|
||||
} else {
|
||||
appDeployment = "docker" // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
if appDeployment != "both" && appDeployment != deployment {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if search != "" {
|
||||
low := strings.ToLower(search)
|
||||
nameOk := strings.Contains(strings.ToLower(a.Name), low)
|
||||
descOk := strings.Contains(strings.ToLower(a.Description), low)
|
||||
authorOk := strings.Contains(strings.ToLower(a.Author), low)
|
||||
if !(nameOk || descOk || authorOk) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
filtered = append(filtered, a)
|
||||
}
|
||||
apps = filtered
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if search != "" {
|
||||
// Convert tag parameter to tags slice
|
||||
var tags []string
|
||||
if tag != "" {
|
||||
tags = strings.Split(tag, ",")
|
||||
}
|
||||
apps, err = manager.SearchApps(search, category, tags)
|
||||
} else {
|
||||
apps, err = manager.ListApps()
|
||||
|
||||
// Filter by category, tag and deployment if specified
|
||||
if category != "" || tag != "" || deployment != "" {
|
||||
var filteredApps []appstore.App
|
||||
for _, app := range apps {
|
||||
matchCategory := category == "" || app.Category == category
|
||||
matchTag := tag == ""
|
||||
if tag != "" {
|
||||
for _, appTag := range app.Tags {
|
||||
if appTag == tag {
|
||||
matchTag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理部署类型过滤,包括 'both' 类型
|
||||
matchDeployment := deployment == "" || deployment == "all"
|
||||
if deployment != "" && deployment != "all" {
|
||||
appDeployment := app.DeploymentType
|
||||
if appDeployment == "" {
|
||||
// 如果没有 deployment_type 字段,尝试从其他字段获取
|
||||
if app.Deploy.Type != "" {
|
||||
appDeployment = app.Deploy.Type
|
||||
} else {
|
||||
appDeployment = "docker" // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
matchDeployment = appDeployment == "both" || appDeployment == deployment
|
||||
}
|
||||
|
||||
if matchCategory && matchTag && matchDeployment {
|
||||
filteredApps = append(filteredApps, app)
|
||||
}
|
||||
}
|
||||
apps = filteredApps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"apps": apps,
|
||||
})
|
||||
}
|
||||
|
||||
// handleGetCategories returns the list of categories
|
||||
func handleGetCategories(ctx *context.Context) {
|
||||
categories, err := appstore_model.GetCategories(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"categories": categories,
|
||||
})
|
||||
}
|
||||
|
||||
// handleGetTags returns the list of tags
|
||||
func handleGetTags(ctx *context.Context) {
|
||||
tags, err := appstore_model.GetTags(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"tags": tags,
|
||||
})
|
||||
}
|
||||
|
||||
// AppStoreInstall handles app installation
|
||||
func AppStoreInstall(ctx *context.Context) {
|
||||
if ctx.Req.Method != "POST" {
|
||||
ctx.JSON(405, map[string]string{"error": "Method Not Allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
// 解析表单数据
|
||||
appID := ctx.FormString("app_id")
|
||||
configJSON := ctx.FormString("config")
|
||||
installTarget := ctx.FormString("install_target")
|
||||
kubeconfig := ctx.FormString("kubeconfig")
|
||||
kubeconfigContext := ctx.FormString("kubeconfig_context")
|
||||
|
||||
if appID == "" {
|
||||
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
// 创建 manager 并执行安装
|
||||
manager := appstore.NewManager(ctx)
|
||||
if err := manager.InstallApp(appID, configJSON, installTarget, kubeconfig, kubeconfigContext); err != nil {
|
||||
// 根据错误类型返回相应的状态码和消息
|
||||
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
||||
switch appErr.Code {
|
||||
case "APP_NOT_FOUND", "CONFIG_MERGE_ERROR":
|
||||
ctx.JSON(400, map[string]string{"error": appErr.Message + ": " + appErr.Details})
|
||||
case "KUBERNETES_INSTALL_ERROR":
|
||||
ctx.JSON(500, map[string]string{"error": appErr.Message + ": " + appErr.Details})
|
||||
case "NOT_IMPLEMENTED":
|
||||
ctx.JSON(501, map[string]string{"error": appErr.Message})
|
||||
default:
|
||||
ctx.JSON(500, map[string]string{"error": appErr.Message})
|
||||
}
|
||||
} else {
|
||||
ctx.JSON(500, map[string]string{"error": "安装失败: " + err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 安装成功
|
||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
||||
ctx.Flash.Success("应用已成功安装到外部 Kubernetes 集群")
|
||||
} else {
|
||||
ctx.Flash.Success("应用配置已准备完成,本地安装功能开发中")
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/appstore")
|
||||
}
|
||||
|
||||
// AppStoreUninstall handles app uninstallation
|
||||
func AppStoreUninstall(ctx *context.Context) {
|
||||
if ctx.Req.Method != "POST" {
|
||||
ctx.JSON(405, map[string]string{"error": "Method Not Allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
// 解析表单数据
|
||||
appID := ctx.FormString("app_id")
|
||||
installTarget := ctx.FormString("install_target")
|
||||
kubeconfig := ctx.FormString("kubeconfig")
|
||||
kubeconfigContext := ctx.FormString("kubeconfig_context")
|
||||
|
||||
if appID == "" {
|
||||
ctx.JSON(400, map[string]string{"error": "应用ID不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
// 创建 manager 并执行卸载
|
||||
manager := appstore.NewManager(ctx)
|
||||
if err := manager.UninstallApp(appID, installTarget, kubeconfig, kubeconfigContext); err != nil {
|
||||
// 根据错误类型返回相应的状态码和消息
|
||||
if appErr, ok := err.(*appstore.AppStoreError); ok {
|
||||
switch appErr.Code {
|
||||
case "APP_NOT_FOUND":
|
||||
ctx.JSON(400, map[string]string{"error": appErr.Message + ": " + appErr.Details})
|
||||
case "KUBERNETES_UNINSTALL_ERROR":
|
||||
ctx.JSON(500, map[string]string{"error": appErr.Message + ": " + appErr.Details})
|
||||
case "NOT_IMPLEMENTED":
|
||||
ctx.JSON(501, map[string]string{"error": appErr.Message})
|
||||
default:
|
||||
ctx.JSON(500, map[string]string{"error": appErr.Message})
|
||||
}
|
||||
} else {
|
||||
ctx.JSON(500, map[string]string{"error": "卸载失败: " + err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 卸载成功
|
||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
||||
ctx.Flash.Success("应用已成功从外部 Kubernetes 集群卸载")
|
||||
} else {
|
||||
ctx.Flash.Success("本地卸载功能开发中")
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/appstore")
|
||||
}
|
||||
|
||||
// AppStoreConfigure handles app configuration
|
||||
func AppStoreConfigure(ctx *context.Context) {
|
||||
// TODO: Load app configuration form
|
||||
ctx.Data["Title"] = "App Configuration"
|
||||
ctx.Data["PageIsSettingsAppStore"] = true
|
||||
ctx.HTML(http.StatusOK, tplSettingsAppStore)
|
||||
}
|
||||
|
||||
// AppStoreConfigurePost handles app configuration form submission
|
||||
func AppStoreConfigurePost(ctx *context.Context) {
|
||||
// TODO: Handle configuration form submission
|
||||
ctx.Flash.Success("App configuration feature coming soon")
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/appstore")
|
||||
}
|
||||
|
||||
// 添加应用API
|
||||
func AddAppAPI(ctx *context.Context) {
|
||||
manager := appstore.NewManager(ctx)
|
||||
if ctx.Req.Method != "POST" {
|
||||
ctx.JSON(405, map[string]string{"error": "Method Not Allowed"})
|
||||
return
|
||||
}
|
||||
defer ctx.Req.Body.Close()
|
||||
jsonBytes, err := io.ReadAll(ctx.Req.Body)
|
||||
if err != nil {
|
||||
ctx.JSON(400, map[string]string{"error": "读取请求体失败"})
|
||||
return
|
||||
}
|
||||
if err := manager.AddAppFromJSON(jsonBytes); err != nil {
|
||||
ctx.JSON(400, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, map[string]string{"msg": "ok"})
|
||||
}
|
Reference in New Issue
Block a user