feat: add user ownership to resources (#21914)

This commit is contained in:
buua436 authored and GitHub committed 2026-11-03 17:36:36 +08:00
1 parent 58360341ab
commit 84705f75c8
37 files changed
+247 -8

No files matched your search

+1
View File
@@ -286,6 +286,7 @@ func (s *Service) CreateUser(ctx context.Context, username, password, role strin
file := &entity.File{
ID: fileID,
ParentID: fileID,
UserID: userID,
TenantID: userID,
CreatedBy: userID,
Name: "/",
+3
View File
@@ -439,6 +439,9 @@ func InitDB(ctx context.Context, migrateDB bool) error {
if err = migrateConversationReferences(ctx, DB); err != nil {
return fmt.Errorf("failed to move references into messages: %w", err)
}
if err = migrateResourceUserIDs(ctx, DB); err != nil {
return fmt.Errorf("failed to backfill resource user IDs: %w", err)
}
common.Info("Database schema migrated successfully")
} else {
for _, column := range []string{"backlog_doc_ids", "inflight_doc_ids"} {
+10
View File
@@ -221,6 +221,7 @@ func (dao *FileDAO) GetRootFolder(ctx context.Context, db *gorm.DB, tenantID str
file = entity.File{
ID: fileID,
ParentID: fileID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: "/",
@@ -323,6 +324,9 @@ func (dao *FileDAO) GetAllParentFolders(ctx context.Context, db *gorm.DB, startI
// Create creates a new file
func (dao *FileDAO) Create(ctx context.Context, db *gorm.DB, file *entity.File) error {
if file.UserID == "" {
file.UserID = file.CreatedBy
}
return db.WithContext(ctx).Create(file).Error
}
@@ -406,6 +410,7 @@ func (dao *FileDAO) CreateFolder(ctx context.Context, db *gorm.DB, parentID, ten
file := &entity.File{
ID: common.GenerateToken(),
ParentID: parentID,
UserID: createdBy,
TenantID: tenantID,
CreatedBy: createdBy,
Name: name,
@@ -421,6 +426,9 @@ func (dao *FileDAO) CreateFolder(ctx context.Context, db *gorm.DB, parentID, ten
// Insert inserts a new file record
func (dao *FileDAO) Insert(ctx context.Context, db *gorm.DB, file *entity.File) error {
if file.UserID == "" {
file.UserID = file.CreatedBy
}
return db.WithContext(ctx).Create(file).Error
}
@@ -615,6 +623,7 @@ func (dao *FileDAO) newAFileFromDataset(ctx context.Context, db *gorm.DB, tenant
file := &entity.File{
ID: fileID,
ParentID: parentID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: name,
@@ -657,6 +666,7 @@ func (dao *FileDAO) addFileFromKB(ctx context.Context, db *gorm.DB, doc *entity.
file := &entity.File{
ID: fileID,
ParentID: datasetFolderID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: docName,
+3
View File
@@ -128,6 +128,9 @@ func (dao *KnowledgebaseDAO) EmbeddingBaseName(ctx context.Context, db *gorm.DB,
// Create creates a new knowledge base record
func (dao *KnowledgebaseDAO) Create(ctx context.Context, db *gorm.DB, kb *entity.Knowledgebase) error {
if kb.UserID == "" {
kb.UserID = kb.CreatedBy
}
return db.WithContext(ctx).Create(kb).Error
}
+87 -8
View File
@@ -126,18 +126,28 @@ func shouldSkipMigration(currentDBVersion, targetVersion string) bool {
// compareMigrationVersion orders two canonical migration versions, returning -1,
// 0 or 1 as current sorts before, equal to, or after target.
//
// It follows semver except for development prereleases, where it matches PEP
// 440, the ordering the Python migration gets from packaging.version: a
// trailing ".devN" identifier marks a build leading the version it is attached
// to, so v1.0.0-rc1.dev1 sorts below v1.0.0-rc1 and below v1.0.0-rc1.dev2.
// semver orders a longer prerelease above its prefix, which would place a dev
// build ahead of the very release it precedes.
// It follows semver except for development and post-release versions, where it
// matches PEP 440, the ordering the Python migration gets from
// packaging.version. A trailing ".devN" marks a build leading the version it is
// attached to, while a numeric "-N" suffix marks a post-release version.
func compareMigrationVersion(current, target string) int {
currentCore := migrationVersionCore(current)
targetCore := migrationVersionCore(target)
if cmp := semver.Compare(currentCore, targetCore); cmp != 0 {
return cmp
}
currentPost, isCurrentPost := postReleaseNumber(current)
targetPost, isTargetPost := postReleaseNumber(target)
if isCurrentPost || isTargetPost {
switch {
case isCurrentPost && isTargetPost:
return comparePostRelease(currentPost, targetPost)
case isCurrentPost:
return 1
default:
return -1
}
}
currentBase, currentDev := splitDevPrerelease(semver.Prerelease(current))
targetBase, targetDev := splitDevPrerelease(semver.Prerelease(target))
if cmp := semver.Compare(currentCore+currentBase, targetCore+targetBase); cmp != 0 {
@@ -157,6 +167,63 @@ func compareMigrationVersion(current, target string) int {
return 1
}
type postReleaseVersion struct {
number int
devNumber int
isDev bool
}
// postReleaseNumber recognizes the PEP 440 "-N" post-release shorthand, such
// as v1.0.0-1. semver parses it as a numeric prerelease, which sorts before rc
// versions and would make a later database migration appear older.
func postReleaseNumber(version string) (postReleaseVersion, bool) {
prerelease := strings.TrimPrefix(semver.Prerelease(version), "-")
if prerelease == "" {
return postReleaseVersion{}, false
}
parts := strings.Split(prerelease, ".")
number, err := strconv.Atoi(parts[0])
if err != nil {
return postReleaseVersion{}, false
}
post := postReleaseVersion{number: number}
if len(parts) == 1 {
return post, true
}
if len(parts) != 2 {
return postReleaseVersion{}, false
}
dev, ok := devIdentifierNumber(parts[1])
if !ok {
return postReleaseVersion{}, false
}
post.devNumber = dev
post.isDev = true
return post, true
}
func comparePostRelease(current, target postReleaseVersion) int {
if current.number < target.number {
return -1
}
if current.number > target.number {
return 1
}
if current.isDev && !target.isDev {
return -1
}
if !current.isDev && target.isDev {
return 1
}
if current.devNumber < target.devNumber {
return -1
}
if current.devNumber > target.devNumber {
return 1
}
return 0
}
// migrationVersionCore strips prerelease and build metadata, leaving the
// "vMAJOR.MINOR.PATCH" core the two versions are first compared on.
func migrationVersionCore(version string) string {
@@ -196,8 +263,9 @@ func devIdentifierNumber(identifier string) (int, bool) {
return value, true
}
// parseMigrationVersion normalizes a version to the "vMAJOR.MINOR.PATCH" form
// understood by semver, returning an empty string when it cannot be parsed.
// parseMigrationVersion normalizes a version to the semver form used for
// ordering, returning an empty string when it cannot be parsed. A numeric
// fourth component is a post-release counter: v1.0.0.1 becomes v1.0.0-1.
func parseMigrationVersion(version string) string {
normalized := strings.TrimSpace(version)
normalized = strings.TrimPrefix(normalized, "v")
@@ -206,6 +274,17 @@ func parseMigrationVersion(version string) string {
return ""
}
normalized = "v" + normalized
if !semver.IsValid(normalized) {
parts := strings.Split(normalized, ".")
if len(parts) == 4 {
if _, err := strconv.Atoi(parts[3]); err == nil {
candidate := strings.Join(parts[:3], ".") + "-" + parts[3]
if semver.IsValid(candidate) {
normalized = candidate
}
}
}
}
if !semver.IsValid(normalized) {
common.Warn("Invalid migration version format", zap.String("version", version))
return ""
+3
View File
@@ -53,6 +53,9 @@ func TestShouldSkipMigration(t *testing.T) {
{name: "missing v prefix", current: "0.27.1", target: "v0.27.1", want: true},
{name: "invalid current", current: "not-a-version", target: "v0.27.1", want: false},
{name: "invalid target", current: "v0.27.1", target: "not-a-version", want: false},
{name: "four-part post-release version", current: "v1.0.0.1", target: "v1.0.0.1", want: true},
{name: "four-part post-release follows release candidate", current: "v1.0.0.1", target: "v1.0.0-rc3.dev3", want: true},
{name: "release candidate precedes four-part post-release", current: "v1.0.0-rc3.dev3", target: "v1.0.0.1", want: false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
@@ -0,0 +1,98 @@
//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package dao
import (
"context"
"fmt"
"ragflow/internal/entity"
"gorm.io/gorm"
)
const resourceUserIDMigrationTargetVersion = "v1.0.0.1"
// migrateResourceUserIDs fills owner IDs added to resource tables. Existing
// created_by values identify the resource owner; tables without created_by
// retain the OSS invariant that tenant_id identifies the owner.
func migrateResourceUserIDs(ctx context.Context, db *gorm.DB) error {
version, err := GetDatabaseMigrationVersion(ctx, db)
if err != nil {
return fmt.Errorf("read database migration version: %w", err)
}
if shouldSkipMigration(version, resourceUserIDMigrationTargetVersion) {
return nil
}
resources := []struct {
table string
model interface{}
ownerField string
ownerColumn string
fallbackField string
fallbackColumn string
}{
{table: "chat_channel", model: &entity.ChatChannel{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "compilation_template_group", model: &entity.CompilationTemplateGroup{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "compilation_template", model: &entity.CompilationTemplate{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "connector", model: &entity.Connector{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "dialog", model: &entity.Chat{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "file", model: &entity.File{}, ownerField: "CreatedBy", ownerColumn: "created_by", fallbackField: "TenantID", fallbackColumn: "tenant_id"},
{table: "knowledgebase", model: &entity.Knowledgebase{}, ownerField: "CreatedBy", ownerColumn: "created_by", fallbackField: "TenantID", fallbackColumn: "tenant_id"},
{table: "memory", model: &entity.Memory{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "search", model: &entity.Search{}, ownerField: "CreatedBy", ownerColumn: "created_by", fallbackField: "TenantID", fallbackColumn: "tenant_id"},
{table: "mcp_server", model: &entity.MCPServer{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "skill_spaces", model: &entity.SkillSpace{}, ownerField: "TenantID", ownerColumn: "tenant_id"},
{table: "document", model: &entity.Document{}, ownerField: "CreatedBy", ownerColumn: "created_by"},
{table: "evaluation_datasets", model: &entity.EvaluationDataset{}, ownerField: "CreatedBy", ownerColumn: "created_by", fallbackField: "TenantID", fallbackColumn: "tenant_id"},
}
scoped := db.WithContext(ctx)
migrator := scoped.Migrator()
for _, resource := range resources {
if !migrator.HasTable(resource.model) {
continue
}
if !migrator.HasColumn(resource.model, "UserID") {
return fmt.Errorf("%s.user_id is missing after GORM migration", resource.table)
}
if !migrator.HasColumn(resource.model, resource.ownerField) {
return fmt.Errorf("%s.%s is missing", resource.table, resource.ownerColumn)
}
if resource.fallbackField != "" && !migrator.HasColumn(resource.model, resource.fallbackField) {
return fmt.Errorf("%s.%s is missing", resource.table, resource.fallbackColumn)
}
ownerExpr := fmt.Sprintf("NULLIF(%s, '')", resource.ownerColumn)
if resource.fallbackColumn != "" {
ownerExpr = fmt.Sprintf("COALESCE(%s, NULLIF(%s, ''), '')", ownerExpr, resource.fallbackColumn)
} else {
ownerExpr = fmt.Sprintf("COALESCE(%s, '')", ownerExpr)
}
if err := scoped.Table(resource.table).
Where("user_id IS NULL OR user_id = ?", "").
UpdateColumn("user_id", gorm.Expr(ownerExpr)).Error; err != nil {
return fmt.Errorf("backfill %s.user_id from %s: %w", resource.table, resource.ownerColumn, err)
}
}
if err := setDatabaseMigrationVersion(ctx, db, resourceUserIDMigrationTargetVersion); err != nil {
return fmt.Errorf("mark database migration version %s: %w", resourceUserIDMigrationTargetVersion, err)
}
return nil
}
+3
View File
@@ -151,6 +151,9 @@ func (dao *SearchDAO) GetByNameAndTenant(ctx context.Context, db *gorm.DB, name
// Create creates a new search
func (dao *SearchDAO) Create(ctx context.Context, db *gorm.DB, search *entity.Search) error {
if search.UserID == "" {
search.UserID = search.CreatedBy
}
return db.WithContext(ctx).Create(search).Error
}
+1
View File
@@ -21,6 +21,7 @@ import "encoding/json"
// Chat chat model (mapped to dialog table)
type Chat struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name *string `gorm:"column:name;size:255;index" json:"name,omitempty"`
Description *string `gorm:"column:description;type:longtext" json:"description,omitempty"`
+1
View File
@@ -19,6 +19,7 @@ package entity
// ChatChannel chat channel model
type ChatChannel struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:128;not null" json:"name"`
Channel string `gorm:"column:channel;size:128;not null;index" json:"channel"`
+1
View File
@@ -19,6 +19,7 @@ package entity
// CompilationTemplate represents a compilation template
type CompilationTemplate struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID *string `gorm:"column:tenant_id;size:32;index" json:"tenant_id,omitempty"`
GroupID *string `gorm:"column:group_id;size:32;index" json:"group_id,omitempty"`
Name string `gorm:"column:name;size:128;not null;index" json:"name"`
@@ -19,6 +19,7 @@ package entity
// CompilationTemplateGroup represents a group for compilation templates
type CompilationTemplateGroup struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:128;not null;index" json:"name"`
Description *string `gorm:"column:description;type:text" json:"description,omitempty"`
+1
View File
@@ -24,6 +24,7 @@ import (
// Connector connector model
type Connector struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:128;not null" json:"name"`
Source string `gorm:"column:source;size:128;not null;index" json:"source"`
+1
View File
@@ -102,6 +102,7 @@ const (
type Knowledgebase struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
Avatar *string `gorm:"column:avatar;type:longtext" json:"avatar,omitempty"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:128;not null;index" json:"name"`
Language *string `gorm:"column:language;size:32;index;default:'English'" json:"language,omitempty"`
+1
View File
@@ -23,6 +23,7 @@ type Document struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
Thumbnail *string `gorm:"column:thumbnail;type:longtext" json:"thumbnail,omitempty"`
KbID string `gorm:"column:kb_id;size:256;not null;index" json:"kb_id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
ParserID string `gorm:"column:parser_id;size:32;not null;index" json:"parser_id"`
PipelineID *string `gorm:"column:pipeline_id;size:32;index" json:"pipeline_id,omitempty"`
PipelineVersionID *string `gorm:"column:pipeline_version_id;size:32;index" json:"pipeline_version_id,omitempty"`
+1
View File
@@ -20,6 +20,7 @@ package entity
// Note: Python defines custom create_time/update_time (not null) instead of using BaseModel's
type EvaluationDataset struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:255;not null;index" json:"name"`
Description *string `gorm:"column:description;type:longtext" json:"description,omitempty"`
+1
View File
@@ -20,6 +20,7 @@ package entity
type File struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
ParentID string `gorm:"column:parent_id;size:32;not null;index" json:"parent_id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
CreatedBy string `gorm:"column:created_by;size:32;not null;index" json:"created_by"`
Name string `gorm:"column:name;size:255;not null;index" json:"name"`
+1
View File
@@ -20,6 +20,7 @@ package entity
type MCPServer struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
Name string `gorm:"column:name;size:255;not null" json:"name"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
URL string `gorm:"column:url;size:2048;not null" json:"url"`
ServerType string `gorm:"column:server_type;size:32;not null" json:"server_type"`
+1
View File
@@ -21,6 +21,7 @@ type Memory struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
Name string `gorm:"column:name;size:128;not null" json:"name"`
Avatar *string `gorm:"column:avatar;type:longtext" json:"avatar,omitempty"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
MemoryType int64 `gorm:"column:memory_type;default:1;index" json:"memory_type"`
StorageType string `gorm:"column:storage_type;size:32;not null;default:table;index" json:"storage_type"`
+1
View File
@@ -20,6 +20,7 @@ package entity
type Search struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
Avatar *string `gorm:"column:avatar;type:longtext" json:"avatar,omitempty"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:128;not null;index" json:"name"`
Description *string `gorm:"column:description;type:longtext" json:"description,omitempty"`
+1
View File
@@ -28,6 +28,7 @@ const (
// SkillSpace represents a skills space (library) that contains skills
type SkillSpace struct {
ID string `gorm:"column:id;primaryKey;size:32" json:"id"`
UserID string `gorm:"column:user_id;size:32;not null;default:'';index" json:"user_id"`
TenantID string `gorm:"column:tenant_id;size:32;not null;index" json:"tenant_id"`
Name string `gorm:"column:name;size:128;not null" json:"name"`
FolderID string `gorm:"column:folder_id;size:32;not null" json:"folder_id"`
+1
View File
@@ -355,6 +355,7 @@ func (s *ChatService) Create(ctx context.Context, userID string, req map[string]
req["name"] = name
chat := buildCreateChatEntity(req, tenantID)
chat.UserID = userID
if err = dao.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := s.chatDAO.Create(ctx, tx, chat); err != nil {
return err
+1
View File
@@ -110,6 +110,7 @@ func (s *ChatChannelService) CreateChatChannel(ctx context.Context, userID, name
}
row := &entity.ChatChannel{
ID: common.GenerateUUID(),
UserID: userID,
TenantID: tenantID,
Name: name,
Channel: channelType,
@@ -133,9 +133,11 @@ func (s *CompilationTemplateGroupService) CreateGroup(ctx context.Context, tenan
}
groupID := common.GenerateUUID()
creatorID := identityForTemplateTenant(ctx, tenantID).UserID
if err := dao.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
group := &entity.CompilationTemplateGroup{
ID: groupID,
UserID: creatorID,
TenantID: tenantID,
Name: groupName,
Description: strptr(req.Description),
@@ -400,6 +402,7 @@ func (s *CompilationTemplateGroupService) insertChildren(ctx context.Context, db
valid := string(entity.StatusValid)
tmpl := &entity.CompilationTemplate{
ID: common.GenerateUUID(),
UserID: identityForTemplateTenant(ctx, tenantID).UserID,
TenantID: &tenantID,
GroupID: &groupID,
Name: name,
@@ -524,6 +527,7 @@ func (s *CompilationTemplateGroupService) reconcileChildren(ctx context.Context,
valid := string(entity.StatusValid)
tmpl := &entity.CompilationTemplate{
ID: newID,
UserID: identityForTemplateTenant(ctx, tenantID).UserID,
TenantID: &tenantID,
GroupID: &groupID,
Name: newName,
+1
View File
@@ -308,6 +308,7 @@ func (s *ConnectorService) CreateConnector(ctx context.Context, userID string, r
connector := &entity.Connector{
ID: common.GenerateUUID(),
UserID: userID,
TenantID: tenantID,
Name: req.Name,
Source: req.Source,
+1
View File
@@ -283,6 +283,7 @@ func (d *DatasetService) CreateDataset(ctx context.Context, req *service.CreateD
ID: kbID,
PipelineVersionID: pipelineVersionID,
Name: name,
UserID: userID,
TenantID: tenantID,
CreatedBy: userID,
ParserID: parserID,
@@ -359,6 +359,9 @@ func (s *DocumentService) RemoveDocumentKeepFile(ctx context.Context, docID stri
// updates dataset/document counters on insert. The document's ID and timestamps
// are populated by the caller / model hooks before insertion.
func (s *DocumentService) InsertDocument(ctx context.Context, doc *entity.Document) error {
if doc.UserID == "" {
doc.UserID = doc.CreatedBy
}
return dao.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if err := tx.Create(doc).Error; err != nil {
return fmt.Errorf("failed to create document: %w", err)
@@ -175,6 +175,7 @@ func (s *DocumentService) newAFileFromKB(ctx context.Context, tenantID, name, pa
folder := &entity.File{
ID: common.GenerateToken(),
ParentID: parentID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: name,
@@ -208,6 +209,7 @@ func (s *DocumentService) addFileFromKB(ctx context.Context, doc *entity.Documen
file := &entity.File{
ID: fileID,
ParentID: kbFolderID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: name,
@@ -324,6 +326,7 @@ func (s *DocumentService) newDatasetDocument(kb *entity.Knowledgebase, tenantID,
doc := &entity.Document{
ID: docID,
KbID: kb.ID,
UserID: tenantID,
ParserID: parserID,
PipelineID: pipelineID,
PipelineVersionID: kb.PipelineVersionID,
@@ -247,6 +247,7 @@ func (s *File2DocumentService) convertFiles(ctx context.Context, fileIDs, kbIDs
doc := &entity.Document{
ID: common.GenerateUUID(),
KbID: kb.ID,
UserID: userID,
ParserID: parserID,
PipelineID: pipelineID,
PipelineVersionID: kb.PipelineVersionID,
@@ -138,6 +138,7 @@ func (s *DocumentService) insertSyncDocument(ctx context.Context, input service.
doc := s.newDatasetDocument(kb, tenantID, filename, "", filetype, selection, parserConfig, input.SourceType, int64(len(input.SourceDocument.Blob)), input.SourceDocument.Blob)
if ownerID := strings.TrimSpace(kb.CreatedBy); ownerID != "" {
doc.CreatedBy = ownerID
doc.UserID = ownerID
}
doc.ID = input.DocumentID
// A fingerprint is acknowledged only after all dependent work succeeds.
+1
View File
@@ -235,6 +235,7 @@ func (s *FileService) initSkillsFolder(ctx context.Context, rootID, tenantID str
folder := &entity.File{
ID: common.GenerateToken(),
ParentID: rootID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: SkillsFolderName,
+1
View File
@@ -146,6 +146,7 @@ func (s *FileService) UploadFile(ctx context.Context, tenantID, parentID string,
fileRecord := &entity.File{
ID: common.GenerateToken(),
ParentID: lastFolder.ID,
UserID: userID,
TenantID: tenantID,
CreatedBy: userID,
Name: uniqueName,
+2
View File
@@ -154,6 +154,7 @@ func (s *MCPService) CreateMCPServer(ctx context.Context, userID string, req Cre
server := &entity.MCPServer{
ID: common.GenerateUUID(),
Name: req.Name,
UserID: userID,
TenantID: tenantID,
URL: req.URL,
ServerType: req.ServerType,
@@ -826,6 +827,7 @@ func (s *MCPService) ImportServers(ctx context.Context, userID string, servers m
server := &entity.MCPServer{
ID: common.GenerateUUID(),
UserID: userID,
TenantID: tenantID,
Name: newName,
URL: url,
+1
View File
@@ -572,6 +572,7 @@ func (s *MemoryService) CreateMemory(ctx context.Context, userID string, req *Cr
memory := &entity.Memory{
ID: newID,
Name: memoryName,
UserID: userID,
TenantID: tenantID,
MemoryType: memoryTypeInt,
StorageType: "table",
+1
View File
@@ -184,6 +184,7 @@ func (s *SearchService) CreateSearch(ctx context.Context, name string, descripti
// Create search entity
search := &entity.Search{
ID: searchID,
UserID: identity.UserID,
TenantID: identity.TenantID,
Name: uniqueName,
CreatedBy: identity.UserID,
+3
View File
@@ -148,6 +148,7 @@ func (s *SkillSpaceService) getSkillsFolderID(ctx context.Context, tenantID stri
folder := &entity.File{
ID: folderID,
ParentID: rootFolder.ID,
UserID: tenantID,
TenantID: tenantID,
CreatedBy: tenantID,
Name: "skills",
@@ -266,6 +267,7 @@ func (s *SkillSpaceService) CreateSpace(ctx context.Context, userID string, req
folder := &entity.File{
ID: folderID,
ParentID: skillsFolderID,
UserID: userID,
TenantID: tenantID,
CreatedBy: userID,
Name: spaceName,
@@ -277,6 +279,7 @@ func (s *SkillSpaceService) CreateSpace(ctx context.Context, userID string, req
// Create the space
space := &entity.SkillSpace{
ID: spaceID,
UserID: userID,
TenantID: tenantID,
Name: spaceName,
FolderID: folderID,
+1
View File
@@ -225,6 +225,7 @@ func (s *UserService) Register(ctx context.Context, req *RegisterRequest) (*enti
rootFile := &entity.File{
ID: fileID,
ParentID: fileID,
UserID: userID,
TenantID: userID,
CreatedBy: userID,
Name: "/",