From 50274184dc653c79c094ba4d2078d72ba4759dc0 Mon Sep 17 00:00:00 2001 From: yanweidong Date: Thu, 23 Jul 2026 13:54:39 +0800 Subject: [PATCH] feat: initialize default root user --- backend/cmd/api/main.go | 4 +++ backend/internal/initdb/user.go | 36 +++++++++++++++++++ backend/internal/initdb/user_test.go | 52 ++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 backend/internal/initdb/user.go create mode 100644 backend/internal/initdb/user_test.go diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index 852ea04..ae79ed6 100644 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -5,6 +5,7 @@ import ( "senlinai-agent/backend/internal/config" "senlinai-agent/backend/internal/httpx" + "senlinai-agent/backend/internal/initdb" "senlinai-agent/backend/internal/logic/ai" "senlinai-agent/backend/internal/logic/auth" "senlinai-agent/backend/internal/logic/files" @@ -22,6 +23,9 @@ func main() { if err != nil { log.Fatal(err) } + if err := initdb.InitUser(models.DBService); err != nil { + log.Fatal(err) + } authService := auth.NewService(cfg.AuthSecret) projectService := projects.NewService() diff --git a/backend/internal/initdb/user.go b/backend/internal/initdb/user.go new file mode 100644 index 0000000..b3ddd38 --- /dev/null +++ b/backend/internal/initdb/user.go @@ -0,0 +1,36 @@ +package initdb + +import ( + "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" + "senlinai-agent/backend/internal/models" +) + +const ( + rootUsername = "root" + rootPassword = "123456" + rootRole = "root" +) + +// InitUser creates the default root account when the user table is empty. +func InitUser(database *gorm.DB) error { + var count int64 + if err := database.Model(&models.SenlinAgentUser{}).Count(&count).Error; err != nil { + return err + } + if count > 0 { + return nil + } + + passwordHash, err := bcrypt.GenerateFromPassword([]byte(rootPassword), bcrypt.DefaultCost) + if err != nil { + return err + } + + return database.Create(&models.SenlinAgentUser{ + Email: rootUsername, + DisplayName: rootUsername, + PasswordHash: string(passwordHash), + Role: rootRole, + }).Error +} diff --git a/backend/internal/initdb/user_test.go b/backend/internal/initdb/user_test.go new file mode 100644 index 0000000..5a2b12a --- /dev/null +++ b/backend/internal/initdb/user_test.go @@ -0,0 +1,52 @@ +package initdb + +import ( + "testing" + + "github.com/glebarez/sqlite" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" + "senlinai-agent/backend/internal/models" +) + +func TestInitUserCreatesRootUserWhenTableIsEmpty(t *testing.T) { + database := newUserTestDatabase(t) + + require.NoError(t, InitUser(database)) + + var users []models.SenlinAgentUser + require.NoError(t, database.Find(&users).Error) + require.Len(t, users, 1) + require.Equal(t, rootUsername, users[0].Email) + require.Equal(t, rootUsername, users[0].DisplayName) + require.Equal(t, rootRole, users[0].Role) + require.NoError(t, bcrypt.CompareHashAndPassword([]byte(users[0].PasswordHash), []byte(rootPassword))) +} + +func TestInitUserDoesNothingWhenTableIsNotEmpty(t *testing.T) { + database := newUserTestDatabase(t) + existing := models.SenlinAgentUser{ + Email: "existing@example.com", + DisplayName: "Existing User", + PasswordHash: "existing-hash", + Role: "user", + } + require.NoError(t, database.Create(&existing).Error) + + require.NoError(t, InitUser(database)) + + var users []models.SenlinAgentUser + require.NoError(t, database.Find(&users).Error) + require.Len(t, users, 1) + require.Equal(t, existing.Email, users[0].Email) +} + +func newUserTestDatabase(t *testing.T) *gorm.DB { + t.Helper() + + database, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + require.NoError(t, err) + require.NoError(t, database.AutoMigrate(&models.SenlinAgentUser{})) + return database +}