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
95func GetTokenFromCtx(ctx *fasthttp.RequestCtx) *Token {
96 tokenValue := ctx.UserValue("token")
97 if token, ok := tokenValue.(*Token); ok {
98 return token
99 }
100 return nil
101}
102
103type InitialSetupMiddleware struct {
104 userRepository repository.UserRepository
105}
106
107func NewInitialSetupMiddleware(userRepository repository.UserRepository) *InitialSetupMiddleware {
108 return &InitialSetupMiddleware{
109 userRepository: userRepository,
110 }
111}
112
113func (i *InitialSetupMiddleware) Check(next fasthttp.RequestHandler) fasthttp.RequestHandler {
114 return func(ctx *fasthttp.RequestCtx) {
115 // if user has been set to context it is logged in already
116 _, ok := ctx.UserValue("token").(*Token)
117 if ok {
118 next(ctx)
119 return
120 }
121
122 path := string(ctx.Path())
123 if path == "/initial" {
124 next(ctx)
125 return
126 }
127
128 exists, err := i.userRepository.Any(ctx)
129 if err != nil {
130 InternalServerError(ctx, err)
131 return
132 }
133
134 if exists {
135 next(ctx)
136 return
137 }
138 ctx.Redirect("/initial", 307)
139 }
140}