From d297afb04a94d4ba8a74658b50a170be3d4e9727 Mon Sep 17 00:00:00 2001 From: lehel Date: Tue, 7 Oct 2025 17:41:19 +0200 Subject: [PATCH] login handler --- auth_test.go | 53 +++++++++++++++++++++++++++++++++ controllers.go | 5 ++-- go.mod | 3 ++ handlechat_integration_test.go | 14 +++++++++ repository.go | 15 +++++++--- visits.bleve/index_meta.json | 1 - visits.bleve/store/root.bolt | Bin 262144 -> 0 bytes 7 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 auth_test.go delete mode 100644 visits.bleve/index_meta.json delete mode 100644 visits.bleve/store/root.bolt diff --git a/auth_test.go b/auth_test.go new file mode 100644 index 0000000..1987c02 --- /dev/null +++ b/auth_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + "errors" +) + +var ErrUserExists = errors.New("user already exists") +var ErrUserNotFound = errors.New("user not found") + +// mockRepo implements ChatRepositoryAPI for auth tests +// Only implements methods needed for auth + +type mockRepo struct { + users map[string]string // username: passwordHash +} + +func (m *mockRepo) CountUsers(ctx context.Context) (int, error) { + return len(m.users), nil +} +func (m *mockRepo) CreateUser(ctx context.Context, username, passwordHash string) error { + if _, exists := m.users[username]; exists { + return ErrUserExists + } + m.users[username] = passwordHash + return nil +} +func (m *mockRepo) GetUserByUsername(ctx context.Context, username string) (*User, error) { + hash, ok := m.users[username] + if !ok { + return nil, ErrUserNotFound + } + return &User{Username: username, PasswordHash: hash}, nil +} + +// Add stubs for all ChatRepositoryAPI methods not used in tests +func (m *mockRepo) SaveChatInteraction(ctx context.Context, rec ChatInteraction) error { return nil } +func (m *mockRepo) ListChatInteractions(ctx context.Context, limit, offset int) ([]ChatInteraction, error) { + return nil, nil +} +func (m *mockRepo) SaveLLMRawEvent(ctx context.Context, correlationID, phase, raw string) error { + return nil +} +func (m *mockRepo) ListLLMRawEvents(ctx context.Context, correlationID string, limit, offset int) ([]RawLLMEvent, error) { + return nil, nil +} +func (m *mockRepo) SaveKnowledgeModel(ctx context.Context, text string) error { return nil } +func (m *mockRepo) ListKnowledgeModels(ctx context.Context, limit, offset int) ([]knowledgeModelMeta, error) { + return nil, nil +} +func (m *mockRepo) GetKnowledgeModelText(ctx context.Context, id int64) (string, error) { + return "", nil +} diff --git a/controllers.go b/controllers.go index e7254a5..ee2c453 100644 --- a/controllers.go +++ b/controllers.go @@ -255,8 +255,7 @@ func AdminAuthMiddleware() gin.HandlerFunc { // CreateInitialAdmin allows creation of the first admin user if none exist func (ctrl *Controller) CreateInitialAdmin(c *gin.Context) { // Only allow if no users exist - var count int - err := ctrl.repo.CountUsers(&count) + count, err := ctrl.repo.CountUsers(c.Request.Context()) if err != nil || count > 0 { c.JSON(http.StatusForbidden, gin.H{"error": "Admin user already exists or error"}) return @@ -272,7 +271,7 @@ func (ctrl *Controller) CreateInitialAdmin(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"}) return } - err = ctrl.repo.CreateUser(username, hash) + err = ctrl.repo.CreateUser(c.Request.Context(), username, hash) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"}) return diff --git a/go.mod b/go.mod index 1ea3fa1..a992042 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/jackc/pgx/v5 v5.7.5 github.com/pressly/goose/v3 v3.26.0 github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.42.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -37,6 +38,7 @@ require ( github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -59,6 +61,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.54.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect diff --git a/handlechat_integration_test.go b/handlechat_integration_test.go index 74c246a..870a7e6 100644 --- a/handlechat_integration_test.go +++ b/handlechat_integration_test.go @@ -74,6 +74,20 @@ func (r *mapChatRepo) ListLLMRawEvents(ctx context.Context, correlationID string } return out, nil } +func (r *mapChatRepo) CountUsers(ctx context.Context) (int, error) { return 0, nil } +func (r *mapChatRepo) CreateUser(ctx context.Context, username, passwordHash string) error { + return nil +} +func (r *mapChatRepo) GetUserByUsername(ctx context.Context, username string) (*User, error) { + return nil, nil +} +func (r *mapChatRepo) SaveKnowledgeModel(ctx context.Context, text string) error { return nil } +func (r *mapChatRepo) ListKnowledgeModels(ctx context.Context, limit, offset int) ([]knowledgeModelMeta, error) { + return nil, nil +} +func (r *mapChatRepo) GetKnowledgeModelText(ctx context.Context, id int64) (string, error) { + return "", nil +} // testVisitDB2 replicates a minimal VisitDB for integration // (avoids relying on real Bleve index) diff --git a/repository.go b/repository.go index ae45b29..aa3f79e 100644 --- a/repository.go +++ b/repository.go @@ -35,6 +35,8 @@ type ChatRepositoryAPI interface { ListKnowledgeModels(ctx context.Context, limit, offset int) ([]knowledgeModelMeta, error) GetKnowledgeModelText(ctx context.Context, id int64) (string, error) GetUserByUsername(ctx context.Context, username string) (*User, error) + CountUsers(ctx context.Context) (int, error) + CreateUser(ctx context.Context, username, passwordHash string) error } // RawLLMEvent represents a stored raw LLM exchange phase @@ -246,13 +248,18 @@ func (r *PGChatRepository) GetUserByUsername(ctx context.Context, username strin } // CountUsers returns the number of users in the users table -func (r *PGChatRepository) CountUsers(count *int) error { - return r.pool.QueryRow(context.Background(), "SELECT COUNT(*) FROM users").Scan(count) +func (r *PGChatRepository) CountUsers(ctx context.Context) (int, error) { + var count int + err := r.pool.QueryRow(ctx, "SELECT COUNT(*) FROM users").Scan(&count) + if err != nil { + return 0, err + } + return count, nil } // CreateUser inserts a new user with username and password hash -func (r *PGChatRepository) CreateUser(username, passwordHash string) error { - _, err := r.pool.Exec(context.Background(), "INSERT INTO users (username, password_hash) VALUES ($1, $2)", username, passwordHash) +func (r *PGChatRepository) CreateUser(ctx context.Context, username, passwordHash string) error { + _, err := r.pool.Exec(ctx, "INSERT INTO users (username, password_hash) VALUES ($1, $2)", username, passwordHash) return err } diff --git a/visits.bleve/index_meta.json b/visits.bleve/index_meta.json deleted file mode 100644 index 5dc3405..0000000 --- a/visits.bleve/index_meta.json +++ /dev/null @@ -1 +0,0 @@ -{"storage":"boltdb","index_type":"scorch"} \ No newline at end of file diff --git a/visits.bleve/store/root.bolt b/visits.bleve/store/root.bolt deleted file mode 100644 index 914c19ff0c30f6b4276bcf9fea80c5d374423373..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262144 zcmeI*%W5Q57y#g!&RmjFFbImvu{JIyGjw_`kzS303qhEL0o}D_sv-q_qpK1o8R8QN zuH3kBEBFlVbmkr0_yprh(50ukPlebdVQ>N?!&h+Xuk+WbQ(a$IFaOg?ve+)i`0TgW zpLLq!2aCk&A{_76;?Zj{ejVetYV>m=>eNpd5Mcq6pcIP$%C{8pG{ zds2URP#4u_v$C#>aU78hM%i>)j=y@G=EZKdH>}%4+JBrD(MDPyOpC#8Sq#I7bkOwDFepwl?}l{jpE#3^v*E#EF$)(}bvDkkS$^Vdp4CNN zj*7uFn^p5cO=de}9!~3W60SWl`1t18ut`_-WL6CR^)WZna-0{BmXrBpXFnV671i>I z`G>2@Dm?zt(Wq5gj_tSD!v554`3-1Z zne{mG-4L4_H}A-_d%WG*xgBN?`gQfVU0EVtArK%yfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkL{6&Gl$!Q0C5 zrbzo`*8hL<(E_|J_gDYFSmWSj*8gwy&a3}_a@H^8ztsO1>i^H;lqW!d009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAVA<%7ij(wzS-3O59THIhs7-Ir{N#y$9Xo((~UGQcC)=oQ=wzw7;n6FUNWDXgQfrcJ{O3UQsQd zc$xM8_q%uA-@4iD^}F5rU|Jkz)BR#rg=Y@$&3dB#fBplqBS3%v0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!Cst z3S5cTpH~0B9?o3~Z8OHU-#gL&wG%ogw20t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK;QxduEjads{enw zasf9n1pxvC2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5Fl`_z-l~uR{ej~E)a0;EKU+2K!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pkFCwrOFJ4cs%#S*;e>uiQHc76={`D9I0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK;Ys8+Is%= zWMw|-wK(-^jP3dW7k5W95g%Dew