Go postgres simple memory sync
Jump to navigation
Jump to search
package main import ( "context" "database/sql" "fmt" "log" "net/http" "sync" "time" _ "github.com/lib/pq" // PostgreSQL driver ) type User struct { Username string Password string } type UserStore struct { mu sync.RWMutex users map[string]User } func NewUserStore() *UserStore { return &UserStore{ users: make(map[string]User), } } func (us *UserStore) Get(username string) (User, bool) { us.mu.RLock() defer us.mu.RUnlock() user, ok := us.users[username] return user, ok } func (us *UserStore) Set(username string, user User) { us.mu.Lock() defer us.mu.Unlock() us.users[username] = user } func (us *UserStore) Delete(username string) { us.mu.Lock() defer us.mu.Unlock() delete(us.users, username) } func syncUsers(ctx context.Context, db *sql.DB, userStore *UserStore, interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ticker.C: if err := loadUsers(db, userStore); err != nil { log.Printf("Error loading users: %v", err) } case <-ctx.Done(): log.Println("User sync stopped") return } } } func loadUsers(db *sql.DB, userStore *UserStore) error { rows, err := db.Query("SELECT username, password FROM users") if err != nil { return fmt.Errorf("query failed: %w", err) } defer rows.Close() newUsers := make(map[string]User) for rows.Next() { var user User if err := rows.Scan(&user.Username, &user.Password); err != nil { return fmt.Errorf("scan failed: %w", err) } newUsers[user.Username] = user } if err := rows.Err(); err != nil { return fmt.Errorf("rows iteration failed: %w", err) } userStore.mu.Lock() userStore.users = newUsers userStore.mu.Unlock() log.Println("Users synchronized") return nil } func basicAuth(userStore *UserStore, handler http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) http.Error(w, "Unauthorized", http.StatusUnauthorized) return } user, ok := userStore.Get(username) if !ok || user.Password != password { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) http.Error(w, "Unauthorized", http.StatusUnauthorized) return } handler(w, r) } } func main() { connStr := "postgres://user:password@host/dbname?sslmode=disable" // Replace with your connection string db, err := sql.Open("postgres", connStr) if err != nil { log.Fatalf("Error opening database: %v", err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatalf("Error pinging database: %v", err) } userStore := NewUserStore() ctx, cancel := context.WithCancel(context.Background()) defer cancel() go syncUsers(ctx, db, userStore, 5*time.Second) // Sync every 5 seconds http.HandleFunc("/", basicAuth(userStore, func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Authenticated!\n") })) log.Println("Server listening on :8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatalf("Error starting server: %v", err) } }