-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathapp.go
107 lines (91 loc) · 3.34 KB
/
app.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package main
import (
"context"
"fmt"
"io/fs"
"log/slog"
"net/http"
"strings"
"time"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/mr-karan/gullak/internal/db"
"github.com/mr-karan/gullak/internal/llm"
)
func initConfig(cfgPath string) (*koanf.Koanf, error) {
k := koanf.New(".")
if err := k.Load(file.Provider(cfgPath), toml.Parser()); err != nil {
return nil, fmt.Errorf("error loading config: %v", err)
}
k.Load(env.Provider("GULLAK_", ".", func(s string) string {
return strings.Replace(strings.ToLower(
strings.TrimPrefix(s, "GULLAK_")), "_", ".", -1)
}), nil)
return k, nil
}
type App struct {
srv *echo.Echo
log *slog.Logger
addr string
llm *llm.Manager
queries *db.Queries
}
func initApp(addr string, timeout time.Duration, static fs.FS, queries *db.Queries, llmMgr *llm.Manager, log *slog.Logger) *App {
e := echo.New()
e.HideBanner = true
// e.Use(middleware.Logger()) -> Too noisy for now.
e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
Timeout: timeout,
}))
// Register handlers.
e.GET("/api", handleIndex) // Simple welcome message or API status
e.POST("/api/transactions", handleCreateTransaction) // Creates a new transaction
e.GET("/api/transactions", handleListTransactions) // Lists all transactions, with optional filters
e.GET("/api/transactions/:id", handleGetTransaction) // Retrieves a specific transaction by ID
e.PUT("/api/transactions/:id", handleUpdateTransaction) // Updates a specific transaction by ID
e.DELETE("/api/transactions/:id", handleDeleteTransaction) // Deletes a specific transaction by ID
e.GET("/api/reports/top-expense-categories", handleTopExpenseCategories) // Retrieves top expense categories
e.GET("/api/reports/daily-spending", handleDailySpending) // Retrieves spending for a specific day
// e.GET("/api/reports/monthly-spending-summary", handleMonthlySpendingSummary) // Retrieves spending summary by month
// Middleware to serve the static files.
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
Root: "/",
Index: "index.html",
HTML5: true, // This kicks in client side routing.
Filesystem: http.FS(static),
}))
return &App{
srv: e,
log: log,
addr: addr,
queries: queries,
llm: llmMgr,
}
}
func (m *App) Start(ctx context.Context) error {
// Register app (*App) to be injected into all HTTP handlers.
m.srv.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("app", m)
return next(c)
}
})
// Start server in a goroutine to allow for graceful shutdown.
go func() {
if err := m.srv.Start(m.addr); err != http.ErrServerClosed {
m.srv.Logger.Fatalf("Shutting down the server: %v", err)
}
}()
// Wait for interrupt signal to gracefully shut down the server with a timeout
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := m.srv.Shutdown(shutdownCtx); err != nil {
m.srv.Logger.Fatalf("Error shutting down server: %v", err)
}
return nil
}