Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
92 lines
3.5 KiB
Go
92 lines
3.5 KiB
Go
package accesscontrol
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
type knowledgeReviewDocument struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
type knowledgeReviewChunk struct {
|
|
ID string `json:"id"`
|
|
Content string `json:"content"`
|
|
ChunkType string `json:"chunk_type"`
|
|
ChunkIndex int `json:"chunk_index"`
|
|
IsEnabled bool `json:"is_enabled"`
|
|
}
|
|
|
|
type knowledgeReviewPage struct {
|
|
Documents []knowledgeReviewDocument `json:"documents,omitempty"`
|
|
Document *knowledgeReviewDocument `json:"document,omitempty"`
|
|
Chunks []knowledgeReviewChunk `json:"chunks,omitempty"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
Total int64 `json:"total"`
|
|
}
|
|
|
|
func (s *Service) GetKnowledgeVersionReview(c *gin.Context) {
|
|
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
if err != nil || page < 1 || page > 100000 {
|
|
versionResult(c, errInvalid, nil)
|
|
return
|
|
}
|
|
result, err := s.knowledgeVersionReview(c.Request.Context(), c.Param("id"), c.Query("document_id"), page)
|
|
versionResult(c, err, result)
|
|
}
|
|
|
|
// Review reads only an immutable stage, without granting ordinary KB access or
|
|
// exposing file handles, provider configuration, or download URLs.
|
|
func (s *Service) knowledgeVersionReview(ctx context.Context, id, document string, page int) (*knowledgeReviewPage, error) {
|
|
if err := versionPermission(ctx, "knowledge", false); err != nil {
|
|
return nil, err
|
|
}
|
|
tenant, user := identity(ctx)
|
|
result := &knowledgeReviewPage{Page: page, PageSize: 20}
|
|
err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var submission ResourceSubmission
|
|
q := tx.Clauses(clause.Locking{Strength: "SHARE"}).Where("tenant_id=? AND id=? AND kind='knowledge'", tenant, id)
|
|
if !administrator(ctx) {
|
|
q = q.Where("owner_id=?", user)
|
|
}
|
|
if err := q.First(&submission).Error; err != nil {
|
|
return err
|
|
}
|
|
if submission.SnapshotID == "" || (submission.Status != "pending" && submission.Status != "rejected" && submission.Status != "withdrawn") {
|
|
return fmt.Errorf("%w: 当前记录没有可查看的只读知识快照;已批准记录请查看已发布知识库", errConflict)
|
|
}
|
|
var kb types.KnowledgeBase
|
|
if err := tx.Clauses(clause.Locking{Strength: "SHARE"}).Where("tenant_id=? AND id=? AND is_temporary=true AND version_state='snapshot'", tenant, submission.SnapshotID).First(&kb).Error; err != nil {
|
|
return err
|
|
}
|
|
docs := tx.Model(&types.Knowledge{}).Where("tenant_id=? AND knowledge_base_id=?", tenant, kb.ID)
|
|
if document == "" {
|
|
if err := docs.Count(&result.Total).Error; err != nil {
|
|
return err
|
|
}
|
|
result.Documents = []knowledgeReviewDocument{}
|
|
return docs.Select("id,title,type").Order("title,id").Offset((page - 1) * result.PageSize).Limit(result.PageSize).Find(&result.Documents).Error
|
|
}
|
|
result.Document = &knowledgeReviewDocument{}
|
|
if err := docs.Select("id,title,type").Where("id=?", document).Take(result.Document).Error; err != nil {
|
|
return err
|
|
}
|
|
chunks := tx.Model(&types.Chunk{}).Where("tenant_id=? AND knowledge_base_id=? AND knowledge_id=?", tenant, kb.ID, document)
|
|
if err := chunks.Count(&result.Total).Error; err != nil {
|
|
return err
|
|
}
|
|
result.Chunks = []knowledgeReviewChunk{}
|
|
return chunks.Select("id,content,chunk_type,chunk_index,is_enabled").Order("chunk_index,id").Offset((page - 1) * result.PageSize).Limit(result.PageSize).Find(&result.Chunks).Error
|
|
})
|
|
return result, err
|
|
}
|