Refactored project structure
This commit is contained in:
150
backend/handlers/app.go
Normal file
150
backend/handlers/app.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FinalizedSummary struct {
|
||||
Pickup []string `json:"pickup"`
|
||||
Kitchen []string `json:"kitchen"`
|
||||
}
|
||||
|
||||
type App struct {
|
||||
DB *sql.DB
|
||||
Broker *Broker
|
||||
FinalizeUpdate chan struct{} // notify scheduler to reload time
|
||||
LastSummary *FinalizedSummary
|
||||
}
|
||||
|
||||
func NewApp(db *sql.DB, broker *Broker, finalizeUpdate chan struct{}) *App {
|
||||
return &App{
|
||||
DB: db,
|
||||
Broker: broker,
|
||||
FinalizeUpdate: finalizeUpdate,
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) RequireLevel(maxAllowed int) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
username, err := usernameFromJWT(r)
|
||||
if err != nil || username == "" {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
role, err := app.getUserRole(username)
|
||||
if err != nil {
|
||||
http.Error(w, "db error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if role > maxAllowed {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) getUserRole(username string) (int, error) {
|
||||
var role int
|
||||
err := app.DB.QueryRow("SELECT role FROM users WHERE username = ?", username).Scan(&role)
|
||||
return role, err
|
||||
}
|
||||
|
||||
func finalizeOrders(app *App) error {
|
||||
rows, err := app.DB.Query("SELECT id, username, soup, main, side FROM orders WHERE status = 'active'")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var summary []Order
|
||||
for rows.Next() {
|
||||
var o Order
|
||||
if err := rows.Scan(&o.ID, &o.Username, &o.Soup, &o.Main, &o.Side); err == nil {
|
||||
summary = append(summary, o)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: send summary (log, email, or message)
|
||||
sendSummary(app, summary)
|
||||
|
||||
// Step 3: archive
|
||||
_, err = app.DB.Exec("UPDATE orders SET status = 'history' WHERE status = 'active'")
|
||||
|
||||
// Step 4: broadcast deletions to SSE clients
|
||||
for _, o := range summary {
|
||||
msg := map[string]any{"id": o.ID, "status": "history", "event": "deleted"}
|
||||
if data, err := json.Marshal(msg); err == nil {
|
||||
app.Broker.broadcast <- data
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func sendSummary(app *App, orders []Order) *FinalizedSummary {
|
||||
// Pickup view
|
||||
userMap := make(map[string][]string)
|
||||
for _, o := range orders {
|
||||
items := []string{}
|
||||
if o.Soup != "" {
|
||||
items = append(items, o.Soup)
|
||||
}
|
||||
items = append(items, o.Main, o.Side)
|
||||
userMap[o.Username] = append(userMap[o.Username], items...)
|
||||
}
|
||||
pickup := []string{}
|
||||
for user, items := range userMap {
|
||||
pickup = append(pickup, fmt.Sprintf("%s: [%s]", user, strings.Join(items, ", ")))
|
||||
}
|
||||
|
||||
// Kitchen view
|
||||
kitchenMap := make(map[string]int)
|
||||
for _, o := range orders {
|
||||
if o.Soup != "" {
|
||||
kitchenMap[o.Soup]++
|
||||
}
|
||||
kitchenMap[o.Main]++
|
||||
kitchenMap[o.Side]++
|
||||
}
|
||||
kitchen := []string{}
|
||||
for item, count := range kitchenMap {
|
||||
kitchen = append(kitchen, fmt.Sprintf("%s: %d", item, count))
|
||||
}
|
||||
|
||||
summary := &FinalizedSummary{Pickup: pickup, Kitchen: kitchen}
|
||||
app.LastSummary = summary
|
||||
|
||||
// Log
|
||||
log.Println("=== Pickup view ===")
|
||||
for _, line := range pickup {
|
||||
log.Println(line)
|
||||
}
|
||||
log.Println("=== Kitchen view ===")
|
||||
for _, line := range kitchen {
|
||||
log.Println(line)
|
||||
}
|
||||
|
||||
return summary
|
||||
}
|
||||
|
||||
func (app *App) getSetting(key, def string) string {
|
||||
var v string
|
||||
err := app.DB.QueryRow("SELECT value FROM settings WHERE key = ?", key).Scan(&v)
|
||||
if err != nil || v == "" {
|
||||
return def
|
||||
}
|
||||
return v
|
||||
}
|
||||
Reference in New Issue
Block a user