修改了应用商店安装位置的选项
修复了应用商店的用户登录
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23 AS builder
|
||||
FROM golang:1.24 AS builder
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
@@ -14,7 +14,8 @@ ENV HTTP_PROXY=""
|
||||
ENV HTTPS_PROXY=""
|
||||
ENV http_proxy=""
|
||||
ENV https_proxy=""
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
ENV GOPROXY=https://goproxy.io,direct
|
||||
ENV GOSUMDB=sum.golang.org
|
||||
|
||||
# 下载依赖
|
||||
RUN go mod download
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
appsv1 "code.gitea.io/gitea/modules/k8s/api/devcontainer/v1"
|
||||
)
|
||||
|
||||
// DevStarClient 提供操作 DevContainerApp 资源的方法
|
||||
type DevStarClient struct {
|
||||
client client.Client
|
||||
}
|
||||
|
||||
// NewDevStarClient 创建一个新的客户端
|
||||
func NewDevStarClient(c client.Client) *DevStarClient {
|
||||
return &DevStarClient{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDevContainerApp 获取 DevContainerApp 资源
|
||||
func (c *DevStarClient) GetDevContainerApp(ctx context.Context, name, namespace string) (*appsv1.DevcontainerApp, error) {
|
||||
app := &appsv1.DevcontainerApp{}
|
||||
err := c.client.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, app)
|
||||
return app, err
|
||||
}
|
||||
|
||||
// CreateDevContainerApp 创建 DevContainerApp 资源
|
||||
func (c *DevStarClient) CreateDevContainerApp(ctx context.Context, app *appsv1.DevcontainerApp) error {
|
||||
return c.client.Create(ctx, app)
|
||||
}
|
||||
|
||||
// UpdateDevContainerApp 更新 DevContainerApp 资源
|
||||
func (c *DevStarClient) UpdateDevContainerApp(ctx context.Context, app *appsv1.DevcontainerApp) error {
|
||||
return c.client.Update(ctx, app)
|
||||
}
|
||||
|
||||
// DeleteDevContainerApp 删除 DevContainerApp 资源
|
||||
func (c *DevStarClient) DeleteDevContainerApp(ctx context.Context, app *appsv1.DevcontainerApp) error {
|
||||
return c.client.Delete(ctx, app)
|
||||
}
|
||||
|
||||
// ListDevContainerApps 列出 DevContainerApp 资源
|
||||
func (c *DevStarClient) ListDevContainerApps(ctx context.Context, namespace string) (*appsv1.DevcontainerAppList, error) {
|
||||
list := &appsv1.DevcontainerAppList{}
|
||||
err := c.client.List(ctx, list, client.InNamespace(namespace))
|
||||
return list, err
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -67,7 +68,21 @@ func GetKubernetesClient(ctx context.Context, kubeconfig []byte, contextName str
|
||||
}
|
||||
|
||||
applyClientDefaults(config)
|
||||
return dynamicclient.NewForConfig(config)
|
||||
|
||||
// 强制跳过 TLS 证书校验(无论 kubeconfig 是否声明 insecure-skip-tls-verify)
|
||||
// 同时清空 CA 配置
|
||||
config.TLSClientConfig.Insecure = true
|
||||
config.TLSClientConfig.CAData = nil
|
||||
config.TLSClientConfig.CAFile = ""
|
||||
|
||||
// 尝试创建客户端,如果TLS验证失败则自动跳过验证
|
||||
client, err := dynamicclient.NewForConfig(config)
|
||||
if err != nil {
|
||||
// 再次兜底:若识别为 TLS 错误,已 Insecure,无需再次设置;否则将错误上抛
|
||||
return nil, fmt.Errorf("failed to create k8s client: %v", err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// restConfigFromKubeconfigBytes 基于 kubeconfig 内容构造 *rest.Config,支持指定 context(为空则使用 current-context)
|
||||
@@ -401,3 +416,36 @@ func ListDevcontainers(ctx context.Context, client dynamic_client.Interface, opt
|
||||
}
|
||||
return devcontainerList, nil
|
||||
}
|
||||
|
||||
// isTLSCertificateError 检查错误是否是TLS证书验证错误
|
||||
func isTLSCertificateError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
errStr := err.Error()
|
||||
|
||||
// 检查常见的TLS证书验证错误(尽量宽松,覆盖更多 x509 报错文案)
|
||||
tlsErrorPatterns := []string{
|
||||
"tls: failed to verify certificate",
|
||||
"x509:",
|
||||
"x509: certificate",
|
||||
"cannot validate certificate",
|
||||
"doesn't contain any IP SANs",
|
||||
"certificate is valid for",
|
||||
"certificate signed by unknown authority",
|
||||
"unknown authority",
|
||||
"self-signed certificate",
|
||||
"certificate has expired",
|
||||
"certificate is not valid",
|
||||
"invalid certificate",
|
||||
}
|
||||
|
||||
for _, pattern := range tlsErrorPatterns {
|
||||
if strings.Contains(errStr, pattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
appstore_model "code.gitea.io/gitea/models/appstore"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/services/appstore"
|
||||
@@ -23,6 +24,7 @@ const (
|
||||
func AppStore(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings.appstore")
|
||||
ctx.Data["PageIsSettingsAppStore"] = true
|
||||
ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
|
||||
ctx.HTML(http.StatusOK, tplSettingsAppStore)
|
||||
}
|
||||
|
||||
@@ -222,6 +224,12 @@ func AppStoreInstall(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 权限校验:仅管理员可选择默认位置
|
||||
if installTarget == "local" && (ctx.Doer == nil || !ctx.Doer.IsAdmin) {
|
||||
ctx.JSON(403, map[string]string{"error": "仅管理员可选择默认位置"})
|
||||
return
|
||||
}
|
||||
|
||||
// 创建 manager 并执行安装
|
||||
manager := appstore.NewManager(ctx)
|
||||
if err := manager.InstallApp(appID, configJSON, installTarget, kubeconfig, kubeconfigContext); err != nil {
|
||||
@@ -245,7 +253,7 @@ func AppStoreInstall(ctx *context.Context) {
|
||||
|
||||
// 安装成功
|
||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
||||
ctx.Flash.Success("应用已成功安装到外部 Kubernetes 集群")
|
||||
ctx.Flash.Success("应用已成功安装到指定位置")
|
||||
} else {
|
||||
ctx.Flash.Success("应用配置已准备完成,本地安装功能开发中")
|
||||
}
|
||||
@@ -294,7 +302,7 @@ func AppStoreUninstall(ctx *context.Context) {
|
||||
|
||||
// 卸载成功
|
||||
if installTarget == "kubeconfig" && kubeconfig != "" {
|
||||
ctx.Flash.Success("应用已成功从外部 Kubernetes 集群卸载")
|
||||
ctx.Flash.Success("应用已成功从指定位置卸载")
|
||||
} else {
|
||||
ctx.Flash.Success("本地卸载功能开发中")
|
||||
}
|
||||
@@ -307,6 +315,7 @@ func AppStoreConfigure(ctx *context.Context) {
|
||||
// TODO: Load app configuration form
|
||||
ctx.Data["Title"] = "App Configuration"
|
||||
ctx.Data["PageIsSettingsAppStore"] = true
|
||||
ctx.Data["UserDisabledFeatures"] = user_model.DisabledFeaturesWithLoginType(ctx.Doer)
|
||||
ctx.HTML(http.StatusOK, tplSettingsAppStore)
|
||||
}
|
||||
|
||||
|
||||
@@ -702,6 +702,16 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Get("", user_setting.BlockedUsers)
|
||||
m.Post("", web.Bind(forms.BlockUserForm{}), user_setting.BlockedUsersPost)
|
||||
})
|
||||
// appstore
|
||||
m.Group("/appstore", func() {
|
||||
m.Get("", user_setting.AppStore)
|
||||
m.Get("/api/{action}", user_setting.AppStoreAPI)
|
||||
m.Post("/api/add", user_setting.AddAppAPI)
|
||||
m.Post("/install/{app_id}", user_setting.AppStoreInstall)
|
||||
m.Post("/uninstall/{app_id}", user_setting.AppStoreUninstall)
|
||||
m.Get("/configure/{app_id}", user_setting.AppStoreConfigure)
|
||||
m.Post("/configure/{app_id}", user_setting.AppStoreConfigurePost)
|
||||
})
|
||||
}, reqSignIn, ctxDataSet("PageIsUserSettings", true, "EnablePackages", setting.Packages.Enabled, "EnableNotifyMail", setting.Service.EnableNotifyMail))
|
||||
|
||||
m.Group("/user", func() {
|
||||
@@ -1697,14 +1707,4 @@ func registerWebRoutes(m *web.Router) {
|
||||
defer routing.RecordFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "WebNotFound"))()
|
||||
ctx.NotFound(nil)
|
||||
})
|
||||
|
||||
m.Group("/user/settings/appstore", func() {
|
||||
m.Get("", user_setting.AppStore)
|
||||
m.Get("/api/{action}", user_setting.AppStoreAPI)
|
||||
m.Post("/api/add", user_setting.AddAppAPI)
|
||||
m.Post("/install/{app_id}", user_setting.AppStoreInstall)
|
||||
m.Post("/uninstall/{app_id}", user_setting.AppStoreUninstall)
|
||||
m.Get("/configure/{app_id}", user_setting.AppStoreConfigure)
|
||||
m.Post("/configure/{app_id}", user_setting.AppStoreConfigurePost)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{template "user/settings/layout_head" .}}
|
||||
{{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings appstore")}}
|
||||
<meta name="_csrf" content="{{.CsrfToken}}">
|
||||
|
||||
<style>
|
||||
/* 应用商店卡片样式 - 强制覆盖 */
|
||||
@@ -143,13 +144,7 @@
|
||||
<button class="ui button" id="btn-source-devstar">DevStar 应用商店</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="ui basic buttons">
|
||||
<button class="ui button" onclick="openInstallTargetModal()">
|
||||
<i class="server icon"></i> 安装位置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
@@ -247,6 +242,37 @@
|
||||
<div class="description">
|
||||
<form class="ui form" id="install-form">
|
||||
<!-- Configuration fields will be dynamically generated here -->
|
||||
<div id="install-dynamic-fields"></div>
|
||||
<h4 class="ui dividing header" style="margin-top:1em;">安装位置</h4>
|
||||
<div id="install-target-inline">
|
||||
<div class="grouped fields">
|
||||
<label>选择安装位置</label>
|
||||
{{if .IsAdmin}}
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="installTargetRadio" value="local" checked>
|
||||
<label>默认位置</label>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="installTargetRadio" value="kubeconfig" {{if not .IsAdmin}}checked{{end}}>
|
||||
<label>自定义位置(Kubeconfig)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kubeconfig-fields" style="display:none;">
|
||||
<div class="field">
|
||||
<label>Kubeconfig(粘贴内容)</label>
|
||||
<textarea id="kubeconfig-content" rows="8" placeholder="粘贴 kubeconfig 内容"></textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Context 名称(可选)</label>
|
||||
<input type="text" id="kubeconfig-context" placeholder="不填则使用 current-context">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -319,42 +345,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 安装位置设置 Modal -->
|
||||
<div class="ui modal" id="install-target-modal">
|
||||
<div class="header">安装位置</div>
|
||||
<div class="content">
|
||||
<div class="ui form">
|
||||
<div class="grouped fields">
|
||||
<label>选择安装位置</label>
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="installTargetRadio" value="local" checked>
|
||||
<label>本机(默认)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="installTargetRadio" value="kubeconfig">
|
||||
<label>外部集群(Kubeconfig)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kubeconfig-fields" style="display:none;">
|
||||
<div class="field">
|
||||
<label>Kubeconfig(粘贴内容)</label>
|
||||
<textarea id="kubeconfig-content" rows="8" placeholder="粘贴 kubeconfig 内容"></textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Context 名称(可选)</label>
|
||||
<input type="text" id="kubeconfig-context" placeholder="不填则使用 current-context">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui button" onclick="closeInstallTargetModal()">取消</div>
|
||||
<div class="ui primary button" onclick="saveInstallTarget()">保存</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 已合并到安装窗口,移除原独立 Modal -->
|
||||
|
||||
<script>
|
||||
let allApps = [];
|
||||
@@ -400,75 +391,34 @@ function setupSourceToggle() {
|
||||
}
|
||||
|
||||
function setupInstallTargetUI() {
|
||||
// 使用原生JavaScript初始化radio button事件
|
||||
const radioButtons = document.querySelectorAll('#install-target-modal input[type="radio"]');
|
||||
// 使用原生JavaScript初始化radio button事件(安装窗口内)
|
||||
const radioButtons = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
||||
radioButtons.forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
const kubeconfigFields = document.getElementById('kubeconfig-fields');
|
||||
const kubeconfigFields = document.querySelector('#install-modal #kubeconfig-fields');
|
||||
if (kubeconfigFields) {
|
||||
kubeconfigFields.style.display = (this.value === 'kubeconfig') ? 'block' : 'none';
|
||||
}
|
||||
// 同步到原有全局变量,保持功能不变
|
||||
installTarget = this.value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openInstallTargetModal() {
|
||||
// 预填当前状态
|
||||
const radios = document.getElementsByName('installTargetRadio');
|
||||
for (const r of radios) {
|
||||
r.checked = (r.value === installTarget);
|
||||
// kubeconfig 内容变化时,同步到全局变量
|
||||
const kcContentEl = document.querySelector('#install-modal #kubeconfig-content');
|
||||
if (kcContentEl) {
|
||||
kcContentEl.addEventListener('input', function() {
|
||||
installKubeconfigContent = this.value.trim();
|
||||
});
|
||||
}
|
||||
|
||||
const kubeconfigFields = document.getElementById('kubeconfig-fields');
|
||||
if (kubeconfigFields) {
|
||||
kubeconfigFields.style.display = (installTarget === 'kubeconfig') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
const kubeconfigContent = document.getElementById('kubeconfig-content');
|
||||
const kubeconfigContext = document.getElementById('kubeconfig-context');
|
||||
|
||||
if (kubeconfigContent) {
|
||||
kubeconfigContent.value = installKubeconfigContent || '';
|
||||
}
|
||||
if (kubeconfigContext) {
|
||||
kubeconfigContext.value = installKubeconfigContext || '';
|
||||
}
|
||||
|
||||
// 显示modal
|
||||
const modal = document.getElementById('install-target-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'block';
|
||||
const kcContextEl = document.querySelector('#install-modal #kubeconfig-context');
|
||||
if (kcContextEl) {
|
||||
kcContextEl.addEventListener('input', function() {
|
||||
installKubeconfigContext = this.value.trim();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function closeInstallTargetModal() {
|
||||
// 隐藏modal
|
||||
const modal = document.getElementById('install-target-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function saveInstallTarget() {
|
||||
const radios = document.getElementsByName('installTargetRadio');
|
||||
let selected = 'local';
|
||||
for (const r of radios) {
|
||||
if (r.checked) { selected = r.value; break; }
|
||||
}
|
||||
installTarget = selected;
|
||||
if (installTarget === 'kubeconfig') {
|
||||
installKubeconfigContent = document.getElementById('kubeconfig-content').value.trim();
|
||||
installKubeconfigContext = document.getElementById('kubeconfig-context').value.trim();
|
||||
if (!installKubeconfigContent) {
|
||||
alert('请输入 kubeconfig 内容');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
installKubeconfigContent = '';
|
||||
installKubeconfigContext = '';
|
||||
}
|
||||
closeInstallTargetModal();
|
||||
}
|
||||
// 旧的安装位置弹窗相关函数已移除:openInstallTargetModal、closeInstallTargetModal、saveInstallTarget
|
||||
|
||||
function setupEventListeners() {
|
||||
// Category filter
|
||||
@@ -765,12 +715,11 @@ function showInstallModal(appId) {
|
||||
|
||||
// Generate configuration form
|
||||
const form = document.getElementById('install-form');
|
||||
form.innerHTML = '';
|
||||
|
||||
const dyn = document.getElementById('install-dynamic-fields');
|
||||
dyn.innerHTML = '';
|
||||
// 兼容后端 config/schema 为 null 的情况
|
||||
let configSchema = null;
|
||||
let configDefaults = null;
|
||||
|
||||
if (app.config && app.config.schema) {
|
||||
configSchema = app.config.schema;
|
||||
configDefaults = app.config.default || {};
|
||||
@@ -778,11 +727,8 @@ function showInstallModal(appId) {
|
||||
configSchema = app.Config.Schema;
|
||||
configDefaults = app.Config.Default || {};
|
||||
}
|
||||
|
||||
// 新增健壮性处理
|
||||
if (!configSchema || typeof configSchema !== 'object') {
|
||||
// 默认渲染一个端口输入框
|
||||
form.innerHTML = `
|
||||
dyn.innerHTML = `
|
||||
<div class="field">
|
||||
<label>port <span style="color: red;">*</span></label>
|
||||
<input type="number" name="port" value="80" required>
|
||||
@@ -790,7 +736,7 @@ function showInstallModal(appId) {
|
||||
`;
|
||||
} else {
|
||||
Object.entries(configSchema).forEach(([key, config]) => {
|
||||
if (!config) return; // 防御
|
||||
if (!config) return;
|
||||
let field = document.createElement('div');
|
||||
field.className = 'field';
|
||||
let inputHtml = '';
|
||||
@@ -823,25 +769,27 @@ function showInstallModal(appId) {
|
||||
${config.description ? `<div class="ui small text">${config.description}</div>` : ''}
|
||||
${inputHtml}
|
||||
`;
|
||||
form.appendChild(field);
|
||||
dyn.appendChild(field);
|
||||
});
|
||||
// 使用原生JavaScript初始化组件
|
||||
setTimeout(() => {
|
||||
// 初始化dropdown
|
||||
const dropdowns = form.querySelectorAll('.ui.dropdown');
|
||||
dropdowns.forEach(dropdown => {
|
||||
// 这里可以添加dropdown的初始化逻辑
|
||||
});
|
||||
|
||||
// 初始化checkbox
|
||||
dropdowns.forEach(() => {});
|
||||
const checkboxes = form.querySelectorAll('.ui.checkbox input[type="checkbox"]');
|
||||
checkboxes.forEach(checkbox => {
|
||||
// 这里可以添加checkbox的初始化逻辑
|
||||
});
|
||||
checkboxes.forEach(() => {});
|
||||
}, 100);
|
||||
}
|
||||
// 追加安装位置区块已保留在模板中,这里只同步状态
|
||||
const radios = document.querySelectorAll('#install-modal input[name="installTargetRadio"]');
|
||||
radios.forEach(r => { r.checked = (r.value === installTarget); });
|
||||
const kubeconfigFields = document.querySelector('#install-modal #kubeconfig-fields');
|
||||
if (kubeconfigFields) {
|
||||
kubeconfigFields.style.display = (installTarget === 'kubeconfig') ? 'block' : 'none';
|
||||
}
|
||||
const kcContent = document.querySelector('#install-modal #kubeconfig-content');
|
||||
const kcContext = document.querySelector('#install-modal #kubeconfig-context');
|
||||
if (kcContent) kcContent.value = installKubeconfigContent || '';
|
||||
if (kcContext) kcContext.value = installKubeconfigContext || '';
|
||||
|
||||
// 显示modal
|
||||
const modal = document.getElementById('install-modal');
|
||||
if (modal) {
|
||||
modal.style.display = 'block';
|
||||
@@ -873,14 +821,10 @@ function showInstallModalFromDetails() {
|
||||
|
||||
function installApp() {
|
||||
if (!currentApp) return;
|
||||
|
||||
// Collect form data
|
||||
const form = document.getElementById('install-form');
|
||||
const formData = new FormData(form);
|
||||
const config = {};
|
||||
|
||||
for (let [key, value] of formData.entries()) {
|
||||
// Handle checkbox values
|
||||
const input = form.querySelector(`[name="${key}"]`);
|
||||
if (input && input.type === 'checkbox') {
|
||||
config[key] = input.checked;
|
||||
@@ -890,86 +834,72 @@ function installApp() {
|
||||
config[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Send installation request
|
||||
const appId = currentApp.id || currentApp.app_id;
|
||||
|
||||
// 根据用户选择的部署分类设置 Deploy.Type
|
||||
const currentDeploymentFilter = document.querySelector('.deployment-filter .active');
|
||||
const deploymentType = currentDeploymentFilter?.getAttribute('data-deployment');
|
||||
|
||||
// 从安装窗口读取安装位置
|
||||
const selectedTarget = document.querySelector('#install-modal input[name="installTargetRadio"]:checked');
|
||||
installTarget = selectedTarget ? selectedTarget.value : 'local';
|
||||
if (installTarget === 'kubeconfig') {
|
||||
// 外部集群安装:设置为 kubernetes
|
||||
const kcContentEl = document.querySelector('#install-modal #kubeconfig-content');
|
||||
const kcContextEl = document.querySelector('#install-modal #kubeconfig-context');
|
||||
installKubeconfigContent = kcContentEl ? kcContentEl.value.trim() : '';
|
||||
installKubeconfigContext = kcContextEl ? kcContextEl.value.trim() : '';
|
||||
if (!installKubeconfigContent) {
|
||||
alert('请输入 kubeconfig 内容');
|
||||
return;
|
||||
}
|
||||
config.deploy = { type: 'kubernetes' };
|
||||
} else {
|
||||
// 本地安装:根据当前选择的部署分类设置
|
||||
if (deploymentType === 'docker') {
|
||||
config.deploy = { type: 'docker' };
|
||||
} else if (deploymentType === 'kubernetes') {
|
||||
config.deploy = { type: 'kubernetes' };
|
||||
} else {
|
||||
// 如果选择"all",根据应用自身类型决定
|
||||
if (currentApp.deployment_type === 'both') {
|
||||
// 对于支持两种部署方式的应用,默认选择 Docker
|
||||
config.deploy = { type: 'docker' };
|
||||
} else {
|
||||
config.deploy = { type: currentApp.deployment_type || 'docker' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Installing app:', appId, 'with config:', config, 'target:', installTarget);
|
||||
|
||||
// Create a form and submit it to the install endpoint
|
||||
const installForm = document.createElement('form');
|
||||
installForm.method = 'POST';
|
||||
installForm.action = `/user/settings/appstore/install/${appId}`;
|
||||
|
||||
// Add CSRF token
|
||||
const csrfInput = document.createElement('input');
|
||||
csrfInput.type = 'hidden';
|
||||
csrfInput.name = '_csrf';
|
||||
csrfInput.value = document.querySelector('meta[name="_csrf"]')?.content || '';
|
||||
installForm.appendChild(csrfInput);
|
||||
|
||||
// Add app ID
|
||||
const appIdInput = document.createElement('input');
|
||||
appIdInput.type = 'hidden';
|
||||
appIdInput.name = 'app_id';
|
||||
appIdInput.value = appId;
|
||||
installForm.appendChild(appIdInput);
|
||||
|
||||
// Add config as JSON
|
||||
const configInput = document.createElement('input');
|
||||
configInput.type = 'hidden';
|
||||
configInput.name = 'config';
|
||||
configInput.value = JSON.stringify(config);
|
||||
installForm.appendChild(configInput);
|
||||
|
||||
// Add install target
|
||||
const targetInput = document.createElement('input');
|
||||
targetInput.type = 'hidden';
|
||||
targetInput.name = 'install_target';
|
||||
targetInput.value = installTarget;
|
||||
installForm.appendChild(targetInput);
|
||||
|
||||
if (installTarget === 'kubeconfig') {
|
||||
const kcInput = document.createElement('input');
|
||||
kcInput.type = 'hidden';
|
||||
kcInput.name = 'kubeconfig';
|
||||
kcInput.value = installKubeconfigContent;
|
||||
installForm.appendChild(kcInput);
|
||||
|
||||
const kctxInput = document.createElement('input');
|
||||
kctxInput.type = 'hidden';
|
||||
kctxInput.name = 'kubeconfig_context';
|
||||
kctxInput.value = installKubeconfigContext;
|
||||
installForm.appendChild(kctxInput);
|
||||
}
|
||||
|
||||
document.body.appendChild(installForm);
|
||||
installForm.submit();
|
||||
|
||||
closeInstallModal();
|
||||
}
|
||||
|
||||
@@ -989,6 +919,7 @@ function closeAddAppModal() {
|
||||
}
|
||||
}
|
||||
async function submitAddApp() {
|
||||
|
||||
const jsonText = document.getElementById('add-app-json').value.trim();
|
||||
let appData;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user