// Package main 程序入口 package main import ( "fmt" "io" "log" "os" "path/filepath" "time" "seeyon-filesystem/api" "seeyon-filesystem/config" "seeyon-filesystem/model" "seeyon-filesystem/storage" "seeyon-filesystem/utils" "golang.org/x/crypto/bcrypt" ) func main() { // 日志输出: 控制台 + 内存缓冲区 + 按日期分割的文件 logDir := "logs" os.MkdirAll(logDir, 0755) logPath := filepath.Join(logDir, fmt.Sprintf("server_%s.log", time.Now().Format("20060102"))) logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err == nil { log.SetOutput(io.MultiWriter(os.Stderr, logFile, utils.LogBuf)) } else { log.SetOutput(io.MultiWriter(os.Stderr, utils.LogBuf)) } cfg, err := config.Load() if err != nil { log.Fatalf("加载配置失败: %v", err) } // 验证授权(启动时必须通过, 否则拒绝启动) if err := checkLicense(cfg.License); err != nil { log.Fatalf("授权验证失败: %v", err) } if err := config.InitDB(&cfg.Database); err != nil { log.Fatalf("初始化数据库失败: %v", err) } // if err := autoMigrate(); err != nil { // log.Fatalf("数据库迁移失败: %v", err) // } initDefaultData() storage.InitEngines(cfg.Storage.LocalBasePath) r := api.SetupRouter() addr := fmt.Sprintf(":%d", cfg.Server.Port) log.Printf("服务启动: http://localhost%s", addr) if err := r.Run(addr); err != nil { log.Fatalf("服务启动失败: %v", err) } } func autoMigrate() error { err := config.DB.AutoMigrate( &model.User{}, &model.Folder{}, &model.File{}, &model.Share{}, &model.StoragePolicy{}, &model.FileAssociation{}, &model.Role{}, &model.Permission{}, &model.RolePermission{}, &model.UserRole{}, &model.SSOProvider{}, &model.SSOSession{}, &model.Department{}, &model.UploadSession{}, &model.StorageAssignment{}, &model.App{}, &model.AppUser{}, &model.FilePermission{}, &model.OperationLog{}, &model.BackupPolicy{}, &model.BackupLog{}, &model.UserSession{}, ) if err != nil { return err } // 添加复合索引优化查询性能 createIndexIfNotExists("fs_file_permission", "idx_fp_user_file", "user_id, file_id") createIndexIfNotExists("fs_file_permission", "idx_fp_role_file", "role_id, file_id") createIndexIfNotExists("fs_file", "idx_file_folder_owner", "folder_id, owner_id, status") createIndexIfNotExists("fs_file", "idx_file_status", "status") return nil } // createIndexIfNotExists 安全创建索引(忽略已存在错误) func createIndexIfNotExists(table, indexName, columns string) { // 先检查索引是否已存在(兼容MySQL/PostgreSQL/SQL Server) var count int64 switch config.AppConfig.Database.Type { case "postgres": config.DB.Raw("SELECT COUNT(*) FROM pg_indexes WHERE indexname = ?", indexName).Scan(&count) case "sqlserver": config.DB.Raw("SELECT COUNT(*) FROM sys.indexes WHERE name = ?", indexName).Scan(&count) default: config.DB.Raw("SELECT COUNT(*) FROM information_schema.statistics WHERE table_name = ? AND index_name = ?", table, indexName).Scan(&count) } if count > 0 { return } sql := fmt.Sprintf("CREATE INDEX %s ON %s(%s)", indexName, table, columns) config.DB.Exec(sql) } // initDefaultData 初始化默认数据: 权限、角色、存储策略、分配规则 func initDefaultData() { // ========== 1. 权限 ========== permissions := []model.Permission{ {Code: "file:create", Name: "创建文件/文件夹", Resource: "file", Action: "create"}, {Code: "file:read", Name: "查看文件", Resource: "file", Action: "read"}, {Code: "file:update", Name: "修改文件", Resource: "file", Action: "update"}, {Code: "file:delete", Name: "删除文件", Resource: "file", Action: "delete"}, {Code: "file:upload", Name: "上传文件", Resource: "file", Action: "upload"}, {Code: "file:download", Name: "下载文件", Resource: "file", Action: "download"}, {Code: "file:grant", Name: "授权文件权限", Resource: "file", Action: "grant"}, {Code: "share:create", Name: "创建分享", Resource: "share", Action: "create"}, {Code: "share:manage", Name: "管理分享", Resource: "share", Action: "manage"}, {Code: "webdav:access", Name: "WebDAV访问", Resource: "webdav", Action: "access"}, {Code: "user:manage", Name: "用户管理", Resource: "user", Action: "manage"}, {Code: "role:manage", Name: "角色管理", Resource: "role", Action: "manage"}, {Code: "sso:manage", Name: "SSO管理", Resource: "sso", Action: "manage"}, {Code: "system:manage", Name: "系统管理", Resource: "system", Action: "manage"}, } for _, p := range permissions { var existing model.Permission if config.DB.Where("code = ?", p.Code).First(&existing).Error != nil { p.ID = utils.GenID() config.DB.Create(&p) } } // ========== 2. 四级角色 ========== roles := []model.Role{ {Name: "admin", DisplayName: "超级管理员", Description: "全平台最高权限, 仅用于后台运维", IsSystem: 1}, {Name: "staff", DisplayName: "内部员工", Description: "团队内部成员, 可上传下载分享文件", IsSystem: 1}, {Name: "user", DisplayName: "普通用户", Description: "注册用户, 基础存储功能", IsSystem: 1}, {Name: "guest", DisplayName: "访客", Description: "仅可查看公开分享资源", IsSystem: 1}, } for _, r := range roles { var existing model.Role if config.DB.Where("name = ?", r.Name).First(&existing).Error != nil { r.ID = utils.GenID() config.DB.Create(&r) } else { r = existing } } // 角色-权限分配 rolePermMap := map[string][]string{ "admin": {"*"}, // 全部权限 "staff": {"file:create", "file:read", "file:update", "file:delete", "file:upload", "file:download", "file:grant", "share:create", "webdav:access"}, "user": {"file:create", "file:read", "file:update", "file:upload", "file:download", "share:create"}, "guest": {"file:read"}, } var allPerms []model.Permission config.DB.Find(&allPerms) permMap := make(map[string]uint64) for _, p := range allPerms { permMap[p.Code] = p.ID } for roleName, permCodes := range rolePermMap { var role model.Role config.DB.Where("name = ?", roleName).First(&role) if role.ID == 0 { continue } // 清除旧权限 config.DB.Where("role_id = ?", role.ID).Delete(&model.RolePermission{}) if len(permCodes) == 1 && permCodes[0] == "*" { // 全部权限 for _, p := range allPerms { config.DB.Create(&model.RolePermission{RoleID: role.ID, PermissionID: p.ID}) } } else { for _, code := range permCodes { if pid, ok := permMap[code]; ok { config.DB.Create(&model.RolePermission{RoleID: role.ID, PermissionID: pid}) } } } } // ========== 3. 四类存储策略 ========== type policyDef struct { Name string PolicyType string Type string BasePath string Quota int64 } policyDefs := []policyDef{ {Name: "高性能热存储", PolicyType: "hot", Type: "local", BasePath: "./data/storage/hot", Quota: 53687091200}, // 50GB {Name: "低成本冷存储", PolicyType: "cold", Type: "local", BasePath: "./data/storage/cold", Quota: 107374182400}, // 100GB {Name: "公开资源存储", PolicyType: "public", Type: "local", BasePath: "./data/storage/public", Quota: 0}, // 不限 {Name: "扩展存储", PolicyType: "slave", Type: "local", BasePath: "./data/storage/slave", Quota: 214748364800}, // 200GB } for _, pd := range policyDefs { var existing model.StoragePolicy if config.DB.Where("policy_type = ?", pd.PolicyType).First(&existing).Error != nil { config.DB.Create(&model.StoragePolicy{ ID: utils.GenID(), Name: pd.Name, Type: pd.Type, PolicyType: pd.PolicyType, Config: model.PolicyConfig{BasePath: pd.BasePath}, Status: 1, }) } } // ========== 4. 角色-存储策略分配(支持多策略+优先级) ========== type assignDef struct { RoleName string PolicyType string Quota int64 Priority int } assignDefs := []assignDef{ {RoleName: "admin", PolicyType: "hot", Quota: 0, Priority: 10}, {RoleName: "staff", PolicyType: "hot", Quota: 53687091200, Priority: 10}, {RoleName: "user", PolicyType: "cold", Quota: 10737418240, Priority: 10}, } for _, ad := range assignDefs { var role model.Role var policy model.StoragePolicy config.DB.Where("name = ?", ad.RoleName).First(&role) config.DB.Where("policy_type = ?", ad.PolicyType).First(&policy) if role.ID == 0 || policy.ID == 0 { continue } var existing model.StorageAssignment if config.DB.Where("group_id = ? AND policy_id = ?", role.ID, policy.ID).First(&existing).Error != nil { config.DB.Create(&model.StorageAssignment{ ID: utils.GenID(), PolicyID: policy.ID, GroupID: role.ID, StorageQuota: ad.Quota, Priority: ad.Priority, Status: 1, }) } } // 设置默认存储策略(热存储) var hotPolicy model.StoragePolicy if config.DB.Where("policy_type = ?", "hot").First(&hotPolicy).Error == nil { var defaultAssign model.StorageAssignment if config.DB.Where("is_default = 1").First(&defaultAssign).Error != nil { config.DB.Create(&model.StorageAssignment{ ID: utils.GenID(), PolicyID: hotPolicy.ID, IsDefault: 1, Status: 1, }) } } // ========== 5. 创建默认管理员 ========== var count int64 config.DB.Model(&model.User{}).Where("username = ?", "admin").Count(&count) if count == 0 { hashedPassword, err := bcrypt.GenerateFromPassword([]byte("admin123"), 10) if err != nil { log.Fatalf("密码加密失败: %v", err) } admin := &model.User{ ID: utils.GenID(), Username: "admin", Password: string(hashedPassword), Nickname: "管理员", Role: "admin", Status: 1, StorageQuota: 107374182400, // 100GB MustChangePwd: 1, } if err := config.DB.Create(admin).Error; err != nil { log.Fatalf("创建 admin 失败: %v", err) } log.Println("默认管理员已创建: admin / admin123") } else { // 确保已有admin用户的Role字段为admin config.DB.Model(&model.User{}).Where("username = ? AND (role IS NULL OR role = '')", "admin").Update("role", "admin") log.Println("admin 用户已存在") } // ========== 6. 确保admin用户有admin角色 ========== var adminUser model.User if config.DB.Where("username = ?", "admin").First(&adminUser).Error == nil { var adminRole model.Role if config.DB.Where("name = ?", "admin").First(&adminRole).Error == nil { var ur model.UserRole if config.DB.Where("user_id = ? AND role_id = ?", adminUser.ID, adminRole.ID).First(&ur).Error != nil { config.DB.Create(&model.UserRole{UserID: adminUser.ID, RoleID: adminRole.ID}) log.Println("admin 已分配 admin 角色") } } } // ========== 7. 给没有角色的用户补上默认角色(user) ========== var userRole model.Role if config.DB.Where("name = ?", "user").First(&userRole).Error == nil { var usersWithoutRole []model.User config.DB.Where("id NOT IN (SELECT user_id FROM fs_user_role) AND username != ?", "admin").Find(&usersWithoutRole) for _, u := range usersWithoutRole { config.DB.Create(&model.UserRole{UserID: u.ID, RoleID: userRole.ID}) } if len(usersWithoutRole) > 0 { log.Printf("已为 %d 个无角色用户分配默认角色", len(usersWithoutRole)) } } log.Println("默认数据初始化完成") } // checkLicense 验证产品授权 func checkLicense(cfg config.LicenseConfig) error { machineID := utils.GetMachineID() log.Printf("机器标识: %s", machineID) licensePath := cfg.FilePath if licensePath == "" { licensePath = "license.json" } // 读取授权文件 license, err := utils.ReadLicenseFile(licensePath) if err != nil { return err } // 设置当前授权信息 utils.SetCurrentLicense(license) // 根据网络类型选择验证方式 network := cfg.Network if network == "" { network = "internal" } if network == "external" && cfg.VerifyURL != "" { // 外网: 调远程接口验证 resp, err := utils.ReadAndVerifyLicense(licensePath, cfg.VerifyURL, "seeyon-filesystem", "1.0") if err != nil { return err } if !resp.Valid { return fmt.Errorf("%s", resp.Message) } if resp.ExpireDays == -1 { log.Printf("[授权] 企业版(外网) - 永久授权, 用户上限: %d, 功能: %s", resp.MaxUsers, license.Features) } else { log.Printf("[授权] 企业版(外网) - 有效期剩余 %d 天, 用户上限: %d, 功能: %s", resp.ExpireDays, resp.MaxUsers, license.Features) } } else { // 内网: 仅本地校验(解密 + 签名 + 机器码比对) if err := utils.VerifyLicense(license); err != nil { return err } days := utils.GetLicenseExpireDays(license) if days == -1 { log.Printf("[授权] 企业版(内网) - 永久授权, 用户上限: %d, 功能: %s", license.MaxUsers, license.Features) } else { log.Printf("[授权] 企业版(内网) - 有效期剩余 %d 天, 用户上限: %d, 功能: %s", days, license.MaxUsers, license.Features) } } return nil }