This commit is contained in:
m15o 2021-12-06 07:14:20 +01:00
parent 3dd8ca4c27
commit ae71abda0b
13 changed files with 113 additions and 22 deletions

View file

@ -1,13 +1,16 @@
package handler package handler
import ( import (
"errors"
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"log" "log"
"net/http" "net/http"
"net/url"
"status/config" "status/config"
"status/storage" "status/storage"
"status/web/session" "status/web/session"
"strings"
) )
func serverError(w http.ResponseWriter, err error) { func serverError(w http.ResponseWriter, err error) {
@ -30,28 +33,24 @@ type Handler struct {
sess *session.Session sess *session.Session
} }
//func (h *Handler) getUser(r *http.Request) (string, error) { func protectClickJacking(w http.ResponseWriter) {
// user, err := h.sess.Get(r) w.Header().Set("X-Frame-Options", "DENY")
// if err != nil { w.Header().Set("Content-Security-Policy", "frame-ancestors 'none'")
// return "", err }
// }
// if h.cfg.Env != "PROD" {
// return user, err
// }
// // Removes "https://" from referer before checking.
// // Thank you crussel for this fix!
// if !strings.HasPrefix(r.Referer()[8:], h.cfg.Host) && !strings.HasPrefix(r.Referer()[8:], user+h.cfg.Host) {
// err = errors.New("wrong referer")
// }
// return user, err
//}
func (h *Handler) getUser(r *http.Request) (string, error) { func (h *Handler) getUser(r *http.Request) (string, error) {
user, err := h.sess.Get(r) user, err := h.sess.Get(r)
if err != nil { if err != nil {
return "", err return "", err
} }
return user, nil u, err := url.Parse(r.Referer())
if err != nil {
return "", err
}
if strings.HasPrefix(u.Path, "/users") {
err = errors.New("forbidden access")
}
return user, err
} }
func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http.Handler, error) { func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http.Handler, error) {
@ -84,9 +83,11 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
router.HandleFunc("/about/status-updater", h.showStatusUpdaterView).Methods(http.MethodGet) router.HandleFunc("/about/status-updater", h.showStatusUpdaterView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}.atom", h.showAtomView).Methods(http.MethodGet) router.HandleFunc("/users/{user}.atom", h.showAtomView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet) router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.json", h.showUserStatusView).Methods(http.MethodGet) router.HandleFunc("/users/{user}/status", h.showUserStatusView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.json", h.showUserStatusJSONView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet) router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/badge.png", h.showUserStatusBadgeView).Methods(http.MethodGet)
router.PathPrefix("/assets/").Handler( router.PathPrefix("/assets/").Handler(
http.StripPrefix("/assets/", http.StripPrefix("/assets/",
http.FileServer( http.FileServer(

View file

@ -297,6 +297,21 @@ var TplMap = map[string]string{
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
{{ end }}`, {{ end }}`,
"status": `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>status cafe</title>
<link rel="stylesheet" href="/assets/style.css"/>
<meta name="description" content="your friends' updates">
</head>
<body>
{{ if .status.Content }}
{{ template "status" .status }}
{{ end }}
</body>
</html>`,
"status-updater": `{{ define "content" }} "status-updater": `{{ define "content" }}
<section> <section>
<h1>Status Updater</h1> <h1>Status Updater</h1>
@ -314,7 +329,7 @@ var TplMap = map[string]string{
Drag the following link to your bookmarks toolbar: Drag the following link to your bookmarks toolbar:
</p> </p>
<p> <p>
<a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=250'))">status updater</a> <a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=290'))">status updater</a>
</p> </p>
<p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p> <p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p>
</section> </section>

View file

@ -15,7 +15,7 @@
Drag the following link to your bookmarks toolbar: Drag the following link to your bookmarks toolbar:
</p> </p>
<p> <p>
<a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=250'))">status updater</a> <a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=290'))">status updater</a>
</p> </p>
<p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p> <p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p>
</section> </section>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>status cafe</title>
<link rel="stylesheet" href="/assets/style.css"/>
<meta name="description" content="your friends' updates">
</head>
<body>
{{ if .status.Content }}
{{ template "status" .status }}
{{ end }}
</body>
</html>

View file

@ -11,6 +11,7 @@ type Update struct {
} }
func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) { func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
protectClickJacking(w)
user, _ := h.sess.Get(r) user, _ := h.sess.Get(r)
statuses, err := h.storage.LatestStatuses() statuses, err := h.storage.LatestStatuses()
if err != nil { if err != nil {

View file

@ -3,6 +3,7 @@ package handler
import "net/http" import "net/http"
func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) { func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
protectClickJacking(w)
username, err := h.getUser(r) username, err := h.getUser(r)
if err != nil { if err != nil {
unauthorized(w) unauthorized(w)

View file

@ -6,6 +6,7 @@ import (
) )
func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) { func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) {
protectClickJacking(w)
_, err := h.getUser(r) _, err := h.getUser(r)
if err != nil { if err != nil {
unauthorized(w) unauthorized(w)

View file

@ -19,6 +19,7 @@ func RouteInt64Param(r *http.Request, param string) int64 {
} }
func (h *Handler) showEditStatusView(w http.ResponseWriter, r *http.Request) { func (h *Handler) showEditStatusView(w http.ResponseWriter, r *http.Request) {
protectClickJacking(w)
user, err := h.getUser(r) user, err := h.getUser(r)
if err != nil { if err != nil {
unauthorized(w) unauthorized(w)

View file

@ -6,6 +6,7 @@ import (
) )
func (h *Handler) handleRemoveStatus(w http.ResponseWriter, r *http.Request) { func (h *Handler) handleRemoveStatus(w http.ResponseWriter, r *http.Request) {
protectClickJacking(w)
user, err := h.getUser(r) user, err := h.getUser(r)
if err != nil { if err != nil {
unauthorized(w) unauthorized(w)

View file

@ -16,7 +16,7 @@ func (h *Handler) initTpl() {
for name, content := range TplMap { for name, content := range TplMap {
views[name] = template.Must(template.New("main").Funcs(template.FuncMap{ views[name] = template.Must(template.New("main").Funcs(template.FuncMap{
"faces": func() []string { "faces": func() []string {
return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺"} return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺", "📖", "🔥", "❄️", "✨", "💡", "🎼"}
}}).Parse(commonTemplates + content)) }}).Parse(commonTemplates + content))
} }
} }

View file

@ -15,6 +15,7 @@ import (
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
"status/model"
"strconv" "strconv"
"time" "time"
@ -96,7 +97,7 @@ type statusjson struct {
TimeAgo string `json:"timeAgo"` TimeAgo string `json:"timeAgo"`
} }
func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) { func (h *Handler) showUserStatusJSONView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"] user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) { if !h.storage.UserExists(user) {
notFound(w) notFound(w)
@ -119,6 +120,19 @@ func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
if err != nil {
serverError(w, err)
return
}
var status model.Status
if len(statuses) > 0 {
status = statuses[0]
}
h.view("status").Execute(w, map[string]interface{}{"status": status})
}
func drawText(canvas *image.RGBA, text string) error { func drawText(canvas *image.RGBA, text string) error {
var ( var (
fgColor image.Image fgColor image.Image
@ -162,6 +176,46 @@ func createAvatar(text string) *image.RGBA {
return background return background
} }
func (h *Handler) showUserStatusBadgeView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) {
notFound(w)
return
}
statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
if err != nil {
serverError(w, err)
return
}
var username string
if len(statuses) > 0 {
username = statuses[0].User
}
background := "#f0ffff"
dc := gg.NewContext(88, 31)
dc.SetHexColor(background)
dc.Clear()
dc.SetHexColor("#191970")
font, err := truetype.Parse(goregular.TTF)
if err != nil {
panic("")
}
face := truetype.NewFace(font, &truetype.Options{
Size: 12,
})
dc.SetFontFace(face)
if err != nil {
fmt.Println(err)
}
//dc.DrawString(username, 10, 14)
dc.DrawStringAnchored(username, 88/2, 8, 0.5, 0.5)
dc.DrawStringAnchored("status.cafe", 88/2, 20, 0.5, 0.5)
dc.EncodePNG(w)
}
func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) { func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) {
user := mux.Vars(r)["user"] user := mux.Vars(r)["user"]
if !h.storage.UserExists(user) { if !h.storage.UserExists(user) {

View file

@ -15,7 +15,7 @@ fetch("https://status.cafe/users/` + name + `/status.json")
document.getElementById("statuscafe-content").innerHTML = "No status yet." document.getElementById("statuscafe-content").innerHTML = "No status yet."
return return
} }
document.getElementById("statuscafe-username").innerHTML = '<a href="https://status.cafe/users/` + name + `">' + r.author + '</a> ' + r.face + ' ' + r.timeAgo document.getElementById("statuscafe-username").innerHTML = '<a href="https://status.cafe/users/` + name + `" target="_blank">' + r.author + '</a> ' + r.face + ' ' + r.timeAgo
document.getElementById("statuscafe-content").innerHTML = r.content document.getElementById("statuscafe-content").innerHTML = r.content
}) })
`)) `))

View file

@ -18,6 +18,7 @@ func New(key string, storage *storage.Storage) *Session {
store := sessions.NewCookieStore([]byte(key)) store := sessions.NewCookieStore([]byte(key))
store.Options = &sessions.Options{ store.Options = &sessions.Options{
HttpOnly: true, HttpOnly: true,
Secure: true,
MaxAge: 86400 * 30, MaxAge: 86400 * 30,
} }
return &Session{ return &Session{