this repo has no description
1package main
2
3import (
4 "context"
5 "log/slog"
6 "net"
7 "net/http"
8 "time"
9
10 "tangled.org/bnewbold.net/scrumble/indexer"
11 "tangled.org/bnewbold.net/scrumble/store"
12
13 "github.com/bluesky-social/indigo/atproto/identity"
14 "github.com/bluesky-social/indigo/atproto/syntax"
15 "github.com/bluesky-social/indigo/util/svcutil"
16 "github.com/labstack/echo/v4"
17 "github.com/labstack/echo/v4/middleware"
18 "github.com/prometheus/client_golang/prometheus/promhttp"
19 "gorm.io/gorm"
20)
21
22type Server struct {
23 logger *slog.Logger
24 dir identity.Directory
25 store *store.Store
26 indexer *indexer.Indexer
27}
28
29func NewServer(db *gorm.DB) (*Server, error) {
30
31 st, err := store.NewStore(db)
32 if err != nil {
33 return nil, err
34 }
35
36 idx, err := indexer.NewIndexer(st, []syntax.DID{})
37 if err != nil {
38 return nil, err
39 }
40
41 return &Server{
42 logger: slog.Default(),
43 dir: identity.DefaultDirectory(),
44 store: st,
45 indexer: idx,
46 }, nil
47}
48
49func (srv *Server) StartMetrics(listen string) error {
50 http.Handle("/metrics", promhttp.Handler())
51 return http.ListenAndServe(listen, nil)
52}
53
54func (srv *Server) StartHTTP(bind string) error {
55 var lc net.ListenConfig
56 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
57 defer cancel()
58
59 li, err := lc.Listen(ctx, "tcp", bind)
60 if err != nil {
61 return err
62 }
63 return srv.startWithListener(li)
64}
65
66func (srv *Server) startWithListener(listen net.Listener) error {
67 e := echo.New()
68 e.HideBanner = true
69 e.HidePort = true
70
71 e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
72 AllowOrigins: []string{"*"},
73 AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAuthorization},
74 }))
75 e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
76 return func(c echo.Context) error {
77 c.Response().Header().Set(echo.HeaderServer, "ScrumbleServer")
78 return next(c)
79 }
80 })
81 e.Use(middleware.LoggerWithConfig(middleware.DefaultLoggerConfig))
82
83 e.File("/robots.txt", "static/robots.txt")
84 e.Static("/static", "static")
85
86 e.Use(svcutil.MetricsMiddleware)
87
88 e.GET("/", srv.HandleHomeMessage)
89 e.GET("/_health", srv.HandleHealthCheck)
90
91 // In order to support booting on random ports in tests, we need to tell
92 // the Echo instance it's already got a port, and then use its StartServer
93 // method to re-use that listener.
94 e.Listener = listen
95 httpServer := &http.Server{}
96 // TODO: attach echo to Server, for shutdown?
97 return e.StartServer(httpServer)
98}
99
100func (srv *Server) StartIndexer() error {
101 // TODO: wire through config
102 return srv.indexer.Start("ws://localhost:2480/channel")
103}
104
105func (srv *Server) Shutdown() []error {
106 var errs []error
107
108 if err := srv.indexer.Shutdown(); err != nil {
109 errs = append(errs, err)
110 }
111 // TODO: stop echo
112 return errs
113}