Difference between revisions of "Go postgres simple memory sync"
Jump to navigation
Jump to search
(Created page with "``` package main import ( "context" "database/sql" "fmt" "log" "net/http" "sync" "time" _ "github.com/lib/pq"...") |
(No difference)
|
Latest revision as of 19:24, 29 March 2025
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)
}
}