mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-03-18 21:30:01 +08:00
### What problem does this PR solve? Fix: Enhanced user management functionality and cascading data deletion. Added tenant and related data initialization functionality during user creation, including tenants, user-tenant relationships, LLM configuration, and root folder. Added cascading deletion logic for user deletion, ensuring that all associated data is cleaned up simultaneously when a user is deleted. Implemented a Werkzeug-compatible password hash algorithm (scrypt) and verification functionality. Added multiple DAO methods to support batch data operations and cascading deletion. Improved user login processing and added token signing functionality. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
230 lines
5.8 KiB
Go
230 lines
5.8 KiB
Go
//
|
|
// 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 (
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"ragflow/internal/model"
|
|
)
|
|
|
|
// FileDAO file data access object
|
|
type FileDAO struct{}
|
|
|
|
// NewFileDAO create file DAO
|
|
func NewFileDAO() *FileDAO {
|
|
return &FileDAO{}
|
|
}
|
|
|
|
// GetByID gets file by ID
|
|
func (dao *FileDAO) GetByID(id string) (*model.File, error) {
|
|
var file model.File
|
|
err := DB.Where("id = ?", id).First(&file).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &file, nil
|
|
}
|
|
|
|
// GetByPfID gets files by parent folder ID with pagination and filtering
|
|
func (dao *FileDAO) GetByPfID(tenantID, pfID string, page, pageSize int, orderby string, desc bool, keywords string) ([]*model.File, int64, error) {
|
|
var files []*model.File
|
|
var total int64
|
|
|
|
query := DB.Model(&model.File{}).
|
|
Where("tenant_id = ? AND parent_id = ? AND id != ?", tenantID, pfID, pfID)
|
|
|
|
// Apply keyword filter
|
|
if keywords != "" {
|
|
query = query.Where("LOWER(name) LIKE ?", "%"+strings.ToLower(keywords)+"%")
|
|
}
|
|
|
|
// Count total
|
|
if err := query.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Apply ordering
|
|
orderDirection := "ASC"
|
|
if desc {
|
|
orderDirection = "DESC"
|
|
}
|
|
query = query.Order(orderby + " " + orderDirection)
|
|
|
|
// Apply pagination
|
|
if page > 0 && pageSize > 0 {
|
|
offset := (page - 1) * pageSize
|
|
if err := query.Offset(offset).Limit(pageSize).Find(&files).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
} else {
|
|
if err := query.Find(&files).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
}
|
|
|
|
return files, total, nil
|
|
}
|
|
|
|
// GetRootFolder gets or creates root folder for tenant
|
|
func (dao *FileDAO) GetRootFolder(tenantID string) (*model.File, error) {
|
|
var file model.File
|
|
err := DB.Where("tenant_id = ? AND parent_id = id", tenantID).First(&file).Error
|
|
if err == nil {
|
|
return &file, nil
|
|
}
|
|
|
|
// Create root folder if not exists
|
|
fileID := generateUUID()
|
|
file = model.File{
|
|
ID: fileID,
|
|
ParentID: fileID,
|
|
TenantID: tenantID,
|
|
CreatedBy: tenantID,
|
|
Name: "/",
|
|
Type: "folder",
|
|
Size: 0,
|
|
}
|
|
file.SourceType = ""
|
|
|
|
if err := DB.Create(&file).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &file, nil
|
|
}
|
|
|
|
// GetParentFolder gets parent folder of a file
|
|
func (dao *FileDAO) GetParentFolder(fileID string) (*model.File, error) {
|
|
var file model.File
|
|
err := DB.Where("id = ?", fileID).First(&file).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var parentFile model.File
|
|
err = DB.Where("id = ?", file.ParentID).First(&parentFile).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &parentFile, nil
|
|
}
|
|
|
|
// ListByParentID lists all files by parent ID (including subfolders)
|
|
func (dao *FileDAO) ListByParentID(parentID string) ([]*model.File, error) {
|
|
var files []*model.File
|
|
err := DB.Where("parent_id = ? AND id != ?", parentID, parentID).Find(&files).Error
|
|
return files, err
|
|
}
|
|
|
|
// GetFolderSize calculates folder size recursively
|
|
func (dao *FileDAO) GetFolderSize(folderID string) (int64, error) {
|
|
var size int64
|
|
|
|
var dfs func(parentID string) error
|
|
dfs = func(parentID string) error {
|
|
var files []*model.File
|
|
if err := DB.Select("id", "size", "type").
|
|
Where("parent_id = ? AND id != ?", parentID, parentID).
|
|
Find(&files).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, f := range files {
|
|
size += f.Size
|
|
if f.Type == "folder" {
|
|
if err := dfs(f.ID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if err := dfs(folderID); err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
}
|
|
|
|
// HasChildFolder checks if folder has child folders
|
|
func (dao *FileDAO) HasChildFolder(folderID string) (bool, error) {
|
|
var count int64
|
|
err := DB.Model(&model.File{}).
|
|
Where("parent_id = ? AND id != ? AND type = ?", folderID, folderID, "folder").
|
|
Count(&count).Error
|
|
return count > 0, err
|
|
}
|
|
|
|
// GetAllParentFolders gets all parent folders in path (from current to root)
|
|
func (dao *FileDAO) GetAllParentFolders(startID string) ([]*model.File, error) {
|
|
var parentFolders []*model.File
|
|
currentID := startID
|
|
|
|
for currentID != "" {
|
|
var file model.File
|
|
err := DB.Where("id = ?", currentID).First(&file).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
parentFolders = append(parentFolders, &file)
|
|
|
|
// Stop if we've reached the root folder (parent_id == id)
|
|
if file.ParentID == file.ID {
|
|
break
|
|
}
|
|
currentID = file.ParentID
|
|
}
|
|
|
|
return parentFolders, nil
|
|
}
|
|
|
|
// Create creates a new file
|
|
func (dao *FileDAO) Create(file *model.File) error {
|
|
return DB.Create(file).Error
|
|
}
|
|
|
|
// DeleteByTenantID deletes all files by tenant ID (hard delete)
|
|
func (dao *FileDAO) DeleteByTenantID(tenantID string) (int64, error) {
|
|
result := DB.Unscoped().Where("tenant_id = ?", tenantID).Delete(&model.File{})
|
|
return result.RowsAffected, result.Error
|
|
}
|
|
|
|
// DeleteByIDs deletes files by IDs (hard delete)
|
|
func (dao *FileDAO) DeleteByIDs(ids []string) (int64, error) {
|
|
if len(ids) == 0 {
|
|
return 0, nil
|
|
}
|
|
result := DB.Unscoped().Where("id IN ?", ids).Delete(&model.File{})
|
|
return result.RowsAffected, result.Error
|
|
}
|
|
|
|
// GetAllIDsByTenantID gets all file IDs by tenant ID
|
|
func (dao *FileDAO) GetAllIDsByTenantID(tenantID string) ([]string, error) {
|
|
var ids []string
|
|
err := DB.Model(&model.File{}).Where("tenant_id = ?", tenantID).Pluck("id", &ids).Error
|
|
return ids, err
|
|
}
|
|
|
|
// generateUUID generates a UUID
|
|
func generateUUID() string {
|
|
id := uuid.New().String()
|
|
return strings.ReplaceAll(id, "-", "")
|
|
}
|