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