feat: 支持列出MinIO 存储桶及权限
This commit is contained in:
@@ -96,3 +96,17 @@ export function updateConversationConfig(config: ConversationConfig): Promise<{
|
||||
export function getPromptTemplates(): Promise<{ data: PromptTemplatesConfig }> {
|
||||
return get('/api/v1/tenants/kv/prompt-templates')
|
||||
}
|
||||
|
||||
export interface MinioBucketInfo {
|
||||
name: string
|
||||
policy: 'public' | 'private' | 'custom'
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface ListMinioBucketsResponse {
|
||||
buckets: MinioBucketInfo[]
|
||||
}
|
||||
|
||||
export function listMinioBuckets(): Promise<{ data: ListMinioBucketsResponse }> {
|
||||
return get('/api/v1/system/minio/buckets')
|
||||
}
|
||||
|
||||
@@ -1270,7 +1270,13 @@ export default {
|
||||
minio: {
|
||||
bucketLabel: 'Bucket Name',
|
||||
bucketDescription: 'Name of the MinIO bucket (required)',
|
||||
bucketPlaceholder: 'Enter bucket name (required)',
|
||||
bucketPlaceholder: 'Select or enter bucket name',
|
||||
bucketHint: 'Select an existing bucket with public read access, or enter a new name to create automatically',
|
||||
policyLabels: {
|
||||
public: 'Public Read',
|
||||
private: 'Private',
|
||||
custom: 'Custom'
|
||||
},
|
||||
useSslLabel: 'Use SSL',
|
||||
useSslDescription: 'Whether to use SSL connection',
|
||||
pathPrefixLabel: 'Path Prefix',
|
||||
|
||||
@@ -1621,7 +1621,13 @@ export default {
|
||||
minio: {
|
||||
bucketLabel: "Bucket 名称",
|
||||
bucketDescription: "MinIO 存储桶名称(必填)",
|
||||
bucketPlaceholder: "请输入 Bucket 名称(必填)",
|
||||
bucketPlaceholder: "选择或输入 Bucket 名称",
|
||||
bucketHint: "选择已存在的公有读权限 Bucket,或输入新名称将自动创建",
|
||||
policyLabels: {
|
||||
public: "公有读",
|
||||
private: "私有",
|
||||
custom: "自定义"
|
||||
},
|
||||
useSslLabel: "使用 SSL",
|
||||
useSslDescription: "是否使用 SSL 连接",
|
||||
pathPrefixLabel: "路径前缀",
|
||||
|
||||
@@ -110,14 +110,51 @@
|
||||
<div class="setting-info">
|
||||
<label>{{ $t('knowledgeEditor.advanced.multimodal.minio.bucketLabel') }} <span class="required">*</span></label>
|
||||
<p class="desc">{{ $t('knowledgeEditor.advanced.multimodal.minio.bucketDescription') }}</p>
|
||||
<t-alert
|
||||
theme="info"
|
||||
:message="$t('knowledgeEditor.advanced.multimodal.minio.bucketHint')"
|
||||
style="margin-top: 8px;"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-control">
|
||||
<t-input
|
||||
<div class="setting-control bucket-control">
|
||||
<t-select
|
||||
v-model="localMultimodal.minio.bucketName"
|
||||
:placeholder="$t('knowledgeEditor.advanced.multimodal.minio.bucketPlaceholder')"
|
||||
:loading="loadingBuckets"
|
||||
filterable
|
||||
creatable
|
||||
@change="handleConfigChange"
|
||||
@focus="loadMinioBuckets"
|
||||
style="width: 280px;"
|
||||
/>
|
||||
>
|
||||
<t-option
|
||||
v-for="bucket in minioBuckets"
|
||||
:key="bucket.name"
|
||||
:value="bucket.name"
|
||||
:label="bucket.name"
|
||||
>
|
||||
<div class="bucket-option">
|
||||
<span class="bucket-name">{{ bucket.name }}</span>
|
||||
<t-tag
|
||||
:theme="bucket.policy === 'public' ? 'success' : bucket.policy === 'private' ? 'default' : 'warning'"
|
||||
size="small"
|
||||
variant="light"
|
||||
>
|
||||
{{ $t(`knowledgeEditor.advanced.multimodal.minio.policyLabels.${bucket.policy}`) }}
|
||||
</t-tag>
|
||||
</div>
|
||||
</t-option>
|
||||
</t-select>
|
||||
<t-button
|
||||
theme="default"
|
||||
variant="outline"
|
||||
size="small"
|
||||
:loading="loadingBuckets"
|
||||
@click="loadMinioBuckets"
|
||||
style="margin-left: 8px;"
|
||||
>
|
||||
<template #icon><refresh-icon /></template>
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -252,9 +289,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { RefreshIcon } from 'tdesign-icons-vue-next'
|
||||
import ModelSelector from '@/components/ModelSelector.vue'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import { getSystemInfo } from '@/api/system'
|
||||
import { getSystemInfo, listMinioBuckets, type MinioBucketInfo } from '@/api/system'
|
||||
|
||||
const uiStore = useUIStore()
|
||||
|
||||
@@ -302,6 +340,25 @@ const localQuestionGeneration = ref<QuestionGenerationConfig>(
|
||||
|
||||
const vllmSelectorRef = ref()
|
||||
const isMinioEnabled = ref(false)
|
||||
const minioBuckets = ref<MinioBucketInfo[]>([])
|
||||
const loadingBuckets = ref(false)
|
||||
|
||||
// Load MinIO buckets
|
||||
const loadMinioBuckets = async () => {
|
||||
if (!isMinioEnabled.value || loadingBuckets.value) return
|
||||
|
||||
loadingBuckets.value = true
|
||||
try {
|
||||
const response = await listMinioBuckets()
|
||||
if (response.data?.buckets) {
|
||||
minioBuckets.value = response.data.buckets
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load MinIO buckets:', error)
|
||||
} finally {
|
||||
loadingBuckets.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Check system status on mount
|
||||
onMounted(async () => {
|
||||
@@ -316,6 +373,11 @@ onMounted(async () => {
|
||||
localMultimodal.value.storageType = 'cos'
|
||||
emit('update:multimodal', localMultimodal.value)
|
||||
}
|
||||
|
||||
// Load MinIO buckets if enabled
|
||||
if (isMinioEnabled.value) {
|
||||
loadMinioBuckets()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch system info:', error)
|
||||
// Default to disabled if we can't fetch the info
|
||||
@@ -513,6 +575,25 @@ const handleConfigChange = () => {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.bucket-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bucket-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.bucket-name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.config-item {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/config"
|
||||
"github.com/Tencent/WeKnora/internal/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/neo4j/neo4j-go-driver/v6/neo4j"
|
||||
)
|
||||
|
||||
@@ -155,3 +158,106 @@ func (h *SystemHandler) isMinioEnabled() bool {
|
||||
|
||||
return endpoint != "" && accessKeyID != "" && secretAccessKey != ""
|
||||
}
|
||||
|
||||
// MinioBucketInfo represents bucket information with access policy
|
||||
type MinioBucketInfo struct {
|
||||
Name string `json:"name"`
|
||||
Policy string `json:"policy"` // "public", "private", "custom"
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
}
|
||||
|
||||
// ListMinioBucketsResponse defines the response structure for listing buckets
|
||||
type ListMinioBucketsResponse struct {
|
||||
Buckets []MinioBucketInfo `json:"buckets"`
|
||||
}
|
||||
|
||||
// ListMinioBuckets godoc
|
||||
// @Summary 列出 MinIO 存储桶
|
||||
// @Description 获取所有 MinIO 存储桶及其访问权限
|
||||
// @Tags 系统
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} ListMinioBucketsResponse "存储桶列表"
|
||||
// @Failure 400 {object} map[string]interface{} "MinIO 未启用"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器错误"
|
||||
// @Router /system/minio/buckets [get]
|
||||
func (h *SystemHandler) ListMinioBuckets(c *gin.Context) {
|
||||
ctx := logger.CloneContext(c.Request.Context())
|
||||
|
||||
// Check if MinIO is enabled
|
||||
if !h.isMinioEnabled() {
|
||||
logger.Warn(ctx, "MinIO is not enabled")
|
||||
c.JSON(400, gin.H{
|
||||
"code": 400,
|
||||
"msg": "MinIO is not enabled",
|
||||
"success": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get MinIO configuration from environment
|
||||
endpoint := os.Getenv("MINIO_ENDPOINT")
|
||||
accessKeyID := os.Getenv("MINIO_ACCESS_KEY_ID")
|
||||
secretAccessKey := os.Getenv("MINIO_SECRET_ACCESS_KEY")
|
||||
useSSL := os.Getenv("MINIO_USE_SSL") == "true"
|
||||
|
||||
// Create MinIO client
|
||||
minioClient, err := minio.New(endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
|
||||
Secure: useSSL,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error(ctx, "Failed to create MinIO client", "error", err)
|
||||
c.JSON(500, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Failed to connect to MinIO",
|
||||
"success": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// List all buckets
|
||||
buckets, err := minioClient.ListBuckets(context.Background())
|
||||
if err != nil {
|
||||
logger.Error(ctx, "Failed to list MinIO buckets", "error", err)
|
||||
c.JSON(500, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Failed to list buckets",
|
||||
"success": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get policy for each bucket
|
||||
bucketInfos := make([]MinioBucketInfo, 0, len(buckets))
|
||||
for _, bucket := range buckets {
|
||||
policy := "private" // default
|
||||
|
||||
// Try to get bucket policy
|
||||
policyStr, err := minioClient.GetBucketPolicy(context.Background(), bucket.Name)
|
||||
if err == nil && policyStr != "" {
|
||||
// Check if policy contains public read access
|
||||
if strings.Contains(policyStr, `"Effect":"Allow"`) &&
|
||||
strings.Contains(policyStr, `"Principal":"*"`) &&
|
||||
strings.Contains(policyStr, `"s3:GetObject"`) {
|
||||
policy = "public"
|
||||
} else {
|
||||
policy = "custom"
|
||||
}
|
||||
}
|
||||
|
||||
bucketInfos = append(bucketInfos, MinioBucketInfo{
|
||||
Name: bucket.Name,
|
||||
Policy: policy,
|
||||
CreatedAt: bucket.CreationDate.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
logger.Info(ctx, "Listed MinIO buckets successfully", "count", len(bucketInfos))
|
||||
c.JSON(200, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"success": true,
|
||||
"data": ListMinioBucketsResponse{Buckets: bucketInfos},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -379,6 +379,7 @@ func RegisterSystemRoutes(r *gin.RouterGroup, handler *handler.SystemHandler) {
|
||||
systemRoutes := r.Group("/system")
|
||||
{
|
||||
systemRoutes.GET("/info", handler.GetSystemInfo)
|
||||
systemRoutes.GET("/minio/buckets", handler.ListMinioBuckets)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user