diff --git a/attachment/convert.go b/attachment/convert.go new file mode 100644 index 00000000..7eb1ee8d --- /dev/null +++ b/attachment/convert.go @@ -0,0 +1,61 @@ +package attachment + +import ( + "fmt" + "strings" +) + +// toBool converts various types to boolean +func toBool(v interface{}) bool { + if v == nil { + return false + } + + switch val := v.(type) { + case bool: + return val + case int: + return val != 0 + case int64: + return val != 0 + case uint8: // MySQL tinyint(1) + return val != 0 + case float64: + return val != 0 + case string: + normalized := strings.ToLower(strings.TrimSpace(val)) + switch normalized { + case "true", "1", "enabled", "yes", "on": + return true + default: + return false + } + default: + return false + } +} + +// toString converts various types to string +func toString(v interface{}) string { + if v == nil { + return "" + } + + switch val := v.(type) { + case string: + return val + case int: + return fmt.Sprintf("%d", val) + case int64: + return fmt.Sprintf("%d", val) + case float64: + return fmt.Sprintf("%.0f", val) + case bool: + if val { + return "true" + } + return "false" + default: + return fmt.Sprintf("%v", val) + } +} diff --git a/attachment/manager.go b/attachment/manager.go index 31e8827e..6bfd8c67 100644 --- a/attachment/manager.go +++ b/attachment/manager.go @@ -652,9 +652,9 @@ func (manager Manager) List(ctx context.Context, option ListOption) (*ListResult // Add select fields if len(option.Select) > 0 { - queryParam.Select = make([]interface{}, len(option.Select)) - for i, field := range option.Select { - queryParam.Select[i] = field + queryParam.Select = make([]interface{}, 0, len(option.Select)) + for _, field := range option.Select { + queryParam.Select = append(queryParam.Select, field) } } @@ -680,6 +680,14 @@ func (manager Manager) List(ctx context.Context, option ListOption) (*ListResult } } + // Add advanced where clauses (for permission filtering, etc.) + if len(option.Wheres) > 0 { + if queryParam.Wheres == nil { + queryParam.Wheres = make([]model.QueryWhere, 0, len(option.Wheres)) + } + queryParam.Wheres = append(queryParam.Wheres, option.Wheres...) + } + // Add ordering if option.OrderBy != "" { // Parse order by string like "created_at desc" or "name asc" @@ -1080,6 +1088,12 @@ func (manager Manager) saveFileToDatabase(ctx context.Context, file *File, stora m := model.Select("__yao.attachment") + // Set default value for share if empty + share := option.Share + if share == "" { + share = "private" + } + // Prepare data for database data := map[string]interface{}{ "file_id": file.ID, @@ -1092,8 +1106,22 @@ func (manager Manager) saveFileToDatabase(ctx context.Context, file *File, stora "status": file.Status, "gzip": option.Gzip, "groups": option.Groups, - "client_id": option.ClientID, - "openid": option.OpenID, + "public": option.Public, + "share": share, + } + + // Add Yao permission fields if provided + if option.YaoCreatedBy != "" { + data["__yao_created_by"] = option.YaoCreatedBy + } + if option.YaoUpdatedBy != "" { + data["__yao_updated_by"] = option.YaoUpdatedBy + } + if option.YaoTeamID != "" { + data["__yao_team_id"] = option.YaoTeamID + } + if option.YaoTenantID != "" { + data["__yao_tenant_id"] = option.YaoTenantID } // Check if record exists first @@ -1128,9 +1156,14 @@ func (manager Manager) getFileFromDatabase(ctx context.Context, fileID string) ( m := model.Select("__yao.attachment") records, err := m.Get(model.QueryParam{ + Select: []interface{}{ + "file_id", "name", "content_type", "status", "user_path", "path", "bytes", + "public", "share", "__yao_created_by", "__yao_team_id", "__yao_tenant_id", + }, Wheres: []model.QueryWhere{ {Column: "file_id", Value: fileID}, }, + Limit: 1, }) if err != nil { @@ -1165,6 +1198,13 @@ func (manager Manager) getFileFromDatabase(ctx context.Context, fileID string) ( file.Bytes = int(bytes) } + // Handle permission fields with safe conversion + file.Public = toBool(record["public"]) + file.Share = toString(record["share"]) + file.YaoCreatedBy = toString(record["__yao_created_by"]) + file.YaoTeamID = toString(record["__yao_team_id"]) + file.YaoTenantID = toString(record["__yao_tenant_id"]) + return file, nil } diff --git a/attachment/manager_test.go b/attachment/manager_test.go index 29e7e2d5..c1424fa5 100644 --- a/attachment/manager_test.go +++ b/attachment/manager_test.go @@ -556,8 +556,8 @@ func TestInfo(t *testing.T) { option := UploadOption{ Groups: []string{"info", "test"}, OriginalFilename: "original-info-test.txt", - ClientID: "test-client-123", - OpenID: "test-openid-456", + Public: false, + Share: "private", Gzip: false, } @@ -1013,6 +1013,373 @@ func TestManagerLocalPath_NonExistentFile(t *testing.T) { } } +func TestPublicAndShareFields(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + + // Force re-migrate the attachment table to ensure schema is up to date + m := model.Select("__yao.attachment") + if m != nil { + // Drop and recreate table to get latest schema + err := m.DropTable() + if err != nil { + t.Logf("Warning: failed to drop table: %v", err) + } + err = m.Migrate(false) + if err != nil { + t.Fatalf("Failed to migrate table: %v", err) + } + } + + manager, err := RegisterDefault("test-public-share") + if err != nil { + t.Fatalf("Failed to register manager: %v", err) + } + + // Test 1: Upload with public=true and share=team + t.Run("PublicTeamShare", func(t *testing.T) { + content := "Public team shared file" + reader := strings.NewReader(content) + + fileHeader := &FileHeader{ + FileHeader: &multipart.FileHeader{ + Filename: "public-team.txt", + Size: int64(len(content)), + Header: make(map[string][]string), + }, + } + fileHeader.Header.Set("Content-Type", "text/plain") + + option := UploadOption{ + Groups: []string{"test"}, + OriginalFilename: "public-team.txt", + Public: true, + Share: "team", + } + + file, err := manager.Upload(context.Background(), fileHeader, reader, option) + if err != nil { + t.Fatalf("Failed to upload public team file: %v", err) + } + + // Verify in database + m := model.Select("__yao.attachment") + records, err := m.Get(model.QueryParam{ + Wheres: []model.QueryWhere{ + {Column: "file_id", Value: file.ID}, + }, + }) + + if err != nil { + t.Fatalf("Failed to query database: %v", err) + } + + if len(records) == 0 { + t.Fatal("No record found in database") + } + + // Debug: print all fields + t.Logf("Record fields: %+v", records[0]) + + publicValue := toBool(records[0]["public"]) + if !publicValue { + t.Errorf("Expected public to be true, got: %v (type: %T)", records[0]["public"], records[0]["public"]) + } + + shareValue := toString(records[0]["share"]) + if shareValue != "team" { + t.Errorf("Expected share to be 'team', got: %v (type: %T)", records[0]["share"], records[0]["share"]) + } + }) + + // Test 2: Upload with public=false and share=private (default) + t.Run("PrivateShare", func(t *testing.T) { + content := "Private file" + reader := strings.NewReader(content) + + fileHeader := &FileHeader{ + FileHeader: &multipart.FileHeader{ + Filename: "private.txt", + Size: int64(len(content)), + Header: make(map[string][]string), + }, + } + fileHeader.Header.Set("Content-Type", "text/plain") + + option := UploadOption{ + Groups: []string{"test"}, + OriginalFilename: "private.txt", + Public: false, + Share: "private", + } + + file, err := manager.Upload(context.Background(), fileHeader, reader, option) + if err != nil { + t.Fatalf("Failed to upload private file: %v", err) + } + + // Verify in database + m := model.Select("__yao.attachment") + records, err := m.Get(model.QueryParam{ + Select: []interface{}{"public", "share"}, + Wheres: []model.QueryWhere{ + {Column: "file_id", Value: file.ID}, + }, + }) + + if err != nil { + t.Fatalf("Failed to query database: %v", err) + } + + if len(records) == 0 { + t.Fatal("No record found in database") + } + + publicValue := toBool(records[0]["public"]) + if publicValue { + t.Errorf("Expected public to be false, got: %v", records[0]["public"]) + } + + shareValue := toString(records[0]["share"]) + if shareValue != "private" { + t.Errorf("Expected share to be 'private', got: %v", records[0]["share"]) + } + }) + + // Test 3: Upload without specifying share (should default to private) + t.Run("DefaultSharePrivate", func(t *testing.T) { + content := "Default share file" + reader := strings.NewReader(content) + + fileHeader := &FileHeader{ + FileHeader: &multipart.FileHeader{ + Filename: "default-share.txt", + Size: int64(len(content)), + Header: make(map[string][]string), + }, + } + fileHeader.Header.Set("Content-Type", "text/plain") + + option := UploadOption{ + Groups: []string{"test"}, + OriginalFilename: "default-share.txt", + Public: false, + // Share not specified, should default to "private" + } + + file, err := manager.Upload(context.Background(), fileHeader, reader, option) + if err != nil { + t.Fatalf("Failed to upload file with default share: %v", err) + } + + // Verify in database + m := model.Select("__yao.attachment") + records, err := m.Get(model.QueryParam{ + Select: []interface{}{"share"}, + Wheres: []model.QueryWhere{ + {Column: "file_id", Value: file.ID}, + }, + }) + + if err != nil { + t.Fatalf("Failed to query database: %v", err) + } + + if len(records) == 0 { + t.Fatal("No record found in database") + } + + shareValue := toString(records[0]["share"]) + if shareValue != "private" { + t.Errorf("Expected default share to be 'private', got: %v", records[0]["share"]) + } + }) +} + +func TestYaoPermissionFields(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + + // Force re-migrate the attachment table to ensure schema is up to date + m := model.Select("__yao.attachment") + if m != nil { + // Drop and recreate table to get latest schema + err := m.DropTable() + if err != nil { + t.Logf("Warning: failed to drop table: %v", err) + } + err = m.Migrate(false) + if err != nil { + t.Fatalf("Failed to migrate table: %v", err) + } + } + + manager, err := RegisterDefault("test-yao-permission") + if err != nil { + t.Fatalf("Failed to register manager: %v", err) + } + + // Test 1: Upload with all Yao permission fields + t.Run("AllYaoFields", func(t *testing.T) { + content := "File with all Yao permission fields" + reader := strings.NewReader(content) + + fileHeader := &FileHeader{ + FileHeader: &multipart.FileHeader{ + Filename: "yao-all-fields.txt", + Size: int64(len(content)), + Header: make(map[string][]string), + }, + } + fileHeader.Header.Set("Content-Type", "text/plain") + + option := UploadOption{ + Groups: []string{"test"}, + OriginalFilename: "yao-all-fields.txt", + YaoCreatedBy: "user123", + YaoUpdatedBy: "user123", + YaoTeamID: "team456", + YaoTenantID: "tenant789", + } + + file, err := manager.Upload(context.Background(), fileHeader, reader, option) + if err != nil { + t.Fatalf("Failed to upload file with Yao fields: %v", err) + } + + // Verify in database + m := model.Select("__yao.attachment") + records, err := m.Get(model.QueryParam{ + Select: []interface{}{"__yao_created_by", "__yao_updated_by", "__yao_team_id", "__yao_tenant_id"}, + Wheres: []model.QueryWhere{ + {Column: "file_id", Value: file.ID}, + }, + }) + + if err != nil { + t.Fatalf("Failed to query database: %v", err) + } + + if len(records) == 0 { + t.Fatal("No record found in database") + } + + // Verify __yao_created_by + createdBy := toString(records[0]["__yao_created_by"]) + if createdBy != "user123" { + t.Errorf("Expected __yao_created_by to be 'user123', got: %v", records[0]["__yao_created_by"]) + } + + // Verify __yao_updated_by + updatedBy := toString(records[0]["__yao_updated_by"]) + if updatedBy != "user123" { + t.Errorf("Expected __yao_updated_by to be 'user123', got: %v", records[0]["__yao_updated_by"]) + } + + // Verify __yao_team_id + teamID := toString(records[0]["__yao_team_id"]) + if teamID != "team456" { + t.Errorf("Expected __yao_team_id to be 'team456', got: %v", records[0]["__yao_team_id"]) + } + + // Verify __yao_tenant_id + tenantID := toString(records[0]["__yao_tenant_id"]) + if tenantID != "tenant789" { + t.Errorf("Expected __yao_tenant_id to be 'tenant789', got: %v", records[0]["__yao_tenant_id"]) + } + }) + + // Test 2: Upload with partial Yao fields (only team and tenant) + t.Run("PartialYaoFields", func(t *testing.T) { + content := "File with partial Yao fields" + reader := strings.NewReader(content) + + fileHeader := &FileHeader{ + FileHeader: &multipart.FileHeader{ + Filename: "yao-partial-fields.txt", + Size: int64(len(content)), + Header: make(map[string][]string), + }, + } + fileHeader.Header.Set("Content-Type", "text/plain") + + option := UploadOption{ + Groups: []string{"test"}, + OriginalFilename: "yao-partial-fields.txt", + YaoTeamID: "team999", + YaoTenantID: "tenant888", + // YaoCreatedBy and YaoUpdatedBy not specified + } + + file, err := manager.Upload(context.Background(), fileHeader, reader, option) + if err != nil { + t.Fatalf("Failed to upload file with partial Yao fields: %v", err) + } + + // Verify in database + m := model.Select("__yao.attachment") + records, err := m.Get(model.QueryParam{ + Select: []interface{}{"__yao_team_id", "__yao_tenant_id"}, + Wheres: []model.QueryWhere{ + {Column: "file_id", Value: file.ID}, + }, + }) + + if err != nil { + t.Fatalf("Failed to query database: %v", err) + } + + if len(records) == 0 { + t.Fatal("No record found in database") + } + + // Verify __yao_team_id + teamID := toString(records[0]["__yao_team_id"]) + if teamID != "team999" { + t.Errorf("Expected __yao_team_id to be 'team999', got: %v", records[0]["__yao_team_id"]) + } + + // Verify __yao_tenant_id + tenantID := toString(records[0]["__yao_tenant_id"]) + if tenantID != "tenant888" { + t.Errorf("Expected __yao_tenant_id to be 'tenant888', got: %v", records[0]["__yao_tenant_id"]) + } + }) + + // Test 3: Upload without Yao fields (should be null/empty in database) + t.Run("NoYaoFields", func(t *testing.T) { + content := "File without Yao fields" + reader := strings.NewReader(content) + + fileHeader := &FileHeader{ + FileHeader: &multipart.FileHeader{ + Filename: "yao-no-fields.txt", + Size: int64(len(content)), + Header: make(map[string][]string), + }, + } + fileHeader.Header.Set("Content-Type", "text/plain") + + option := UploadOption{ + Groups: []string{"test"}, + OriginalFilename: "yao-no-fields.txt", + // No Yao fields specified + } + + file, err := manager.Upload(context.Background(), fileHeader, reader, option) + if err != nil { + t.Fatalf("Failed to upload file without Yao fields: %v", err) + } + + // Should succeed without errors + if file.ID == "" { + t.Error("File ID should not be empty") + } + + t.Logf("Successfully uploaded file without Yao fields - ID: %s", file.ID) + }) +} + func TestManagerLocalPath_ValidationFlow(t *testing.T) { test.Prepare(t, config.Conf) defer test.Clean() diff --git a/attachment/types.go b/attachment/types.go index 99c9232c..b125174b 100644 --- a/attachment/types.go +++ b/attachment/types.go @@ -5,6 +5,7 @@ import ( "io" "mime/multipart" + "github.com/yaoapp/gou/model" "github.com/yaoapp/gou/types" ) @@ -58,6 +59,13 @@ type File struct { Filename string `json:"filename"` ContentType string `json:"content_type"` Status string `json:"status"` // uploading, uploaded, indexing, indexed, upload_failed, index_failed + + // Permission fields + Public bool `json:"public,omitempty"` // Whether this attachment is shared across all teams + Share string `json:"share,omitempty"` // Attachment sharing scope: "private" or "team" + YaoCreatedBy string `json:"-"` // User who created the attachment (not exposed in JSON) + YaoTeamID string `json:"-"` // Team ID for team-based access control (not exposed in JSON) + YaoTenantID string `json:"-"` // Tenant ID for multi-tenancy support (not exposed in JSON) } // FileResponse represents a file download response @@ -80,9 +88,15 @@ type Attachment struct { UserPath string `json:"user_path,omitempty"` // User-specified complete file path Path string `json:"path,omitempty"` // Actual storage path Groups []string `json:"groups,omitempty"` - Gzip bool `json:"gzip,omitempty"` // Gzip the file, Optional, default is false - ClientID string `json:"client_id,omitempty"` // Client identifier - OpenID string `json:"openid,omitempty"` // OpenID identifier + Gzip bool `json:"gzip,omitempty"` // Gzip the file, Optional, default is false + Public bool `json:"public,omitempty"` // Whether this attachment is shared across all teams in the platform + Share string `json:"share,omitempty"` // Attachment sharing scope: "private" or "team" + + // Yao custom fields for permission control + YaoCreatedBy string `json:"__yao_created_by,omitempty"` // User who created the attachment + YaoUpdatedBy string `json:"__yao_updated_by,omitempty"` // User who last updated the attachment + YaoTeamID string `json:"__yao_team_id,omitempty"` // Team ID for team-based access control + YaoTenantID string `json:"__yao_tenant_id,omitempty"` // Tenant ID for multi-tenancy support } // Manager the manager struct @@ -132,8 +146,14 @@ type UploadOption struct { Gzip bool `json:"gzip,omitempty" form:"gzip"` // Gzip the file, Optional, default is false OriginalFilename string `json:"original_filename,omitempty" form:"original_filename"` // Original filename sent separately to avoid encoding issues Groups []string `json:"groups,omitempty" form:"groups"` // Groups, Optional, default is empty, Multi-level groups like ["user", "user123", "chat", "chat456"] - ClientID string `json:"client_id,omitempty" form:"client_id"` // Client identifier - OpenID string `json:"openid,omitempty" form:"openid"` // OpenID identifier + Public bool `json:"public,omitempty" form:"public"` // Whether this attachment is shared across all teams in the platform + Share string `json:"share,omitempty" form:"share"` // Attachment sharing scope: "private" or "team" + + // Yao custom fields for permission control + YaoCreatedBy string `json:"__yao_created_by,omitempty" form:"__yao_created_by"` // User who created the attachment + YaoUpdatedBy string `json:"__yao_updated_by,omitempty" form:"__yao_updated_by"` // User who last updated the attachment + YaoTeamID string `json:"__yao_team_id,omitempty" form:"__yao_team_id"` // Team ID for team-based access control + YaoTenantID string `json:"__yao_tenant_id,omitempty" form:"__yao_tenant_id"` // Tenant ID for multi-tenancy support } // ListOption defines options for listing files @@ -141,6 +161,7 @@ type ListOption struct { Page int `json:"page,omitempty"` // Page number (1-based), default is 1 PageSize int `json:"page_size,omitempty"` // Page size, default is 20 Filters map[string]interface{} `json:"filters,omitempty"` // Filter conditions, e.g., {"status": "uploaded", "content_type": "image/*"} + Wheres []model.QueryWhere `json:"wheres,omitempty"` // Advanced where clauses for permission filtering OrderBy string `json:"order_by,omitempty"` // Order by field, e.g., "created_at desc", "name asc" Select []string `json:"select,omitempty"` // Fields to select, empty means select all } diff --git a/data/bindata.go b/data/bindata.go index 115f8433..d3c56eb3 100644 --- a/data/bindata.go +++ b/data/bindata.go @@ -319,7 +319,7 @@ func cuiSetupIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/setup/index.html", size: 10, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "cui/setup/index.html", size: 10, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -339,7 +339,7 @@ func cuiV09IndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v0.9/index.html", size: 13, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "cui/v0.9/index.html", size: 13, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -359,7 +359,7 @@ func cuiV10IndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v1.0/index.html", size: 49, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "cui/v1.0/index.html", size: 49, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -379,7 +379,7 @@ func cuiV10Layouts__indexAsyncJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v1.0/layouts__index.async.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "cui/v1.0/layouts__index.async.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -399,7 +399,7 @@ func cuiV10UmiJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cui/v1.0/umi.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "cui/v1.0/umi.js", size: 71, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -419,7 +419,7 @@ func initEnv() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.env", size: 219, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.env", size: 219, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -439,7 +439,7 @@ func initVscodeSettingsJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/settings.json", size: 4666, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/settings.json", size: 4666, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -459,7 +459,7 @@ func initVscodeTypesRuntimeConsoleDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/console.d.ts", size: 221, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/console.d.ts", size: 221, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -479,7 +479,7 @@ func initVscodeTypesRuntimeExceptionDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/exception.d.ts", size: 738, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/exception.d.ts", size: 738, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -499,7 +499,7 @@ func initVscodeTypesRuntimeFsDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/fs.d.ts", size: 8554, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/fs.d.ts", size: 8554, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -519,7 +519,7 @@ func initVscodeTypesRuntimeGlobalDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/global.d.ts", size: 1759, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/global.d.ts", size: 1759, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -539,7 +539,7 @@ func initVscodeTypesRuntimeHttpDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/http.d.ts", size: 6179, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/http.d.ts", size: 6179, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -559,7 +559,7 @@ func initVscodeTypesRuntimeIoDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/io.d.ts", size: 587, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/io.d.ts", size: 587, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -579,7 +579,7 @@ func initVscodeTypesRuntimeLogDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/log.d.ts", size: 1692, mode: os.FileMode(493), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/log.d.ts", size: 1692, mode: os.FileMode(493), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -599,7 +599,7 @@ func initVscodeTypesRuntimeNeoDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/neo.d.ts", size: 3750, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/neo.d.ts", size: 3750, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -619,7 +619,7 @@ func initVscodeTypesRuntimeProcessFsDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process/fs.d.ts", size: 11133, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process/fs.d.ts", size: 11133, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -639,7 +639,7 @@ func initVscodeTypesRuntimeProcessHttpDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process/http.d.ts", size: 5653, mode: os.FileMode(493), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process/http.d.ts", size: 5653, mode: os.FileMode(493), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -659,7 +659,7 @@ func initVscodeTypesRuntimeProcessModelDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process/model.d.ts", size: 6656, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process/model.d.ts", size: 6656, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -679,7 +679,7 @@ func initVscodeTypesRuntimeProcessDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/process.d.ts", size: 23165, mode: os.FileMode(493), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/process.d.ts", size: 23165, mode: os.FileMode(493), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -699,7 +699,7 @@ func initVscodeTypesRuntimeQueryDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/query.d.ts", size: 6124, mode: os.FileMode(493), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/query.d.ts", size: 6124, mode: os.FileMode(493), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -719,7 +719,7 @@ func initVscodeTypesRuntimeStoreDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/store.d.ts", size: 2251, mode: os.FileMode(493), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/store.d.ts", size: 2251, mode: os.FileMode(493), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -739,7 +739,7 @@ func initVscodeTypesRuntimeSuiDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/sui.d.ts", size: 1713, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/sui.d.ts", size: 1713, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -759,7 +759,7 @@ func initVscodeTypesRuntimeTimeDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime/time.d.ts", size: 711, mode: os.FileMode(493), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime/time.d.ts", size: 711, mode: os.FileMode(493), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -779,7 +779,7 @@ func initVscodeTypesRuntimeDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/runtime.d.ts", size: 424, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/runtime.d.ts", size: 424, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -799,7 +799,7 @@ func initVscodeTypesSuiDTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/.vscode/types/sui.d.ts", size: 8931, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/.vscode/types/sui.d.ts", size: 8931, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -819,7 +819,7 @@ func initAppYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/app.yao", size: 3115, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/app.yao", size: 3115, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -839,7 +839,7 @@ func initDataReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/README.md", size: 41, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/README.md", size: 41, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -859,7 +859,7 @@ func initDataTemplatesDefault__assetsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -879,7 +879,7 @@ func initDataTemplatesDefault__assetsImagesIconsAppPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -899,7 +899,7 @@ func initDataTemplatesDefault__assetsImagesLogosLogo_colorSvg() (*asset, error) return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -919,7 +919,7 @@ func initDataTemplatesDefault__assetsImagesLogosWordmarkSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -939,7 +939,7 @@ func initDataTemplatesDefault__dataJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__data.json", size: 30, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__data.json", size: 30, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -959,7 +959,7 @@ func initDataTemplatesDefault__documentHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/__document.html", size: 492, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/__document.html", size: 492, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -979,7 +979,7 @@ func initDataTemplatesDefaultIndexIndexCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/index/index.css", size: 2896, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/index/index.css", size: 2896, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -999,7 +999,7 @@ func initDataTemplatesDefaultIndexIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/index/index.html", size: 2361, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/index/index.html", size: 2361, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1019,7 +1019,7 @@ func initDataTemplatesDefaultIndexIndexJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/data/templates/default/index/index.json", size: 31, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/data/templates/default/index/index.json", size: 31, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1039,7 +1039,7 @@ func initDbReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/db/README.md", size: 84, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/db/README.md", size: 84, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1059,7 +1059,7 @@ func initFlowsMenuFlowYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/flows/menu.flow.yao", size: 813, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/flows/menu.flow.yao", size: 813, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1079,7 +1079,7 @@ func initFormsAccountFormYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/forms/account.form.yao", size: 1194, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/forms/account.form.yao", size: 1194, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1099,7 +1099,7 @@ func initIconsAppIcns() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/icons/app.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/icons/app.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1119,7 +1119,7 @@ func initIconsAppIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/icons/app.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/icons/app.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1139,7 +1139,7 @@ func initIconsAppPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1159,7 +1159,7 @@ func initLoginsAdminLoginYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/logins/admin.login.yao", size: 302, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/logins/admin.login.yao", size: 302, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1179,7 +1179,7 @@ func initLogsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/logs/README.md", size: 28, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/logs/README.md", size: 28, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1199,7 +1199,7 @@ func initModelsAdminUserModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/models/admin/user.mod.yao", size: 6416, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/models/admin/user.mod.yao", size: 6416, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1219,7 +1219,7 @@ func initModelsTestsPetModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/models/tests/pet.mod.yao", size: 525, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/models/tests/pet.mod.yao", size: 525, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1239,7 +1239,7 @@ func initNeoNeoYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/neo/neo.yml", size: 724, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/neo/neo.yml", size: 724, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1259,7 +1259,7 @@ func initPublicReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/README.md", size: 108, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/README.md", size: 108, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1279,7 +1279,7 @@ func initPublicAssetsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/assets/README.md", size: 33, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1299,7 +1299,7 @@ func initPublicAssetsImagesIconsAppPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/assets/images/icons/app.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1319,7 +1319,7 @@ func initPublicAssetsImagesLogosLogo_colorSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/assets/images/logos/logo_color.svg", size: 2909, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1339,7 +1339,7 @@ func initPublicAssetsImagesLogosWordmarkSvg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/assets/images/logos/wordmark.svg", size: 3615, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1359,7 +1359,7 @@ func initPublicAssetsLibsuiMinJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/libsui.min.js", size: 12569, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/assets/libsui.min.js", size: 12569, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1379,7 +1379,7 @@ func initPublicAssetsLibsuiMinJsMap() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/assets/libsui.min.js.map", size: 38553, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/assets/libsui.min.js.map", size: 38553, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1399,7 +1399,7 @@ func initPublicIndexCfg() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/index.cfg", size: 85, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/index.cfg", size: 85, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1419,7 +1419,7 @@ func initPublicIndexSui() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/public/index.sui", size: 5682, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/public/index.sui", size: 5682, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1439,7 +1439,7 @@ func initScriptsAccountTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/account.ts", size: 2521, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/scripts/account.ts", size: 2521, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1459,7 +1459,7 @@ func initScriptsAiNeoTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/ai/neo.ts", size: 375, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/scripts/ai/neo.ts", size: 375, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1479,7 +1479,7 @@ func initScriptsTestsTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/tests.ts", size: 1044, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/scripts/tests.ts", size: 1044, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1499,7 +1499,7 @@ func initScriptsUtilsTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/scripts/utils.ts", size: 1230, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/scripts/utils.ts", size: 1230, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1519,7 +1519,7 @@ func initSuisWebSuiYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/suis/web.sui.yao", size: 675, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/suis/web.sui.yao", size: 675, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1539,7 +1539,7 @@ func initTablesAccountTabYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/tables/account.tab.yao", size: 5597, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/tables/account.tab.yao", size: 5597, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1559,7 +1559,7 @@ func initTsconfigJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "init/tsconfig.json", size: 178, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "init/tsconfig.json", size: 178, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1579,7 +1579,7 @@ func libsuiAgentTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/agent.ts", size: 15267, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "libsui/agent.ts", size: 15267, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1599,7 +1599,7 @@ func libsuiIndexTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/index.ts", size: 13049, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "libsui/index.ts", size: 13049, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1619,7 +1619,7 @@ func libsuiUtilsTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/utils.ts", size: 5959, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "libsui/utils.ts", size: 5959, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1639,7 +1639,7 @@ func libsuiYaoTs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "libsui/yao.ts", size: 4338, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "libsui/yao.ts", size: 4338, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1659,7 +1659,7 @@ func publicIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "public/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "public/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1679,7 +1679,7 @@ func uiIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "ui/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "ui/index.html", size: 11, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1699,7 +1699,7 @@ func yaoDataIcons404Png() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1719,7 +1719,7 @@ func yaoDataIconsIconIcns() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.icns", size: 67465, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1739,7 +1739,7 @@ func yaoDataIconsIconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.ico", size: 54993, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1759,7 +1759,7 @@ func yaoDataIconsIconPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/icons/icon.png", size: 34558, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1779,7 +1779,7 @@ func yaoDataIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/index.html", size: 282, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1799,7 +1799,7 @@ func yaoDataKbProvidersChunkingSemanticEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/en.json", size: 5543, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/en.json", size: 5543, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1819,7 +1819,7 @@ func yaoDataKbProvidersChunkingSemanticZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/zh-cn.json", size: 5446, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/semantic/zh-cn.json", size: 5446, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1839,7 +1839,7 @@ func yaoDataKbProvidersChunkingStructuredEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/en.json", size: 2423, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/en.json", size: 2423, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1859,7 +1859,7 @@ func yaoDataKbProvidersChunkingStructuredZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/zh-cn.json", size: 2321, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/chunking/structured/zh-cn.json", size: 2321, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1879,7 +1879,7 @@ func yaoDataKbProvidersConverterMcpEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/en.json", size: 4235, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/en.json", size: 4235, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1899,7 +1899,7 @@ func yaoDataKbProvidersConverterMcpZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/zh-cn.json", size: 4060, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/mcp/zh-cn.json", size: 4060, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1919,7 +1919,7 @@ func yaoDataKbProvidersConverterOcrEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/en.json", size: 6631, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/en.json", size: 6631, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1939,7 +1939,7 @@ func yaoDataKbProvidersConverterOcrZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/zh-cn.json", size: 6501, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/ocr/zh-cn.json", size: 6501, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1959,7 +1959,7 @@ func yaoDataKbProvidersConverterOfficeEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/en.json", size: 5476, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/en.json", size: 5476, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1979,7 +1979,7 @@ func yaoDataKbProvidersConverterOfficeZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/zh-cn.json", size: 5356, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/office/zh-cn.json", size: 5356, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1999,7 +1999,7 @@ func yaoDataKbProvidersConverterUtf8EnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/en.json", size: 292, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/en.json", size: 292, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2019,7 +2019,7 @@ func yaoDataKbProvidersConverterUtf8ZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/zh-cn.json", size: 281, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/utf8/zh-cn.json", size: 281, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2039,7 +2039,7 @@ func yaoDataKbProvidersConverterVideoEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/en.json", size: 6411, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/en.json", size: 6411, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2059,7 +2059,7 @@ func yaoDataKbProvidersConverterVideoZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/zh-cn.json", size: 6297, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/video/zh-cn.json", size: 6297, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2079,7 +2079,7 @@ func yaoDataKbProvidersConverterVisionEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/en.json", size: 4085, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/en.json", size: 4085, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2099,7 +2099,7 @@ func yaoDataKbProvidersConverterVisionZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/zh-cn.json", size: 3949, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/vision/zh-cn.json", size: 3949, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2119,7 +2119,7 @@ func yaoDataKbProvidersConverterWhisperEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/en.json", size: 4449, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/en.json", size: 4449, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2139,7 +2139,7 @@ func yaoDataKbProvidersConverterWhisperZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/zh-cn.json", size: 4312, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/converter/whisper/zh-cn.json", size: 4312, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2159,7 +2159,7 @@ func yaoDataKbProvidersEmbeddingFastembedEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/en.json", size: 6865, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/en.json", size: 6865, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2179,7 +2179,7 @@ func yaoDataKbProvidersEmbeddingFastembedZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/zh-cn.json", size: 6685, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/fastembed/zh-cn.json", size: 6685, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2199,7 +2199,7 @@ func yaoDataKbProvidersEmbeddingOpenaiEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/en.json", size: 5636, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/en.json", size: 5636, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2219,7 +2219,7 @@ func yaoDataKbProvidersEmbeddingOpenaiZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/zh-cn.json", size: 5463, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/embedding/openai/zh-cn.json", size: 5463, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2239,7 +2239,7 @@ func yaoDataKbProvidersExtractionOpenaiEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/en.json", size: 9110, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/en.json", size: 9110, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2259,7 +2259,7 @@ func yaoDataKbProvidersExtractionOpenaiZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/zh-cn.json", size: 8827, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/extraction/openai/zh-cn.json", size: 8827, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2279,7 +2279,7 @@ func yaoDataKbProvidersFetcherHttpEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/en.json", size: 5885, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/en.json", size: 5885, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2299,7 +2299,7 @@ func yaoDataKbProvidersFetcherHttpZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/zh-cn.json", size: 5925, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/http/zh-cn.json", size: 5925, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2319,7 +2319,7 @@ func yaoDataKbProvidersFetcherMcpEnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/en.json", size: 6819, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/en.json", size: 6819, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2339,7 +2339,7 @@ func yaoDataKbProvidersFetcherMcpZhCnJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/zh-cn.json", size: 6611, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/data/kb/providers/fetcher/mcp/zh-cn.json", size: 6611, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2359,7 +2359,7 @@ func yaoFieldsModelTransJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/fields/model.trans.json", size: 14938, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/fields/model.trans.json", size: 14938, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2379,7 +2379,7 @@ func yaoLangsEnUsJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/en-US.json", size: 66, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2399,7 +2399,7 @@ func yaoLangsZhCnGlobalYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-cn/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/zh-cn/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2419,7 +2419,7 @@ func yaoLangsZhCnLoginsAdminLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-cn/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/zh-cn/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2439,7 +2439,7 @@ func yaoLangsZhCnLoginsUserLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-cn/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/zh-cn/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2459,7 +2459,7 @@ func yaoLangsZhHkGlobalYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-hk/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/zh-hk/global.yml", size: 1762, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2479,7 +2479,7 @@ func yaoLangsZhHkLoginsAdminLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-hk/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/zh-hk/logins/admin.login.yml", size: 94, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2499,7 +2499,7 @@ func yaoLangsZhHkLoginsUserLoginYml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/langs/zh-hk/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/langs/zh-hk/logins/user.login.yml", size: 90, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2519,7 +2519,7 @@ func yaoModelsAgentAssistantModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/agent/assistant.mod.yao", size: 4651, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/agent/assistant.mod.yao", size: 4651, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2539,7 +2539,7 @@ func yaoModelsAgentChatModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/agent/chat.mod.yao", size: 2110, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/agent/chat.mod.yao", size: 2110, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2559,12 +2559,12 @@ func yaoModelsAgentHistoryModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/agent/history.mod.yao", size: 4022, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/agent/history.mod.yao", size: 4022, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _yaoModelsAttachmentModYao = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x57\x5f\x6f\xd3\x3e\x14\x7d\xef\xa7\xb8\xca\xf3\x7e\xd2\x7e\x48\x43\x6a\xdf\xc6\x06\x68\x12\x82\x09\x98\x78\x98\xa6\xca\x4d\x6e\x52\x23\xc7\x0e\xf6\x8d\xa0\x9b\xfa\xdd\x91\x9d\xa4\xb5\x53\xa7\x5d\x02\xe3\xa9\xea\xfd\x73\x7c\xce\xf5\xb5\x7d\xf3\x34\x03\x48\x24\x2b\x31\x59\x40\xc2\x88\x58\xba\x2e\x51\x52\x72\x66\xed\x82\xad\x50\x58\xc7\x65\xcf\x91\xa1\x49\x35\xaf\x88\x2b\x19\xba\x81\xd8\x4a\x20\xe4\x4a\x83\x21\xa5\xb9\x2c\x20\xe7\x02\x61\x8f\x6c\xe0\x27\xa7\x35\x94\x48\x2c\x63\xc4\x80\xc9\x0c\x58\x9a\xa2\x31\x90\x2a\x49\x5a\x89\x66\x09\x62\x85\x49\x16\x70\x9f\x98\x8d\x21\x2c\x93\x07\x67\x5d\xd5\x5c\x10\xb7\x8b\x92\xae\xd1\x99\x34\xb2\x4c\x49\xb1\xf1\x6d\x46\x69\x4a\x16\x30\x9f\xcf\xe7\x2d\xd8\x4a\x58\x85\x56\xed\xb0\x5e\x80\x24\x55\xa5\xfb\x1b\x11\x95\xcc\x00\xb6\x0e\x2d\x55\xa2\x2e\xa5\x63\xe7\xb2\x1a\x54\x0f\x97\x67\x2d\x9e\x5d\x7a\x53\x39\xdb\xcd\xf5\xde\x16\xa9\x2b\xf8\x7e\x8f\xc5\x9d\xe4\x3f\x6a\xbf\x7e\xc0\x33\x94\xc4\x73\x8e\x3a\x71\xf1\xdb\xb3\x38\x09\x5b\xf7\x65\x8c\x89\x21\xbb\x2f\x11\x36\xef\xec\x4e\x0d\xf0\x70\x3e\x6f\xe9\x7d\x36\xca\x82\xd6\xc9\x02\x5e\x5d\x5c\xec\x8c\xb2\x16\xa2\x2d\x79\xce\x84\xc1\x9d\xa3\x76\x72\xbc\xad\x72\x56\x2e\x33\xfc\xd5\x1a\x8f\x6a\xaa\x2b\xa1\x58\xe6\x2f\x7f\x52\xd4\xdd\x41\x4a\x5f\x55\x07\x0a\x0e\x2b\x22\xec\xfc\xfc\xb4\xb0\x67\x4b\xb0\x4d\x8e\x92\x96\xe1\x62\x27\x65\x5c\x35\x69\xf0\x35\x48\xeb\x4b\x69\xc1\xff\x8d\x12\xf7\xfb\x7c\x05\x1f\x83\xf0\x3e\xf3\x10\x6c\xc7\xf8\xe2\xaf\x32\xae\xb5\x18\xd3\x39\x9f\x3f\x0c\xf3\x0d\x9c\x3b\xba\xff\x9f\xc7\xf9\x46\xbb\xdd\x89\x38\xca\xd7\xbf\x66\x9f\xcf\xfb\x3a\x96\xd5\xe7\x1f\x85\x7e\x29\x1d\x23\x7b\xfd\x78\x8f\x8f\xeb\xed\x89\xf7\x8c\x41\xbd\xac\x18\xad\xc7\xb4\x8b\x41\x0d\xb7\x41\x8e\x7f\x8f\x1b\xd4\xff\x99\x0a\x53\x7b\x7d\x66\x90\xaa\xb2\x12\x48\xd8\xbc\x8e\xe1\x4a\xd3\x76\xe1\xa4\xa6\x91\x72\xbe\x90\xd2\xac\xc0\x61\x45\x97\x29\xd5\x4c\xb8\x67\xde\xc6\x59\x78\xf7\xee\xd3\xba\x51\x35\x42\xd0\xc4\xe3\x5c\x68\x55\x57\xe6\x50\xd3\x77\x13\x34\x75\xa7\xe8\x7d\x2f\xbc\xdf\x58\x7d\xb8\x5e\xc5\x8f\x53\x79\xe4\xd5\x21\x91\x95\x52\x02\x59\x94\x4b\x10\xef\x31\xf9\xb6\x46\x5a\xa3\x6e\xfa\x82\x1b\xb0\xc0\x15\x7a\xaf\x78\x86\x39\xab\x05\x4d\xaf\xda\x6a\x43\x18\x29\xda\x8a\x17\x37\x92\xb0\x08\xde\xf6\x8e\xee\x9b\x30\xa7\x5f\x39\xc3\x1f\x11\xb8\x84\x1e\xf4\x9f\xef\xb0\x21\x46\x75\x84\x2c\xca\xba\x8c\xf6\x6c\x18\xde\xe7\x59\x69\x65\x07\x4d\x3b\x95\xf6\x91\x55\x37\xcc\xde\xb7\x16\xe8\xa6\x0d\xff\x78\xec\x8c\xde\x8e\x74\x7a\x7a\x71\xce\x16\x86\x35\xa9\xcb\x9c\x71\x11\xc9\xef\xec\xad\xf9\x21\xb2\xe3\x11\x46\x23\xce\xbf\x56\x85\x46\x13\xa9\xe6\xe0\x1d\x70\x7b\x90\xe2\x55\xf4\x76\x5f\xcc\x0e\x1a\xb8\xcc\x95\x2e\xd9\xc0\xab\x72\xe4\x8a\x3e\xca\x1c\xb5\x56\x63\x46\xbe\xb7\x61\xbc\xc7\xd9\x79\x4e\xb0\x7c\x3d\x91\x65\x2a\xb8\x9d\xeb\x46\x4d\xdc\x57\x2e\x67\x68\xe6\x6e\xbd\x53\xa6\xee\x69\x4f\x84\xaa\x50\x8e\xe2\xff\xa9\x42\x39\x40\xbe\x71\xbd\x04\xf9\x59\x7b\x38\x12\x8d\xc2\x6d\xa2\xfd\x12\x7b\x6a\x3e\xcd\x9a\x53\xe7\x3e\xcd\x9a\x98\xdd\xb9\x7e\x82\x84\x78\x89\x86\x58\x59\x99\x6e\x11\xfb\xa5\x98\xd3\x32\x43\xfb\x16\x9b\xee\x9e\x82\xed\x6c\x3b\xfb\x1d\x00\x00\xff\xff\x6d\x48\xa5\x86\x19\x0f\x00\x00") +var _yaoModelsAttachmentModYao = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x57\x51\x8f\xe3\x34\x10\x7e\xef\xaf\x18\xf9\x79\xd1\x2d\x48\x8b\xb4\xfb\xb6\xdc\x01\x3a\x09\xc1\x0a\x38\x78\x38\x9d\x2a\x27\x99\x36\x46\x8e\x1d\x3c\x93\x3b\x7a\xab\xfd\xef\xc8\x4e\xd2\x3a\xae\x9b\x6e\xba\x70\x4f\x55\xc7\x33\x9f\xbf\x6f\x3c\x19\x7b\x1e\x57\x00\xc2\xc8\x06\xc5\x1d\x08\xc9\x2c\xcb\xba\x41\xc3\xe2\xca\xdb\xb5\x2c\x50\xfb\x85\xfb\x64\xa1\x42\x2a\x9d\x6a\x59\x59\x33\x5d\x06\x96\x85\x46\xd8\x58\x07\xc4\xd6\x29\xb3\x85\x8d\xd2\x08\x07\x64\x82\x4f\x8a\x6b\x68\x90\x65\x25\x59\x82\x34\x15\xc8\xb2\x44\x22\x28\xad\x61\x67\x75\xbf\x05\xcb\x2d\x89\x3b\x78\x2f\x68\x47\x8c\x8d\xf8\x10\xac\x45\xa7\x34\x2b\xbf\x29\xbb\x0e\x83\xc9\xa1\xac\xac\xd1\xbb\xd8\x46\xd6\xb1\xb8\x83\xdb\xdb\xdb\xdb\x01\xac\xd0\x5e\xa1\x57\x7b\x5a\x2f\x80\x28\x6d\x13\xfe\x66\x44\x89\x15\xc0\x53\x40\x2b\xad\xee\x1a\x13\xd8\x85\xa8\x1e\x35\xc2\x55\xd5\x80\xe7\xb7\xde\xb5\xc1\xf6\xf6\xcd\xc1\x96\xc9\x2b\xc4\xeb\x11\x8b\x77\x46\xfd\xdd\xc5\xf9\x03\x55\xa1\x61\xb5\x51\xe8\x44\xf0\x7f\xba\xca\x93\xf0\x79\x5f\xe7\x98\x10\xfb\x73\xc9\xb0\xf9\xc1\x9f\xd4\x09\x1e\x61\x2d\xda\xfa\x10\x8d\x66\xcb\xb5\xb8\x83\x6f\x6e\x6e\xf6\x46\xd3\x69\x3d\xa4\x7c\x23\x35\xe1\x7e\xa1\x0b\x72\xa2\xa3\x0a\x56\x65\x2a\xfc\x67\x30\xce\x6a\xea\x5a\x6d\x65\x15\x6f\x7f\x56\xd4\xbb\xa3\x90\x54\xd5\x08\x0a\x01\x2b\x23\xec\xfa\xfa\xbc\xb0\x67\x4b\xf0\x45\x8e\x86\xd7\xd3\xcd\xce\xca\x78\xdd\x87\xc1\xef\x93\xb0\x54\xca\x00\xfe\x65\x94\x84\xdf\xe7\x2b\xf8\x79\xe2\x9e\x32\x9f\x82\xed\x19\xdf\xfc\xa7\x8c\x3b\xa7\x97\x54\xce\xaf\x3f\x9d\xe6\x3b\x59\xdc\xd3\xfd\xfa\x3a\xcf\x37\x5b\xed\x41\xc4\x2c\xdf\xb8\xcd\x3e\x9f\xf7\x9b\x5c\x54\xca\x3f\x0b\xfd\x7f\xe9\x58\x58\xeb\xf3\x35\xbe\xac\xb6\x2f\xec\x33\x84\x6e\xdd\x4a\xae\x97\x94\x0b\xa1\x83\x87\x49\x4c\xdc\xc7\x09\xdd\x57\xd4\x62\xe9\xdb\x67\x05\xa5\x6d\x5a\x8d\x8c\xfd\xed\x38\xdd\xe9\xb2\x53\x38\xab\x69\xa1\x9c\xdf\xd8\x3a\xb9\xc5\xd3\x8a\xee\x4b\xee\xa4\x0e\xd7\xbc\xf7\xf3\xf0\xe1\xde\xe7\xba\x57\xb5\x40\xd0\x85\x9f\xf3\xd6\xd9\xae\xa5\x63\x4d\x7f\xd1\xa4\xa8\x47\x45\x3f\x26\xee\x69\x61\xa5\x70\x49\xc6\xe7\xa9\x7c\x56\xed\x31\x91\xc2\x5a\x8d\x32\xcb\x65\xe2\x1f\x31\xf9\xb3\x46\xae\xd1\xf5\x75\xa1\x08\x3c\x70\x8b\xd1\x2d\x5e\xe1\x46\x76\x9a\x2f\xcf\x5a\xb1\x63\xcc\x24\xad\x50\xdb\xb7\x86\x71\x3b\xb9\xdb\x47\xba\xdf\x4d\x63\xd2\xcc\x91\xfa\x8c\xa0\x0c\x24\xd0\x2f\x3f\x61\x62\xc9\x5d\x86\x2c\x9a\xae\xc9\xd6\xec\xd4\x3d\xe5\xd9\x3a\xeb\x1f\x9a\xfe\x55\x9a\x22\xdb\xf1\x31\xfb\x7e\xb0\xc0\xf8\xda\x88\x3f\x8f\xbd\x31\x3a\x91\x51\x4f\xe2\x17\x6c\x53\xb7\x3e\x74\xbd\x91\x4a\x67\xe2\x47\xfb\x60\xfe\x90\x39\xf1\x0c\xa3\x05\xdf\xbf\xb3\x5b\x87\x94\xc9\xe6\xc9\x1e\xf0\x70\x14\x12\x65\xf4\xe1\x90\xcc\x11\x1a\x94\xd9\x58\xd7\xc8\x13\xb7\xca\x4c\x8b\x9e\x65\x8e\xce\xd9\x25\x4f\xbe\xef\xa7\xfe\x11\xe7\xb0\x72\x86\xe5\xb7\xe7\x59\x9e\x4a\x30\x12\xf2\x92\x2e\xf0\x10\x22\xe0\x3e\x1d\x43\xf2\x2d\x81\x6b\x45\xbe\x25\x48\xe8\x77\x82\xa3\xf1\x65\xa6\x3d\xa4\xdf\xe2\x19\x29\x5d\xa1\x55\xb9\x48\x4a\x88\x58\x24\x25\x9e\x69\x08\xa8\x96\x0e\xfd\x28\xe8\x2c\x11\x48\xad\x81\x51\x36\xbe\xa4\xc2\x8d\xd2\x6a\xc9\xfe\xd4\x5e\xa8\xf4\xd5\x2b\x78\xdd\x11\xdb\x06\x5a\x74\x8d\x22\x52\xd6\xd0\x89\xde\xe3\x09\x3d\xbf\xf5\x4c\xbd\xf3\x73\xa4\x87\x0c\xcd\xa7\xb4\xf1\x1b\x26\xd7\x7b\x5a\xa7\x3e\x4a\x46\x71\xe5\x29\xff\x62\xf4\x0e\x3e\x2a\x52\x7e\xb6\x66\x1b\x12\x62\x3f\x19\x74\x07\x7f\x9f\x2c\xe1\x7d\xff\x38\xb8\x8d\x49\x84\x06\x9b\x02\x1d\xcd\x75\x96\xfd\x7e\x17\x34\xef\xd5\x80\x29\x1c\xea\xf0\x55\xf9\xd1\xf8\xb1\x9f\x95\xfb\x36\x18\x66\xe5\xde\x67\x2f\xf6\x11\x04\xab\x06\x89\x65\xd3\xd2\xf8\xaa\xf1\xa3\xfb\x86\xd7\x15\xfa\xc7\x11\xed\xf7\x06\x71\x38\xae\xc1\x15\x9e\x56\x4f\xab\x7f\x03\x00\x00\xff\xff\xfc\x82\x4c\x81\xbe\x10\x00\x00") func yaoModelsAttachmentModYaoBytes() ([]byte, error) { return bindataRead( @@ -2579,7 +2579,7 @@ func yaoModelsAttachmentModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/attachment.mod.yao", size: 3865, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/attachment.mod.yao", size: 4286, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2599,7 +2599,7 @@ func yaoModelsAuditModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/audit.mod.yao", size: 5588, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/audit.mod.yao", size: 5588, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2619,7 +2619,7 @@ func yaoModelsConfigModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/config.mod.yao", size: 1649, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/config.mod.yao", size: 1649, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2639,7 +2639,7 @@ func yaoModelsDslModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/dsl.mod.yao", size: 3826, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/dsl.mod.yao", size: 3826, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2659,7 +2659,7 @@ func yaoModelsInvitationModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/invitation.mod.yao", size: 6693, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/invitation.mod.yao", size: 6693, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2679,7 +2679,7 @@ func yaoModelsJobCategoryModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/category.mod.yao", size: 2041, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/job/category.mod.yao", size: 2041, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2699,7 +2699,7 @@ func yaoModelsJobExecutionModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/execution.mod.yao", size: 7201, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/job/execution.mod.yao", size: 7201, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2719,7 +2719,7 @@ func yaoModelsJobJobModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/job.mod.yao", size: 6330, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/job/job.mod.yao", size: 6330, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2739,7 +2739,7 @@ func yaoModelsJobLogModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/job/log.mod.yao", size: 4711, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/job/log.mod.yao", size: 4711, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2759,7 +2759,7 @@ func yaoModelsKbCollectionModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/kb/collection.mod.yao", size: 5390, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/kb/collection.mod.yao", size: 5390, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2779,7 +2779,7 @@ func yaoModelsKbDocumentModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/kb/document.mod.yao", size: 9906, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/kb/document.mod.yao", size: 9906, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2799,7 +2799,7 @@ func yaoModelsMemberModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/member.mod.yao", size: 14798, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/member.mod.yao", size: 14798, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2819,7 +2819,7 @@ func yaoModelsRoleModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/role.mod.yao", size: 6434, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/role.mod.yao", size: 6434, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2839,7 +2839,7 @@ func yaoModelsTeamModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/team.mod.yao", size: 15823, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/team.mod.yao", size: 15823, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2859,7 +2859,7 @@ func yaoModelsUserOauth_accountModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user/oauth_account.mod.yao", size: 6928, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/user/oauth_account.mod.yao", size: 6928, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2879,7 +2879,7 @@ func yaoModelsUserTypeModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user/type.mod.yao", size: 7502, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/user/type.mod.yao", size: 7502, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2899,7 +2899,7 @@ func yaoModelsUserModYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/models/user.mod.yao", size: 12335, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/models/user.mod.yao", size: 12335, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2919,7 +2919,7 @@ func yaoReleaseAppYaz() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/release/app.yaz", size: 181682, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/release/app.yaz", size: 181682, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2939,7 +2939,7 @@ func yaoStoresAgentCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/agent/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/agent/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2959,7 +2959,7 @@ func yaoStoresAgentMemoryBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/agent/memory.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/agent/memory.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2979,7 +2979,7 @@ func yaoStoresCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/cache.lru.yao", size: 285, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/cache.lru.yao", size: 285, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -2999,7 +2999,7 @@ func yaoStoresKbCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/kb/cache.lru.yao", size: 304, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/kb/cache.lru.yao", size: 304, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3019,7 +3019,7 @@ func yaoStoresKbStoreBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/kb/store.badger.yao", size: 349, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/kb/store.badger.yao", size: 349, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3039,7 +3039,7 @@ func yaoStoresOauthCacheLruYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/oauth/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/oauth/cache.lru.yao", size: 301, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3059,7 +3059,7 @@ func yaoStoresOauthClientBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/oauth/client.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/oauth/client.badger.yao", size: 352, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3079,7 +3079,7 @@ func yaoStoresOauthStoreBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/oauth/store.badger.yao", size: 376, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/oauth/store.badger.yao", size: 376, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3099,7 +3099,7 @@ func yaoStoresStoreBadgerYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/stores/store.badger.yao", size: 341, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/stores/store.badger.yao", size: 341, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -3119,7 +3119,7 @@ func yaoUploadersAttachmentLocalYao() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "yao/uploaders/attachment.local.yao", size: 1163, mode: os.FileMode(420), modTime: time.Unix(1761880418, 0)} + info := bindataFileInfo{name: "yao/uploaders/attachment.local.yao", size: 1163, mode: os.FileMode(420), modTime: time.Unix(1762335157, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/openapi/file/file.go b/openapi/file/file.go index 2e627ba9..64cb90e6 100644 --- a/openapi/file/file.go +++ b/openapi/file/file.go @@ -8,7 +8,9 @@ import ( "strings" "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/model" "github.com/yaoapp/yao/attachment" + "github.com/yaoapp/yao/openapi/oauth/authorized" "github.com/yaoapp/yao/openapi/oauth/types" "github.com/yaoapp/yao/openapi/response" ) @@ -85,61 +87,11 @@ func upload(c *gin.Context) { } defer file.Close() - // Get original filename from form data - originalFilename := c.PostForm("original_filename") - if originalFilename == "" { - originalFilename = fileHeader.Filename - } - - // Get path from form data for user_path - userPath := c.PostForm("path") - if userPath == "" { - userPath = originalFilename - } - - // Parse groups from form data - var groups []string - groupsStr := c.PostForm("groups") - if groupsStr != "" { - groups = strings.Split(groupsStr, ",") - // Trim spaces - for i, group := range groups { - groups[i] = strings.TrimSpace(group) - } - } - // Create upload header from request header := attachment.GetHeader(c.Request.Header, fileHeader.Header, fileHeader.Size) - // Parse gzip option - gzip := false - if gzipStr := c.PostForm("gzip"); gzipStr == "true" { - gzip = true - } - - // Parse compress image options - compressImage := false - if compressImageStr := c.PostForm("compress_image"); compressImageStr == "true" { - compressImage = true - } - - compressSize := 0 - if compressSizeStr := c.PostForm("compress_size"); compressSizeStr != "" { - if size, err := strconv.Atoi(compressSizeStr); err == nil && size > 0 { - compressSize = size - } - } - - // Create upload options - uploadOption := attachment.UploadOption{ - OriginalFilename: originalFilename, // Use original filename from form data - Groups: groups, // Groups for directory structure - ClientID: c.PostForm("client_id"), - OpenID: c.PostForm("openid"), - Gzip: gzip, // Gzip compression - CompressImage: compressImage, // Image compression - CompressSize: compressSize, // Compression size - } + // Create upload options with all parameters parsed from form data + uploadOption := createUploadOption(c, fileHeader.Filename) // Upload the file uploadedFile, err := manager.Upload(c.Request.Context(), header, file, uploadOption) @@ -194,6 +146,9 @@ func list(c *gin.Context) { } } + // Get auth info for permission filtering + authInfo := authorized.GetInfo(c) + // Parse filters filters := make(map[string]interface{}) filters["uploader"] = uploaderID // Always filter by current uploader @@ -208,6 +163,18 @@ func list(c *gin.Context) { filters["name"] = name + "*" // Wildcard search } + // Build where clauses for permission-based filtering + var wheres []model.QueryWhere + + // Add basic filters as where clauses + wheres = append(wheres, model.QueryWhere{ + Column: "uploader", + Value: uploaderID, + }) + + // Apply permission-based filtering + wheres = append(wheres, AuthFilter(c, authInfo)...) + // Parse order by orderBy := c.Query("order_by") if orderBy == "" { @@ -223,11 +190,12 @@ func list(c *gin.Context) { } } - // Create list option + // Create list option with where clauses listOption := attachment.ListOption{ Page: page, PageSize: pageSize, Filters: filters, + Wheres: wheres, OrderBy: orderBy, Select: selectFields, } @@ -281,7 +249,7 @@ func retrieve(c *gin.Context) { return } - // Get file info using the new Info method + // Get file info (includes permission fields) fileInfo, err := manager.Info(c.Request.Context(), fileID) if err != nil { errorResp := &response.ErrorResponse{ @@ -292,6 +260,27 @@ func retrieve(c *gin.Context) { return } + // Check read permission using file info + authInfo := authorized.GetInfo(c) + hasPermission, err := checkFilePermission(authInfo, fileInfo, true) // true = readable mode + if err != nil { + errorResp := &response.ErrorResponse{ + Code: response.ErrServerError.Code, + ErrorDescription: err.Error(), + } + response.RespondWithError(c, response.StatusForbidden, errorResp) + return + } + + if !hasPermission { + errorResp := &response.ErrorResponse{ + Code: response.ErrAccessDenied.Code, + ErrorDescription: "Forbidden: No permission to access file", + } + response.RespondWithError(c, response.StatusForbidden, errorResp) + return + } + // Return the file info response.RespondWithSuccess(c, response.StatusOK, fileInfo) } @@ -330,8 +319,9 @@ func delete(c *gin.Context) { return } - // Check if file exists first - if !manager.Exists(c.Request.Context(), fileID) { + // Get file info first (includes permission fields) + fileInfo, err := manager.Info(c.Request.Context(), fileID) + if err != nil { errorResp := &response.ErrorResponse{ Code: response.ErrInvalidRequest.Code, ErrorDescription: "File not found", @@ -340,8 +330,29 @@ func delete(c *gin.Context) { return } - // Delete the file - err := manager.Delete(c.Request.Context(), fileID) + // Check delete permission using file info (false = write permission required) + authInfo := authorized.GetInfo(c) + hasPermission, err := checkFilePermission(authInfo, fileInfo, false) // false = write permission required + if err != nil { + errorResp := &response.ErrorResponse{ + Code: response.ErrServerError.Code, + ErrorDescription: err.Error(), + } + response.RespondWithError(c, response.StatusInternalServerError, errorResp) + return + } + + if !hasPermission { + errorResp := &response.ErrorResponse{ + Code: response.ErrAccessDenied.Code, + ErrorDescription: "Forbidden: No permission to delete file", + } + response.RespondWithError(c, response.StatusForbidden, errorResp) + return + } + + // Delete the file (permission already checked) + err = manager.Delete(c.Request.Context(), fileID) if err != nil { errorResp := &response.ErrorResponse{ Code: response.ErrServerError.Code, @@ -392,7 +403,7 @@ func content(c *gin.Context) { return } - // Get file info first to obtain metadata + // Get file info (includes permission fields) fileInfo, err := manager.Info(c.Request.Context(), fileID) if err != nil { errorResp := &response.ErrorResponse{ @@ -403,8 +414,29 @@ func content(c *gin.Context) { return } + // Check read permission using file info + authInfo := authorized.GetInfo(c) + hasPermission, err := checkFilePermission(authInfo, fileInfo, true) // true = readable mode + if err != nil { + errorResp := &response.ErrorResponse{ + Code: response.ErrServerError.Code, + ErrorDescription: err.Error(), + } + response.RespondWithError(c, response.StatusForbidden, errorResp) + return + } + + if !hasPermission { + errorResp := &response.ErrorResponse{ + Code: response.ErrAccessDenied.Code, + ErrorDescription: "Forbidden: No permission to access file content", + } + response.RespondWithError(c, response.StatusForbidden, errorResp) + return + } + // Read the file content - content, err := manager.Read(c.Request.Context(), fileID) + fileContent, err := manager.Read(c.Request.Context(), fileID) if err != nil { errorResp := &response.ErrorResponse{ Code: response.ErrServerError.Code, @@ -419,10 +451,10 @@ func content(c *gin.Context) { if fileInfo.Filename != "" { c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileInfo.Filename)) } - c.Header("Content-Length", fmt.Sprintf("%d", len(content))) + c.Header("Content-Length", fmt.Sprintf("%d", len(fileContent))) // Return file content directly - c.Data(http.StatusOK, fileInfo.ContentType, content) + c.Data(http.StatusOK, fileInfo.ContentType, fileContent) } // exists checks if a file exists @@ -468,3 +500,123 @@ func exists(c *gin.Context) { } response.RespondWithSuccess(c, response.StatusOK, successData) } + +// createUploadOption creates an UploadOption from request context and form data +// Parses all upload parameters including auth info, permission fields, and upload options +func createUploadOption(c *gin.Context, defaultFilename string) attachment.UploadOption { + option := attachment.UploadOption{} + + // Parse original filename from form data + originalFilename := c.PostForm("original_filename") + if originalFilename == "" { + originalFilename = defaultFilename + } + option.OriginalFilename = originalFilename + + // Parse groups from form data + if groupsStr := c.PostForm("groups"); groupsStr != "" { + groups := strings.Split(groupsStr, ",") + // Trim spaces from each group + for i, group := range groups { + groups[i] = strings.TrimSpace(group) + } + option.Groups = groups + } + + // Parse gzip option + if gzipStr := c.PostForm("gzip"); gzipStr == "true" || gzipStr == "1" { + option.Gzip = true + } + + // Parse compress image options + if compressImageStr := c.PostForm("compress_image"); compressImageStr == "true" || compressImageStr == "1" { + option.CompressImage = true + } + + // Parse compress size + if compressSizeStr := c.PostForm("compress_size"); compressSizeStr != "" { + if size, err := strconv.Atoi(compressSizeStr); err == nil && size > 0 { + option.CompressSize = size + } + } + + // Extract auth info from context (set by OAuth guard middleware) + authInfo := authorized.GetInfo(c) + if authInfo != nil { + // Set Yao permission fields from authenticated user info + // Note: YaoUpdatedBy is not set on upload (creation), only on update + if authInfo.UserID != "" { + option.YaoCreatedBy = authInfo.UserID + } + if authInfo.TeamID != "" { + option.YaoTeamID = authInfo.TeamID + } + if authInfo.TenantID != "" { + option.YaoTenantID = authInfo.TenantID + } + } + + // Parse public field from form data (user can override) + if publicStr := c.PostForm("public"); publicStr != "" { + if publicStr == "true" || publicStr == "1" { + option.Public = true + } else { + option.Public = false + } + } + + // Parse share field from form data (user can override) + // Valid values: "private", "team" + if shareStr := c.PostForm("share"); shareStr != "" { + shareStr = strings.TrimSpace(strings.ToLower(shareStr)) + if shareStr == "private" || shareStr == "team" { + option.Share = shareStr + } + } + + return option +} + +// checkFilePermission checks if the user has permission to access the file +func checkFilePermission(authInfo *types.AuthorizedInfo, fileInfo *attachment.File, readable ...bool) (bool, error) { + // No auth info, allow access + if authInfo == nil { + return true, nil + } + + // No constraints, allow access + if !authInfo.Constraints.TeamOnly && !authInfo.Constraints.OwnerOnly { + return true, nil + } + + // If readable mode and file is public, allow access + if len(readable) > 0 && readable[0] { + if fileInfo.Public { + return true, nil + } + + // If file is shared with team and user is in the same team, allow access + if fileInfo.Share == "team" && authInfo.Constraints.TeamOnly && fileInfo.YaoTeamID == authInfo.TeamID { + return true, nil + } + } + + // Combined Team and Owner permission validation + if authInfo.Constraints.TeamOnly && authInfo.Constraints.OwnerOnly { + if fileInfo.YaoCreatedBy == authInfo.UserID && fileInfo.YaoTeamID == authInfo.TeamID { + return true, nil + } + } + + // Owner only permission validation + if authInfo.Constraints.OwnerOnly && fileInfo.YaoCreatedBy == authInfo.UserID { + return true, nil + } + + // Team only permission validation + if authInfo.Constraints.TeamOnly && fileInfo.YaoTeamID == authInfo.TeamID { + return true, nil + } + + return false, nil +} diff --git a/openapi/file/filter.go b/openapi/file/filter.go new file mode 100644 index 00000000..eb1ba5ff --- /dev/null +++ b/openapi/file/filter.go @@ -0,0 +1,68 @@ +package file + +import ( + "github.com/gin-gonic/gin" + "github.com/yaoapp/gou/model" + "github.com/yaoapp/yao/openapi/oauth/authorized" + "github.com/yaoapp/yao/openapi/oauth/types" +) + +// AuthFilter applies permission-based filtering to file query wheres +// This function builds where clauses based on the user's authorization constraints +// It supports TeamOnly and OwnerOnly constraints for file access control +// +// Parameters: +// - c: gin.Context containing authorization information +// - authInfo: authorized information extracted from the context +// +// Returns: +// - []model.QueryWhere: array of where clauses to apply to the query +func AuthFilter(c *gin.Context, authInfo *types.AuthorizedInfo) []model.QueryWhere { + if authInfo == nil { + return []model.QueryWhere{} + } + + var wheres []model.QueryWhere + scope := authInfo.AccessScope() + + // Team only - User can access: + // 1. Public files (public = true) + // 2. Files in their team where: + // - They uploaded the file (__yao_created_by matches) + // - OR the file is shared with team (share = "team") + if authInfo.Constraints.TeamOnly && authorized.IsTeamMember(c) { + wheres = append(wheres, model.QueryWhere{ + Wheres: []model.QueryWhere{ + {Column: "public", Value: true, Method: "orwhere"}, + {Wheres: []model.QueryWhere{ + {Column: "__yao_team_id", Value: scope.TeamID}, + {Wheres: []model.QueryWhere{ + {Column: "__yao_created_by", Value: scope.CreatedBy}, + {Column: "share", Value: "team", Method: "orwhere"}, + }}, + }, Method: "orwhere"}, + }, + }) + return wheres + } + + // Owner only - User can access: + // 1. Public files (public = true) + // 2. Files they uploaded where: + // - __yao_team_id is null (not team files) + // - __yao_created_by matches their user ID + if authInfo.Constraints.OwnerOnly && authInfo.UserID != "" { + wheres = append(wheres, model.QueryWhere{ + Wheres: []model.QueryWhere{ + {Column: "public", Value: true, Method: "orwhere"}, + {Wheres: []model.QueryWhere{ + {Column: "__yao_team_id", OP: "null"}, + {Column: "__yao_created_by", Value: scope.CreatedBy}, + }, Method: "orwhere"}, + }, + }) + return wheres + } + + return wheres +} diff --git a/openapi/tests/file/file_test.go b/openapi/tests/file/file_test.go index b625f98f..a543b374 100644 --- a/openapi/tests/file/file_test.go +++ b/openapi/tests/file/file_test.go @@ -132,8 +132,8 @@ func TestFileUpload(t *testing.T) { "original_filename": testFileName, "path": "documents/reports/quarterly-report.txt", "groups": "documents,reports", - "client_id": "test-client", - "openid": "test-user", + "public": "false", + "share": "private", }) assert.NoError(t, err) @@ -1008,3 +1008,96 @@ func TestFileIntegration(t *testing.T) { t.Logf("Completed full file lifecycle test for: %s", testFileID) }) } + +// TestFilePermissionFields tests the new permission and auth fields +func TestFilePermissionFields(t *testing.T) { + serverURL := testutils.Prepare(t) + defer testutils.Clean() + + setupTestUploader(t) + + baseURL := "" + if openapi.Server != nil && openapi.Server.Config != nil { + baseURL = openapi.Server.Config.BaseURL + } + + client := testutils.RegisterTestClient(t, "File Permission Test Client", []string{"https://localhost/callback"}) + defer testutils.CleanupTestClient(t, client.ClientID) + tokenInfo := testutils.ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile") + + t.Run("UploadWithPublicTeamShare", func(t *testing.T) { + // Upload file with public=true and share=team + requestURL := serverURL + baseURL + "/file/" + testUploaderID + req, err := createMultipartRequest(requestURL, "file", "public-team-file.txt", []byte("Public team content"), map[string]string{ + "original_filename": "public-team-file.txt", + "groups": "shared,public", + "public": "true", + "share": "team", + }) + assert.NoError(t, err) + req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken) + + resp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + var response map[string]interface{} + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + assert.Contains(t, response, "file_id") + t.Logf("Successfully uploaded public team file: %s", response["file_id"]) + }) + + t.Run("UploadWithPrivateShare", func(t *testing.T) { + // Upload file with public=false and share=private (default) + requestURL := serverURL + baseURL + "/file/" + testUploaderID + req, err := createMultipartRequest(requestURL, "file", "private-file.txt", []byte("Private content"), map[string]string{ + "original_filename": "private-file.txt", + "groups": "personal", + "public": "false", + "share": "private", + }) + assert.NoError(t, err) + req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken) + + resp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + var response map[string]interface{} + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + assert.Contains(t, response, "file_id") + t.Logf("Successfully uploaded private file: %s", response["file_id"]) + }) + + t.Run("UploadWithoutPermissionFields", func(t *testing.T) { + // Upload file without specifying public/share (should use defaults) + requestURL := serverURL + baseURL + "/file/" + testUploaderID + req, err := createMultipartRequest(requestURL, "file", "default-permissions.txt", []byte("Default permissions content"), map[string]string{ + "original_filename": "default-permissions.txt", + "groups": "defaults", + }) + assert.NoError(t, err) + req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken) + + resp, err := http.DefaultClient.Do(req) + assert.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + + var response map[string]interface{} + err = json.NewDecoder(resp.Body).Decode(&response) + assert.NoError(t, err) + + assert.Contains(t, response, "file_id") + t.Logf("Successfully uploaded file with default permissions: %s", response["file_id"]) + }) +} diff --git a/yao/models/attachment.mod.yao b/yao/models/attachment.mod.yao index a54b9b6f..c1986e9b 100644 --- a/yao/models/attachment.mod.yao +++ b/yao/models/attachment.mod.yao @@ -154,26 +154,41 @@ "length": 600, "nullable": true }, + { - "name": "client_id", - "type": "string", - "label": "Client ID", - "comment": "Client identifier", - "length": 255, - "nullable": true, - "index": true + "name": "preset", + "type": "boolean", + "label": "Preset Attachment", + "comment": "Whether this is a preset attachment", + "default": false, + "nullable": false }, + { - "name": "openid", - "type": "string", - "label": "OpenID", - "comment": "OpenID identifier", - "length": 255, - "nullable": true, + "name": "public", + "type": "boolean", + "label": "Public Attachment", + "comment": "Whether this attachment is shared across all teams in the platform", + "default": false, + "nullable": false + }, + + // Custom permissions + { + "name": "share", + "type": "enum", + "label": "Share", + "comment": "Attachment sharing scope", + "option": [ + "private", // Only visible to the owner + "team" // Visible to all team members + ], + "default": "private", + "nullable": false, "index": true } ], "relations": {}, "indexes": [], - "option": { "timestamps": true, "soft_deletes": false } + "option": { "timestamps": true, "soft_deletes": false, "permission": true } }