1package ext
2
3import (
4 "encoding/base64"
5 "time"
6
7 "github.com/sirupsen/logrus"
8 "github.com/valyala/fasthttp"
9
10 "git.sr.ht/~gabrielgio/img/pkg/database/repository"
11)
12
13func HTML(next fasthttp.RequestHandler) fasthttp.RequestHandler {
14 return func(ctx *fasthttp.RequestCtx) {
15 if len(ctx.Request.Header.ContentType()) > 0 {
16 ctx.Response.Header.SetContentType("text/html")
17 }
18 next(ctx)
19 }
20}
21
22type LogMiddleware struct {
23 entry *logrus.Entry
24}
25
26func NewLogMiddleare(log *logrus.Entry) *LogMiddleware {
27 return &LogMiddleware{
28 entry: log,
29 }
30}
31
32func (l *LogMiddleware) HTTP(next fasthttp.RequestHandler) fasthttp.RequestHandler {
33 return func(ctx *fasthttp.RequestCtx) {
34 start := time.Now()
35 next(ctx)
36 elapsed := time.Since(start)
37 l.entry.
38 WithField("time", elapsed).
39 WithField("code", ctx.Response.StatusCode()).
40 WithField("path", string(ctx.Path())).
41 WithField("bytes", len(ctx.Response.Body())).
42 Info(string(ctx.Request.Header.Method()))
43 }
44}
45
46type AuthMiddleware struct {
47 key []byte
48 entry *logrus.Entry
49}
50
51func NewAuthMiddleware(key []byte, log *logrus.Entry) *AuthMiddleware {
52 return &AuthMiddleware{
53 key: key,
54 entry: log.WithField("context", "auth"),
55 }
56}
57
58func (a *AuthMiddleware) LoggedIn(next fasthttp.RequestHandler) fasthttp.RequestHandler {
59 return func(ctx *fasthttp.RequestCtx) {
60 path := string(ctx.Path())
61 if path == "/login" || path == "/initial" {
62 next(ctx)
63 return
64 }
65
66 redirectLogin := "/login?redirect=" + path
67 authBase64 := ctx.Request.Header.Cookie("auth")
68 if authBase64 == nil {
69 a.entry.Info("No auth provided")
70 ctx.Redirect(redirectLogin, 307)
71 return
72 }
73
74 auth, err := base64.StdEncoding.DecodeString(string(authBase64))
75 if err != nil {
76 a.entry.Error(err)
77 return
78 }
79
80 token, err := ReadToken(auth, a.key)
81 if err != nil {
82 a.entry.Error(err)
83 ctx.Redirect(redirectLogin, 307)
84 return
85 }
86 ctx.SetUserValue("token", token)
87 a.entry.
88 WithField("userID", token.UserID).
89 WithField("username", token.Username).
90 Info("user recognized")
91 next(ctx)
92 }
93}
94
95type InitialSetupMiddleware struct {
96 userRepository repository.UserRepository
97}
98
99func NewInitialSetupMiddleware(userRepository repository.UserRepository) *InitialSetupMiddleware {
100 return &InitialSetupMiddleware{
101 userRepository: userRepository,
102 }
103}
104
105func (i *InitialSetupMiddleware) Check(next fasthttp.RequestHandler) fasthttp.RequestHandler {
106 return func(ctx *fasthttp.RequestCtx) {
107 // if user has been set to context it is logged in already
108 _, ok := ctx.UserValue("token").(*Token)
109 if ok {
110 next(ctx)
111 return
112 }
113
114 path := string(ctx.Path())
115 if path == "/initial" {
116 next(ctx)
117 return
118 }
119
120 exists, err := i.userRepository.Any(ctx)
121 if err != nil {
122 InternalServerError(ctx, err)
123 return
124 }
125
126 if exists {
127 next(ctx)
128 return
129 }
130 ctx.Redirect("/initial", 307)
131 }
132}