~evanj/cms

ref: 37b75b6fd4e482657f38cb98668b78df49c66971 cms/internal/c/c.go -rw-r--r-- 4.1 KiB
37b75b6fEvan M Jones TEST(cache): Attempting to cache at controller level. 8 months ago
                                                                                
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package c

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"strings"

	"git.sr.ht/~evanj/cms/internal/m/user"
)

type KeyCookie = string

var KeyUserLogin KeyCookie = "KeyUserLogin"

type Controller struct {
	log   *log.Logger
	db    dber
	cache cacher
}

type dber interface {
	UserGet(username, password string) (user.User, error)
	UserGetFromToken(token string) (user.User, error)
}

func New(log *log.Logger, db dber, cache cacher) *Controller {
	return &Controller{
		log,
		db,
		cache,
	}
}

// TODO: You know why this is bad, change it.
func (c *Controller) GetCookieUser2(w http.ResponseWriter, r *http.Request) (user.User, error) {
	cookie, err := r.Cookie(KeyUserLogin)
	if err != nil {
		return nil, err
	}

	user, err := c.db.UserGetFromToken(cookie.Value)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func (c *Controller) GetCookieUser(w http.ResponseWriter, r *http.Request) (user.User, error) {
	user, err := c.GetCookieUser2(w, r)
	if err != nil {
		// No user in cookie, lets check in basic auth.

		u, p, ok := r.BasicAuth()
		if !ok {
			return nil, fmt.Errorf("no user available")
		}

		user, err = c.db.UserGet(u, p)
		if err != nil {
			return nil, err
		}
	}

	return user, nil
}

func (c *Controller) SetCookieUser(w http.ResponseWriter, r *http.Request, user user.User) {
	var tok string
	if user != nil {
		tok = user.Token()
	}

	http.SetCookie(w, &http.Cookie{
		Name:     KeyUserLogin,
		Value:    tok,
		HttpOnly: true,
		MaxAge:   50000,
		Path:     "/",
	})
}

func (c *Controller) String(w http.ResponseWriter, r *http.Request, str string) {
	w.WriteHeader(http.StatusOK)
	w.Header().Add("Content-Type", "text/plain")
	fmt.Fprintf(w, str)
}

func (c *Controller) Error(w http.ResponseWriter, r *http.Request, code int, str string) {
	w.WriteHeader(code)
	w.Header().Add("Content-Type", "text/plain")
	fmt.Fprintf(w, str)
}

// TODO: You know why this is bad, change it.
func (c *Controller) HTML(w http.ResponseWriter, r *http.Request, tmpl *template.Template, data interface{}, cacheKeys ...string) {
	var cache cacheItem

	if strings.Contains(r.Header.Get("Accept"), "text/html") {
		// HTML response.
		buf := bytes.Buffer{}
		if err := tmpl.Execute(&buf, data); err != nil {
			c.log.Println(err)
			c.Error(w, r, http.StatusInternalServerError, "failed to build html response")
			return
		}

		cache = cacheItem{
			http.StatusOK,
			"text/html",
			buf.Bytes(),
		}

		// w.WriteHeader(http.StatusOK)
		// w.Header().Add("Content-Type", "text/html")
		// io.Copy(w, &buf)
	} else {
		// JSON response.
		bytes, err := json.Marshal(data)
		if err != nil {
			c.log.Println(err)
			c.Error(w, r, http.StatusInternalServerError, "failed to build json response")
			return
		}

		cache = cacheItem{
			http.StatusOK,
			"application/json",
			bytes,
		}

		// w.WriteHeader(http.StatusOK)
		// w.Header().Add("Content-Type", "application/json")
		// w.Write(bytes)
	}

	w.WriteHeader(cache.StatusCode)
	w.Header().Add("Content-Type", cache.ContentType)
	w.Write(cache.Body)

	if len(cacheKeys) > 0 {
		cacheKeys = append(cacheKeys, cache.ContentType)
		key := c.cache.Key(cacheKeys...)
		if err := c.cache.Set(key, cache); err != nil {
			c.log.Println("failed to save cache", err)
		}
	}
}

// Cache things.

type cacher interface {
	Key(...string) string
	Get(string, interface{}) error
	Set(string, interface{}) error
	Del(string) error
}

type cacheItem struct {
	StatusCode  int
	ContentType string
	Body        []byte
}

func (c *Controller) CacheTry(w http.ResponseWriter, r *http.Request, things ...string) (successful bool) {
	contentType := "application/json"
	if strings.Contains(r.Header.Get("Accept"), "text/html") {
		contentType = "text/html"
	}

	things = append(things, contentType)
	key := c.cache.Key(things...)

	var item cacheItem
	if err := c.cache.Get(key, &item); err != nil {
		return false
	}

	w.WriteHeader(item.StatusCode)
	w.Header().Add("Content-Type", item.ContentType)
	w.Write(item.Body)

	return true
}

func (c *Controller) CacheBreak(w http.ResponseWriter, r *http.Request, things ...string) {
	key := c.cache.Key(things...)
	err := c.cache.Del(key)
	if err != nil {
		c.log.Println("failed to remove cache for", key)
	}
}