9992fb4ef1
Refactor the application to a Go-based architecture for improved performance and maintainability. Replace the Dockerfile to utilize a multi-stage build process, enhancing image efficiency. Implement comprehensive session store tests to ensure reliability and create new OAuth handlers for managing authentication efficiently. Update documentation to reflect these structural changes.
129 lines
2.6 KiB
Go
129 lines
2.6 KiB
Go
package store
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// User represents a user in the system
|
|
type User struct {
|
|
Email string `json:"email"`
|
|
GivenName string `json:"given_name"`
|
|
FamilyName string `json:"family_name"`
|
|
UserID string `json:"user_id"`
|
|
Picture string `json:"picture,omitempty"`
|
|
}
|
|
|
|
// UserStore provides thread-safe user storage
|
|
type UserStore struct {
|
|
mu sync.RWMutex
|
|
users map[string]*User
|
|
}
|
|
|
|
// NewUserStore creates a new user store
|
|
func NewUserStore() *UserStore {
|
|
return &UserStore{
|
|
users: make(map[string]*User),
|
|
}
|
|
}
|
|
|
|
// LoadFromFile loads users from a JSON file
|
|
func (s *UserStore) LoadFromFile(path string) error {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil // File doesn't exist, start with empty store
|
|
}
|
|
return fmt.Errorf("read users file: %w", err)
|
|
}
|
|
|
|
var rawUsers map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &rawUsers); err != nil {
|
|
return fmt.Errorf("parse users file: %w", err)
|
|
}
|
|
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
for email, raw := range rawUsers {
|
|
var user User
|
|
if err := json.Unmarshal(raw, &user); err != nil {
|
|
return fmt.Errorf("parse user %s: %w", email, err)
|
|
}
|
|
user.Email = email // Ensure email is set
|
|
if user.UserID == "" {
|
|
user.UserID = email
|
|
}
|
|
s.users[email] = &user
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetByEmail retrieves a user by email
|
|
func (s *UserStore) GetByEmail(email string) (*User, bool) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
user, ok := s.users[email]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
// Return a copy to prevent external modification
|
|
userCopy := *user
|
|
return &userCopy, true
|
|
}
|
|
|
|
// Create adds a new user
|
|
func (s *UserStore) Create(email string, user *User) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
user.Email = email
|
|
if user.UserID == "" {
|
|
user.UserID = email
|
|
}
|
|
s.users[email] = user
|
|
}
|
|
|
|
// Update modifies an existing user
|
|
func (s *UserStore) Update(email string, updates *User) (*User, bool) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
existing, ok := s.users[email]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
// Apply updates (only non-empty fields)
|
|
if updates.GivenName != "" {
|
|
existing.GivenName = updates.GivenName
|
|
}
|
|
if updates.FamilyName != "" {
|
|
existing.FamilyName = updates.FamilyName
|
|
}
|
|
if updates.Picture != "" {
|
|
existing.Picture = updates.Picture
|
|
}
|
|
|
|
// Return a copy
|
|
userCopy := *existing
|
|
return &userCopy, true
|
|
}
|
|
|
|
// List returns all users
|
|
func (s *UserStore) List() []*User {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
users := make([]*User, 0, len(s.users))
|
|
for _, user := range s.users {
|
|
userCopy := *user
|
|
users = append(users, &userCopy)
|
|
}
|
|
return users
|
|
}
|