Compare commits

..

49 Commits

Author SHA1 Message Date
e03fd78ea9 Merge branch 'devel' 2025-01-12 14:36:01 +01:00
370ef205a9 Merge branch 'devel' 2024-12-27 21:36:02 +01:00
d328ddb749 Merge branch 'devel' 2024-10-30 03:25:18 +01:00
5dc5590da9 Merge branch 'devel' 2024-10-30 02:24:50 +01:00
364112a0a4 Merge branch 'devel' 2024-10-30 02:14:16 +01:00
a38523e933 Merge branch 'devel' 2024-10-30 02:13:03 +01:00
200672dae2 Merge branch 'devel' 2024-10-28 18:28:31 +01:00
3d3aad88c8 Merge branch 'devel' 2024-10-28 18:27:33 +01:00
e4e43d1a83 Merge branch 'devel' 2024-10-28 18:02:56 +01:00
737a9ec314 Merge branch 'devel' 2024-10-27 15:29:50 +01:00
1ebe0380ee Merge branch 'devel' 2024-10-27 15:28:22 +01:00
d62d71b5d1 Merge branch 'devel' 2024-10-27 15:14:11 +01:00
b36e0ea503 Merge branch 'devel' 2024-10-04 16:06:57 +02:00
bc4d8fa37e Merge branch 'devel' 2024-09-28 13:59:34 +02:00
d2b21e7405 Merge branch 'devel' 2024-09-28 12:36:46 +02:00
e3c192359f Merge branch 'devel' 2024-09-11 18:15:31 +02:00
46532e4c85 Merge branch 'devel' 2024-09-11 18:15:07 +02:00
c722135a56 Merge branch 'devel' 2024-09-11 17:18:42 +02:00
887fa863bc Merge branch 'devel' 2024-09-10 19:43:22 +02:00
74d71cfb6a Merge branch 'devel' 2024-09-09 22:03:43 +02:00
ca7e7cddd3 Merge branch 'devel' 2024-09-08 16:22:59 +02:00
94431a2aa9 Merge branch 'devel' 2024-09-08 16:21:38 +02:00
5b1f20c5bc Merge branch 'devel' 2024-09-08 13:36:22 +02:00
d0c566f8df Merge branch 'devel' 2024-09-01 21:14:05 +02:00
5e586aa49a Merge branch 'devel' 2024-09-01 18:51:33 +02:00
66b2743d3d Merge branch 'devel' 2024-09-01 18:48:26 +02:00
3723b2b5e6 Merge branch 'devel' 2024-09-01 18:18:18 +02:00
ce788bfd50 Merge branch 'devel' 2024-09-01 12:54:12 +02:00
230a6278cc Merge branch 'devel' 2024-09-01 12:49:30 +02:00
42d6e0c198 Merge branch 'devel' 2024-08-31 12:17:41 +02:00
e1af2979af Merge branch 'devel' 2024-08-31 11:27:15 +02:00
f6dedc6f10 Merge branch 'devel' 2024-08-31 01:43:53 +02:00
cdf0a49550 Merge branch 'devel' 2024-08-31 01:38:28 +02:00
c3c0650210 Merge branch 'devel' 2024-08-31 01:00:55 +02:00
d077f700d8 Merge branch 'devel' 2024-08-31 00:36:48 +02:00
ec752b1c66 Merge branch 'devel' 2024-08-30 23:43:12 +02:00
46aef4f12f Merge branch 'devel' 2024-08-25 10:51:35 +02:00
1b29e328cf Merge branch 'devel' 2024-08-25 06:38:55 +02:00
e50cb819f3 Merge branch 'devel' 2024-08-23 21:45:30 +02:00
c32e38ca10 Merge branch 'devel' 2024-08-23 20:57:11 +02:00
d7c8c7a43a Merge branch 'devel' 2024-08-18 17:31:00 +02:00
1cd3edc90c Merge branch 'devel' 2024-08-18 12:06:29 +02:00
0e768c9f61 Merge branch 'devel' 2024-08-08 21:27:07 +02:00
1fcd775cc5 Merge branch 'devel' 2024-08-08 21:14:24 +02:00
203a1ed147 Implemented EasyMDE 2024-08-08 21:13:25 +02:00
ef1914ee5c Implemented article preview 2024-08-08 21:13:25 +02:00
084b101e31 Register f.ArticlePreviewHtmlData in init() 2024-08-08 21:13:25 +02:00
b2db128aa9 Shorten lines by referencing frontend as f and backend as b 2024-08-08 21:13:25 +02:00
081e880fb6 Change structure of code tor frontend and backend one 2024-08-08 21:13:25 +02:00
15 changed files with 489 additions and 396 deletions

View File

@ -12,6 +12,7 @@ args_bin = [
"-domain localhost", "-domain localhost",
"-feed tmp/cpolis.atom", "-feed tmp/cpolis.atom",
"-firebase tmp/firebase.json", "-firebase tmp/firebase.json",
"-gob tmp/cpolis.gob",
"-img-width 256", "-img-width 256",
"-link https://distrikt-ni-st.de", "-link https://distrikt-ni-st.de",
"-log tmp/cpolis.log", "-log tmp/cpolis.log",

View File

@ -12,48 +12,48 @@ import (
) )
type Config struct { type Config struct {
AESKeyFile string AESKeyFile string
ArticleDir string ArticleDir string
AtomFile string ConfigFile string
ConfigFile string DBName string
DBName string Description string
Description string Domain string
Domain string AtomFile string
FirebaseKey string FirebaseKey string
Link string GOBKeyFile string
LogFile string Link string
PDFDir string LogFile string
PicsDir string PDFDir string
Port string PicsDir string
Title string Port string
Version string Title string
WebDir string Version string
CookieExpiryHours int WebDir string
MaxBannerHeight int MaxBannerHeight int
MaxBannerWidth int MaxBannerWidth int
MaxImgHeight int MaxImgHeight int
MaxImgWidth int MaxImgWidth int
} }
func newConfig() *Config { func newConfig() *Config {
return &Config{ return &Config{
AESKeyFile: "/var/www/cpolis/aes.key", AESKeyFile: "/var/www/cpolis/aes.key",
ArticleDir: "/var/www/cpolis/articles", ArticleDir: "/var/www/cpolis/articles",
AtomFile: "/var/www/cpolis/cpolis.atom", AtomFile: "/var/www/cpolis/cpolis.atom",
ConfigFile: "/etc/cpolis/config.toml", ConfigFile: "/etc/cpolis/config.toml",
CookieExpiryHours: 24 * 30, DBName: "cpolis",
DBName: "cpolis", FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json", GOBKeyFile: "/var/www/cpolis/gob.key",
LogFile: "/var/log/cpolis.log", LogFile: "/var/log/cpolis.log",
MaxBannerHeight: 1080, MaxBannerHeight: 1080,
MaxBannerWidth: 1920, MaxBannerWidth: 1920,
MaxImgHeight: 1080, MaxImgHeight: 1080,
MaxImgWidth: 1920, MaxImgWidth: 1920,
PDFDir: "/var/www/cpolis/pdfs", PDFDir: "/var/www/cpolis/pdfs",
PicsDir: "/var/www/cpolis/pics", PicsDir: "/var/www/cpolis/pics",
Port: ":8080", Port: ":8080",
Version: "v0.15.0", Version: "v0.14.1",
WebDir: "/var/www/cpolis/web", WebDir: "/var/www/cpolis/web",
} }
} }
@ -116,13 +116,13 @@ func (c *Config) handleCliArgs() error {
flag.StringVar(&c.Description, "desc", c.Description, "channel description") flag.StringVar(&c.Description, "desc", c.Description, "channel description")
flag.StringVar(&c.Domain, "domain", c.Domain, "domain name") flag.StringVar(&c.Domain, "domain", c.Domain, "domain name")
flag.StringVar(&c.FirebaseKey, "firebase", c.FirebaseKey, "Firebase service account key file") flag.StringVar(&c.FirebaseKey, "firebase", c.FirebaseKey, "Firebase service account key file")
flag.StringVar(&c.GOBKeyFile, "gob", c.GOBKeyFile, "gob key file")
flag.StringVar(&c.Link, "link", c.Link, "channel Link") flag.StringVar(&c.Link, "link", c.Link, "channel Link")
flag.StringVar(&c.LogFile, "log", c.LogFile, "log file") flag.StringVar(&c.LogFile, "log", c.LogFile, "log file")
flag.StringVar(&c.PDFDir, "pdfs", c.PDFDir, "pdf directory") flag.StringVar(&c.PDFDir, "pdfs", c.PDFDir, "pdf directory")
flag.StringVar(&c.PicsDir, "pics", c.PicsDir, "pictures directory") flag.StringVar(&c.PicsDir, "pics", c.PicsDir, "pictures directory")
flag.StringVar(&c.Title, "title", c.Title, "channel title") flag.StringVar(&c.Title, "title", c.Title, "channel title")
flag.StringVar(&c.WebDir, "web", c.WebDir, "web directory") flag.StringVar(&c.WebDir, "web", c.WebDir, "web directory")
flag.IntVar(&c.CookieExpiryHours, "cookie-expiry-hours", c.CookieExpiryHours, "cookies expire after this amount of hours")
flag.IntVar(&c.MaxBannerHeight, "banner-height", c.MaxBannerHeight, "maximum banner height") flag.IntVar(&c.MaxBannerHeight, "banner-height", c.MaxBannerHeight, "maximum banner height")
flag.IntVar(&c.MaxBannerWidth, "banner-width", c.MaxBannerWidth, "maximum banner width") flag.IntVar(&c.MaxBannerWidth, "banner-width", c.MaxBannerWidth, "maximum banner width")
flag.IntVar(&c.MaxImgHeight, "img-height", c.MaxImgHeight, "maximum image height") flag.IntVar(&c.MaxImgHeight, "img-height", c.MaxImgHeight, "maximum image height")
@ -171,18 +171,6 @@ func (c *Config) setupConfig(cliConfig *Config) error {
return fmt.Errorf("error setting up directory: %v", err) return fmt.Errorf("error setting up directory: %v", err)
} }
if cliConfig.AtomFile != defaultConfig.AtomFile {
c.AtomFile = cliConfig.AtomFile
}
c.AtomFile, err = mkFile(c.AtomFile, 0644, 0744)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.CookieExpiryHours != defaultConfig.CookieExpiryHours {
c.CookieExpiryHours = cliConfig.CookieExpiryHours
}
if cliConfig.DBName != defaultConfig.DBName { if cliConfig.DBName != defaultConfig.DBName {
c.DBName = cliConfig.DBName c.DBName = cliConfig.DBName
} }
@ -194,11 +182,19 @@ func (c *Config) setupConfig(cliConfig *Config) error {
if cliConfig.Domain != defaultConfig.Domain { if cliConfig.Domain != defaultConfig.Domain {
c.Domain = cliConfig.Domain c.Domain = cliConfig.Domain
} }
domainStrings := strings.Split(c.Domain, ":") domainStrings := strings.Split(c.Domain, "/")
if domainStrings[0] != "http" && domainStrings[0] != "https" { if domainStrings[0] != "http:" && domainStrings[0] != "https:" {
c.Domain = "https://" + c.Domain c.Domain = "https://" + c.Domain
} }
if cliConfig.AtomFile != defaultConfig.AtomFile {
c.AtomFile = cliConfig.AtomFile
}
c.AtomFile, err = mkFile(c.AtomFile, 0644, 0744)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.FirebaseKey != defaultConfig.FirebaseKey { if cliConfig.FirebaseKey != defaultConfig.FirebaseKey {
c.FirebaseKey = cliConfig.FirebaseKey c.FirebaseKey = cliConfig.FirebaseKey
} }
@ -207,6 +203,14 @@ func (c *Config) setupConfig(cliConfig *Config) error {
return fmt.Errorf("error setting up file: %v", err) return fmt.Errorf("error setting up file: %v", err)
} }
if cliConfig.GOBKeyFile != defaultConfig.GOBKeyFile {
c.GOBKeyFile = cliConfig.GOBKeyFile
}
c.GOBKeyFile, err = mkFile(c.GOBKeyFile, 0600, 0700)
if err != nil {
return fmt.Errorf("error setting up file: %v", err)
}
if cliConfig.Link != defaultConfig.Link { if cliConfig.Link != defaultConfig.Link {
c.Link = cliConfig.Link c.Link = cliConfig.Link
} }

63
cmd/backend/sessions.go Normal file
View File

@ -0,0 +1,63 @@
package backend
import (
"crypto/rand"
"encoding/gob"
"fmt"
"io"
"os"
"github.com/gorilla/sessions"
)
type (
CookieStore struct{ sessions.CookieStore }
Session struct{ sessions.Session }
)
func NewKey() ([]byte, error) {
key := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return nil, fmt.Errorf("error generating key: %v", err)
}
return key, nil
}
func SaveKey(key []byte, filename string) error {
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("error creating key file: %v", err)
}
defer file.Close()
file.Chmod(0600)
if err = gob.NewEncoder(file).Encode(key); err != nil {
return fmt.Errorf("error ecoding key: %v", err)
}
return nil
}
func LoadKey(filename string) ([]byte, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("error opening key file: %v", err)
}
defer file.Close()
key := make([]byte, 32)
if err = gob.NewDecoder(file).Decode(&key); err != nil {
return nil, fmt.Errorf("error decoding key: %v", err)
}
return key, nil
}
func NewCookieStore(key []byte) *CookieStore {
store := sessions.NewCookieStore(key)
store.Options.Secure = true
store.Options.HttpOnly = true
return &CookieStore{*store}
}

View File

@ -9,9 +9,9 @@ import (
f "streifling.com/jason/cpolis/cmd/frontend" f "streifling.com/jason/cpolis/cmd/frontend"
) )
func ServeImage(c *b.Config, s map[string]*f.Session) http.HandlerFunc { func ServeImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := f.ManageSession(w, r, c, s); err != nil { if _, err := f.GetSession(w, r, c, s); err != nil {
if !tokenIsVerified(w, r, c) { if !tokenIsVerified(w, r, c) {
return return
} }

View File

@ -45,27 +45,33 @@ type EditorHTMLData struct {
Contributors []*b.User Contributors []*b.User
} }
func WriteArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func WriteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
var data *EditorHTMLData
if session.Values["article"] == nil {
data = &EditorHTMLData{Action: "submit", Article: new(b.Article), ArticleUsers: make(map[string]*ArticleUser)}
} else {
data = session.Values["article"].(*EditorHTMLData)
}
users, err := db.GetAllUsers(c) users, err := db.GetAllUsers(c)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
data := &EditorHTMLData{Action: "submit", Article: new(b.Article), ArticleUsers: make(map[string]*ArticleUser)}
for _, user := range users { for _, user := range users {
data.ArticleUsers[fmt.Sprint(user.LastName, user.FirstName, user.ID)] = &ArticleUser{User: user, ArticleRole: None} data.ArticleUsers[fmt.Sprint(user.LastName, user.FirstName, user.ID)] = &ArticleUser{User: user, ArticleRole: None}
} }
creator, err := db.GetUser(c, session.User.ID) creator, err := db.GetUser(c, session.Values["id"].(int64))
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -91,11 +97,19 @@ func WriteArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc
} }
} }
func SubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["article"] = nil
if err = session.Save(r, w); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -103,7 +117,7 @@ func SubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
Title: r.PostFormValue("article-title"), Title: r.PostFormValue("article-title"),
BannerLink: r.PostFormValue("article-banner-url"), BannerLink: r.PostFormValue("article-banner-url"),
Summary: r.PostFormValue("article-summary"), Summary: r.PostFormValue("article-summary"),
CreatorID: session.User.ID, CreatorID: session.Values["id"].(int64),
Published: false, Published: false,
Rejected: false, Rejected: false,
IsInIssue: r.PostFormValue("issue") == "on", IsInIssue: r.PostFormValue("issue") == "on",
@ -201,7 +215,7 @@ func SubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -213,11 +227,12 @@ func SubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
} }
} }
func ResubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -225,7 +240,7 @@ func ResubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerF
Title: r.PostFormValue("article-title"), Title: r.PostFormValue("article-title"),
BannerLink: r.PostFormValue("article-banner-url"), BannerLink: r.PostFormValue("article-banner-url"),
Summary: r.PostFormValue("article-summary"), Summary: r.PostFormValue("article-summary"),
CreatorID: session.User.ID, CreatorID: session.Values["id"].(int64),
IsInIssue: r.PostFormValue("issue") == "on", IsInIssue: r.PostFormValue("issue") == "on",
} }
@ -331,7 +346,7 @@ func ResubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerF
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -343,10 +358,11 @@ func ResubmitArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerF
} }
} }
func ShowUnpublishedUnrejectedAndPublishedRejectedArticles(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func ShowUnpublishedUnrejectedAndPublishedRejectedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -386,11 +402,12 @@ func ShowUnpublishedUnrejectedAndPublishedRejectedArticles(c *b.Config, db *b.DB
} }
} }
func ShowRejectedArticles(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func ShowRejectedArticles(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -408,7 +425,7 @@ func ShowRejectedArticles(c *b.Config, db *b.DB, s map[string]*Session) http.Han
data.MyIDs = make(map[int64]bool) data.MyIDs = make(map[int64]bool)
for _, article := range data.RejectedArticles { for _, article := range data.RejectedArticles {
if article.CreatorID == session.User.ID { if article.CreatorID == session.Values["id"].(int64) {
data.MyIDs[article.ID] = true data.MyIDs[article.ID] = true
} }
} }
@ -423,11 +440,12 @@ func ShowRejectedArticles(c *b.Config, db *b.DB, s map[string]*Session) http.Han
} }
} }
func ReviewRejectedArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -495,7 +513,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s map[string]*Session) http.Ha
data.ArticleUsers[fmt.Sprint(contributor.LastName, contributor.FirstName, contributor.ID)].ArticleRole = Contributor data.ArticleUsers[fmt.Sprint(contributor.LastName, contributor.FirstName, contributor.ID)].ArticleRole = Contributor
} }
creator, err := db.GetUser(c, session.User.ID) creator, err := db.GetUser(c, session.Values["id"].(int64))
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -527,11 +545,12 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s map[string]*Session) http.Ha
} }
} }
func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -605,7 +624,7 @@ func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFu
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -617,11 +636,12 @@ func PublishArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFu
} }
} }
func RejectArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func RejectArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -639,7 +659,7 @@ func RejectArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -651,10 +671,11 @@ func RejectArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
} }
} }
func ShowCurrentIssue(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func ShowCurrentIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -674,10 +695,11 @@ func ShowCurrentIssue(c *b.Config, db *b.DB, s map[string]*Session) http.Handler
} }
} }
func ShowPublishedArticles(c *b.Config, db *b.DB, s map[string]*Session, action string) http.HandlerFunc { func ShowPublishedArticles(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -710,10 +732,11 @@ func ShowPublishedArticles(c *b.Config, db *b.DB, s map[string]*Session, action
} }
} }
func ReviewArticle(c *b.Config, db *b.DB, s map[string]*Session, action, title, button string) http.HandlerFunc { func ReviewArticle(c *b.Config, db *b.DB, s *b.CookieStore, action, title, button string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -804,11 +827,12 @@ func ReviewArticle(c *b.Config, db *b.DB, s map[string]*Session, action, title,
} }
} }
func DeleteArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -844,7 +868,7 @@ func DeleteArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -856,11 +880,12 @@ func DeleteArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFun
} }
} }
func AllowEditArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func AllowEditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -937,7 +962,7 @@ func AllowEditArticle(c *b.Config, db *b.DB, s map[string]*Session) http.Handler
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html")) tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html"))
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil { if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
@ -948,10 +973,11 @@ func AllowEditArticle(c *b.Config, db *b.DB, s map[string]*Session) http.Handler
} }
} }
func EditArticle(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func EditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@ -1,99 +0,0 @@
package frontend
import (
"html/template"
"log"
"net/http"
"time"
b "streifling.com/jason/cpolis/cmd/backend"
)
func HomePage(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
numRows, err := db.CountEntries("users")
if err != nil {
log.Fatalln(err)
}
data := new(struct {
*UserHTMLData
Version string
})
data.UserHTMLData = &UserHTMLData{User: new(b.User)}
data.Version = c.Version
files := make([]string, 2)
files[0] = c.WebDir + "/templates/index.html"
if numRows == 0 {
data.Role = b.NonExistent
data.Title = "Erster Benutzer (Administrator)"
data.ButtonText = "Anlegen"
data.URL = "/user/add-first"
files[1] = c.WebDir + "/templates/edit-user.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
cookie, err := r.Cookie("cpolis_session")
if err != nil {
files[1] = c.WebDir + "/templates/login.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
return
}
session, ok := s[cookie.Value]
if !ok {
cookie.Expires = time.Now()
http.SetCookie(w, cookie)
files[1] = c.WebDir + "/templates/login.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
return
}
data.Role = session.User.Role
files[1] = c.WebDir + "/templates/hub.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}
}
func ShowHub(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s)
if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized)
return
}
data := new(struct{ Role int })
data.Role = session.User.Role
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}

View File

@ -9,10 +9,11 @@ import (
b "streifling.com/jason/cpolis/cmd/backend" b "streifling.com/jason/cpolis/cmd/backend"
) )
func UploadEasyMDEImage(c *b.Config, s map[string]*Session) http.HandlerFunc { func UploadEasyMDEImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -41,10 +42,11 @@ func UploadEasyMDEImage(c *b.Config, s map[string]*Session) http.HandlerFunc {
} }
} }
func UploadImage(c *b.Config, s map[string]*Session, fileKey, htmlFile, htmlTemplate string) http.HandlerFunc { func UploadImage(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@ -11,11 +11,19 @@ import (
b "streifling.com/jason/cpolis/cmd/backend" b "streifling.com/jason/cpolis/cmd/backend"
) )
func PublishLatestIssue(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["article"] = nil
if err = session.Save(r, w); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -41,7 +49,14 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s map[string]*Session) http.Handl
} }
authorIDs := make([]int64, 1) authorIDs := make([]int64, 1)
authorIDs[0] = session.User.ID var ok bool
if authorIDs[0], ok = session.Values["id"].(int64); !ok {
msg := "fälschlicherweise session.Values[\"id\"].(int64) für authorIDs[0] angenommen"
log.Println(msg)
http.Error(w, msg, http.StatusInternalServerError)
return
}
if err = db.WriteArticleAuthors(article.ID, authorIDs); err != nil { if err = db.WriteArticleAuthors(article.ID, authorIDs); err != nil {
log.Println(err) log.Println(err)
@ -86,8 +101,15 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s map[string]*Session) http.Handl
return return
} }
session.Values["issue-image"] = nil
if err = session.Save(r, w); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)

View File

@ -10,10 +10,11 @@ import (
b "streifling.com/jason/cpolis/cmd/backend" b "streifling.com/jason/cpolis/cmd/backend"
) )
func UploadPDF(c *b.Config, s map[string]*Session) http.HandlerFunc { func UploadPDF(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@ -1,102 +1,115 @@
package frontend package frontend
import ( import (
"context"
"errors"
"fmt" "fmt"
"html/template" "html/template"
"log" "log"
"net/http" "net/http"
"time"
"github.com/google/uuid"
b "streifling.com/jason/cpolis/cmd/backend" b "streifling.com/jason/cpolis/cmd/backend"
) )
type Session struct { func saveSession(w http.ResponseWriter, r *http.Request, s *b.CookieStore, u *b.User) error {
ctx context.Context session, err := s.Get(r, "cookie")
cancel context.CancelFunc if err != nil {
cookie *http.Cookie return fmt.Errorf("error getting session: %v", err)
User *b.User
}
func newSession(w http.ResponseWriter, c *b.Config, sessionExpiryChan chan<- string, user *b.User) *Session {
sessionID := uuid.New().String()
expires := time.Now().Add(time.Hour * time.Duration(c.CookieExpiryHours))
ctx, cancel := context.WithDeadline(context.Background(), expires)
session := &Session{
ctx: ctx,
cancel: cancel,
cookie: &http.Cookie{
Name: "cpolis_session",
Value: sessionID,
Expires: expires,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
},
User: user,
} }
go func() { session.Values["authenticated"] = true
<-session.ctx.Done() session.Values["id"] = u.ID
sessionExpiryChan <- session.cookie.Value session.Values["name"] = u.FirstName + u.LastName
session.cookie.Expires = time.Now() session.Values["role"] = u.Role
http.SetCookie(w, session.cookie) if err := session.Save(r, w); err != nil {
}() return fmt.Errorf("error saving session: %v", err)
}
return session return nil
} }
func StartSessions() (map[string]*Session, chan string) { // GetSession is used for verifying that the user is logged in and returns their session and an error.
sessions := make(map[string]*Session) func GetSession(w http.ResponseWriter, r *http.Request, c *b.Config, s *b.CookieStore) (*b.Session, error) {
sessionExpiryChan := make(chan string) msg := "Keine gültige Session. Bitte erneut anmelden."
go func() {
for sessionID := range sessionExpiryChan {
delete(sessions, sessionID)
}
}()
return sessions, sessionExpiryChan
}
// ManageSession is used for verifying that the user is logged in and returns
// their session and an error. It also handles cases where the user is not
// logged in.
func ManageSession(w http.ResponseWriter, r *http.Request, c *b.Config, s map[string]*Session) (*Session, error) {
tmpl, tmplErr := template.ParseFiles(c.WebDir+"/templates/index.html", c.WebDir+"/templates/login.html") tmpl, tmplErr := template.ParseFiles(c.WebDir+"/templates/index.html", c.WebDir+"/templates/login.html")
cookie, err := r.Cookie("cpolis_session") tmpSession, err := s.Get(r, "cookie")
if err != nil { if err != nil {
if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", nil); err != nil { if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg); err != nil {
return nil, fmt.Errorf("error executing template: %v", err) return nil, fmt.Errorf("error executing template: %v", err)
} }
return nil, fmt.Errorf("error getting session: %v", err)
return nil, errors.New("no cookie set")
} }
session, ok := s[cookie.Value] session := &b.Session{Session: *tmpSession}
if !ok { if session.IsNew {
cookie.Expires = time.Now() if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg); err != nil {
http.SetCookie(w, cookie)
if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", nil); err != nil {
return nil, fmt.Errorf("error executing template: %v", err) return nil, fmt.Errorf("error executing template: %v", err)
} }
return nil, errors.New("session does not exist")
} }
session.cookie.Expires = time.Now().Add(time.Hour * time.Duration(c.CookieExpiryHours))
http.SetCookie(w, session.cookie)
return session, nil return session, nil
} }
func Login(c *b.Config, db *b.DB, s map[string]*Session, sessionExpiryChan chan string) http.HandlerFunc { func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
numRows, err := db.CountEntries("users")
if err != nil {
log.Fatalln(err)
}
data := new(struct {
*UserHTMLData
Version string
})
data.UserHTMLData = &UserHTMLData{User: new(b.User)}
data.Version = c.Version
files := make([]string, 2)
files[0] = c.WebDir + "/templates/index.html"
if numRows == 0 {
data.Role = b.NonExistent
data.Title = "Erster Benutzer (Administrator)"
data.ButtonText = "Anlegen"
data.URL = "/user/add-first"
files[1] = c.WebDir + "/templates/edit-user.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
session, err := s.Get(r, "cookie")
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if auth, ok := session.Values["authenticated"].(bool); auth && ok {
data.Role = session.Values["role"].(int)
files[1] = c.WebDir + "/templates/hub.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else {
data.Role = b.Author
files[1] = c.WebDir + "/templates/login.html"
tmpl, err := template.ParseFiles(files...)
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}
}
}
func Login(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
userName := r.PostFormValue("username") userName := r.PostFormValue("username")
password := r.PostFormValue("password") password := r.PostFormValue("password")
@ -120,9 +133,11 @@ func Login(c *b.Config, db *b.DB, s map[string]*Session, sessionExpiryChan chan
return return
} }
session := newSession(w, c, sessionExpiryChan, user) if err := saveSession(w, r, s, user); err != nil {
s[session.cookie.Value] = session log.Println(err)
http.SetCookie(w, session.cookie) http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user); err != nil { if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user); err != nil {
@ -133,32 +148,52 @@ func Login(c *b.Config, db *b.DB, s map[string]*Session, sessionExpiryChan chan
} }
} }
func Logout(c *b.Config, s map[string]*Session) http.HandlerFunc { func Logout(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
tmpl, tmplErr := template.ParseFiles(c.WebDir + "/templates/login.html") session, err := GetSession(w, r, c, s)
cookie, err := r.Cookie("cpolis_session")
if err != nil { if err != nil {
if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", nil); err != nil { log.Println(err)
log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusInternalServerError) return
return
}
} }
cookie.Expires = time.Now()
http.SetCookie(w, cookie)
session, ok := s[cookie.Value] session.Options.MaxAge = -1
if !ok { if err = session.Save(r, w); err != nil {
if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", nil); err != nil { log.Println(err)
log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusInternalServerError) return
return
}
} }
session.cancel()
if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", nil); err != nil { tmpl, err := template.ParseFiles(c.WebDir + "/templates/login.html")
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", nil); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}
func ShowHub(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, err := GetSession(w, r, c, s)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session.Values["article"] = nil
if err = session.Save(r, w); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := new(struct{ Role int })
data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return

View File

@ -8,10 +8,11 @@ import (
b "streifling.com/jason/cpolis/cmd/backend" b "streifling.com/jason/cpolis/cmd/backend"
) )
func CreateTag(c *b.Config, s map[string]*Session) http.HandlerFunc { func CreateTag(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -24,11 +25,12 @@ func CreateTag(c *b.Config, s map[string]*Session) http.HandlerFunc {
} }
} }
func AddTag(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func AddTag(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -40,7 +42,7 @@ func AddTag(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
db.AddTag(tag) db.AddTag(tag)
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)

View File

@ -43,10 +43,11 @@ func sortUsersByName(users []*b.User) {
}) })
} }
func CreateUser(c *b.Config, s map[string]*Session) http.HandlerFunc { func CreateUser(c *b.Config, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -66,11 +67,12 @@ func CreateUser(c *b.Config, s map[string]*Session) http.HandlerFunc {
} }
} }
func AddUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -132,7 +134,7 @@ func AddUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -144,15 +146,16 @@ func AddUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
} }
func EditSelf(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func EditSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
user, err := db.GetUser(c, session.User.ID) user, err := db.GetUser(c, session.Values["id"].(int64))
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -176,16 +179,17 @@ func EditSelf(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
} }
func UpdateSelf(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
user := &b.User{ user := &b.User{
ID: session.User.ID, ID: session.Values["id"].(int64),
UserName: r.PostFormValue("username"), UserName: r.PostFormValue("username"),
FirstName: r.PostFormValue("first-name"), FirstName: r.PostFormValue("first-name"),
LastName: r.PostFormValue("last-name"), LastName: r.PostFormValue("last-name"),
@ -240,7 +244,7 @@ func UpdateSelf(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)
@ -252,7 +256,7 @@ func UpdateSelf(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
} }
func AddFirstUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var err error var err error
user := &b.User{ user := &b.User{
@ -299,6 +303,12 @@ func AddFirstUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc
return return
} }
if err := saveSession(w, r, s, user); err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if _, err := db.AddIssue(); err != nil { if _, err := db.AddIssue(); err != nil {
log.Println(err) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
@ -317,11 +327,12 @@ func AddFirstUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc
} }
} }
func ShowAllUsers(c *b.Config, db *b.DB, s map[string]*Session, action string) http.HandlerFunc { func ShowAllUsers(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -337,7 +348,7 @@ func ShowAllUsers(c *b.Config, db *b.DB, s map[string]*Session, action string) h
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
delete(data.Users, session.User.ID) delete(data.Users, session.Values["id"].(int64))
tmpl, err := template.ParseFiles(c.WebDir + "/templates/show-all-users.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/show-all-users.html")
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil { if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
@ -348,10 +359,11 @@ func ShowAllUsers(c *b.Config, db *b.DB, s map[string]*Session, action string) h
} }
} }
func EditUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func EditUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if _, err := ManageSession(w, r, c, s); err != nil { if _, err := GetSession(w, r, c, s); err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -386,11 +398,12 @@ func EditUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
} }
func UpdateUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func UpdateUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -460,7 +473,7 @@ func UpdateUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html")) tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html"))
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil { if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
@ -471,11 +484,12 @@ func UpdateUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
} }
func DeleteUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc { func DeleteUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
session, err := ManageSession(w, r, c, s) session, err := GetSession(w, r, c, s)
if err != nil { if err != nil {
http.Error(w, "Die Session ist abgelaufen. Bitte erneut anmelden.", http.StatusUnauthorized) log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -493,7 +507,7 @@ func DeleteUser(c *b.Config, db *b.DB, s map[string]*Session) http.HandlerFunc {
} }
data := new(struct{ Role int }) data := new(struct{ Role int })
data.Role = session.User.Role data.Role = session.Values["role"].(int)
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html") tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
tmpl = template.Must(tmpl, err) tmpl = template.Must(tmpl, err)

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/gob"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -10,6 +11,10 @@ import (
f "streifling.com/jason/cpolis/cmd/frontend" f "streifling.com/jason/cpolis/cmd/frontend"
) )
func init() {
gob.Register(b.User{})
}
func main() { func main() {
config, err := b.HandleConfig() config, err := b.HandleConfig()
if err != nil { if err != nil {
@ -29,59 +34,68 @@ func main() {
} }
defer db.Close() defer db.Close()
sessions, sessionExpiryChan := f.StartSessions() key, err := b.LoadKey(config.GOBKeyFile)
defer close(sessionExpiryChan) if err != nil {
key, err = b.NewKey()
if err != nil {
log.Fatalln(err)
}
if err = b.SaveKey(key, config.GOBKeyFile); err != nil {
log.Fatalln(err)
}
}
store := b.NewCookieStore(key)
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/web/static/", http.StripPrefix("/web/static/", mux.Handle("/web/static/", http.StripPrefix("/web/static/",
http.FileServer(http.Dir(config.WebDir+"/static/")))) http.FileServer(http.Dir(config.WebDir+"/static/"))))
mux.HandleFunc("/", f.HomePage(config, db, sessions)) mux.HandleFunc("/", f.HomePage(config, db, store))
mux.HandleFunc("GET /article/allow-edit/{id}", f.AllowEditArticle(config, db, sessions)) mux.HandleFunc("GET /article/allow-edit/{id}", f.AllowEditArticle(config, db, store))
mux.HandleFunc("GET /article/all-published/review-edit", f.ShowPublishedArticles(config, db, sessions, "review-edit")) mux.HandleFunc("GET /article/all-published/review-edit", f.ShowPublishedArticles(config, db, store, "review-edit"))
mux.HandleFunc("GET /article/all-published/delete", f.ShowPublishedArticles(config, db, sessions, "review-delete")) mux.HandleFunc("GET /article/all-published/delete", f.ShowPublishedArticles(config, db, store, "review-delete"))
mux.HandleFunc("GET /article/all-rejected", f.ShowRejectedArticles(config, db, sessions)) mux.HandleFunc("GET /article/all-rejected", f.ShowRejectedArticles(config, db, store))
mux.HandleFunc("GET /article/all-unpublished-unrejected-and-published-rejected", f.ShowUnpublishedUnrejectedAndPublishedRejectedArticles(config, db, sessions)) mux.HandleFunc("GET /article/all-unpublished-unrejected-and-published-rejected", f.ShowUnpublishedUnrejectedAndPublishedRejectedArticles(config, db, store))
mux.HandleFunc("GET /article/delete/{id}", f.DeleteArticle(config, db, sessions)) mux.HandleFunc("GET /article/delete/{id}", f.DeleteArticle(config, db, store))
mux.HandleFunc("GET /article/edit/{id}", f.EditArticle(config, db, sessions)) mux.HandleFunc("GET /article/edit/{id}", f.EditArticle(config, db, store))
mux.HandleFunc("GET /article/publish/{id}", f.PublishArticle(config, db, sessions)) mux.HandleFunc("GET /article/publish/{id}", f.PublishArticle(config, db, store))
mux.HandleFunc("GET /article/reject/{id}", f.RejectArticle(config, db, sessions)) mux.HandleFunc("GET /article/reject/{id}", f.RejectArticle(config, db, store))
mux.HandleFunc("GET /article/review-delete/{id}", f.ReviewArticle(config, db, sessions, "delete", "Artikel löschen", "Löschen")) mux.HandleFunc("GET /article/review-delete/{id}", f.ReviewArticle(config, db, store, "delete", "Artikel löschen", "Löschen"))
mux.HandleFunc("GET /article/review-edit/{id}", f.ReviewArticle(config, db, sessions, "allow-edit", "Artikel bearbeiten", "Bearbeiten erlauben")) mux.HandleFunc("GET /article/review-edit/{id}", f.ReviewArticle(config, db, store, "allow-edit", "Artikel bearbeiten", "Bearbeiten erlauben"))
mux.HandleFunc("GET /article/review-rejected/{id}", f.ReviewRejectedArticle(config, db, sessions)) mux.HandleFunc("GET /article/review-rejected/{id}", f.ReviewRejectedArticle(config, db, store))
mux.HandleFunc("GET /article/review-unpublished/{id}", f.ReviewArticle(config, db, sessions, "publish", "Artikel veröffentlichen", "Veröffentlichen")) mux.HandleFunc("GET /article/review-unpublished/{id}", f.ReviewArticle(config, db, store, "publish", "Artikel veröffentlichen", "Veröffentlichen"))
mux.HandleFunc("GET /article/serve/{id}", c.ServeArticle(config, db)) mux.HandleFunc("GET /article/serve/{id}", c.ServeArticle(config, db))
mux.HandleFunc("GET /article/serve/{id}/clicks", c.ServeClicks(db)) mux.HandleFunc("GET /article/serve/{id}/clicks", c.ServeClicks(db))
mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, sessions)) mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, store))
mux.HandleFunc("GET /atom/serve", c.ServeAtomFeed(config)) mux.HandleFunc("GET /atom/serve", c.ServeAtomFeed(config))
mux.HandleFunc("GET /hub", f.ShowHub(config, db, sessions)) mux.HandleFunc("GET /hub", f.ShowHub(config, db, store))
mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config, sessions)) mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config, store))
mux.HandleFunc("GET /issue/this", f.ShowCurrentIssue(config, db, sessions)) mux.HandleFunc("GET /issue/this", f.ShowCurrentIssue(config, db, store))
mux.HandleFunc("GET /logout", f.Logout(config, sessions)) mux.HandleFunc("GET /logout", f.Logout(config, store))
mux.HandleFunc("GET /pdf/get-list", c.ServePDFList(config)) mux.HandleFunc("GET /pdf/get-list", c.ServePDFList(config))
mux.HandleFunc("GET /pdf/serve/{id}", c.ServePDF(config)) mux.HandleFunc("GET /pdf/serve/{id}", c.ServePDF(config))
mux.HandleFunc("GET /tag/create", f.CreateTag(config, sessions)) mux.HandleFunc("GET /tag/create", f.CreateTag(config, store))
mux.HandleFunc("GET /user/create", f.CreateUser(config, sessions)) mux.HandleFunc("GET /user/create", f.CreateUser(config, store))
mux.HandleFunc("GET /user/delete/{id}", f.DeleteUser(config, db, sessions)) mux.HandleFunc("GET /user/delete/{id}", f.DeleteUser(config, db, store))
mux.HandleFunc("GET /user/edit/{id}", f.EditUser(config, db, sessions)) mux.HandleFunc("GET /user/edit/{id}", f.EditUser(config, db, store))
mux.HandleFunc("GET /user/edit/self", f.EditSelf(config, db, sessions)) mux.HandleFunc("GET /user/edit/self", f.EditSelf(config, db, store))
mux.HandleFunc("GET /user/show-all/delete", f.ShowAllUsers(config, db, sessions, "delete")) mux.HandleFunc("GET /user/show-all/delete", f.ShowAllUsers(config, db, store, "delete"))
mux.HandleFunc("GET /user/show-all/edit", f.ShowAllUsers(config, db, sessions, "edit")) mux.HandleFunc("GET /user/show-all/edit", f.ShowAllUsers(config, db, store, "edit"))
mux.HandleFunc("POST /article/resubmit/{id}", f.ResubmitArticle(config, db, sessions)) mux.HandleFunc("POST /article/resubmit/{id}", f.ResubmitArticle(config, db, store))
mux.HandleFunc("POST /article/submit", f.SubmitArticle(config, db, sessions)) mux.HandleFunc("POST /article/submit", f.SubmitArticle(config, db, store))
mux.HandleFunc("POST /article/upload-banner", f.UploadImage(config, sessions, "article-banner", "editor.html", "article-banner-template")) mux.HandleFunc("POST /article/upload-banner", f.UploadImage(config, store, "article-banner", "editor.html", "article-banner-template"))
mux.HandleFunc("POST /article/upload-image", f.UploadEasyMDEImage(config, sessions)) mux.HandleFunc("POST /article/upload-image", f.UploadEasyMDEImage(config, store))
mux.HandleFunc("POST /issue/publish", f.PublishLatestIssue(config, db, sessions)) mux.HandleFunc("POST /issue/publish", f.PublishLatestIssue(config, db, store))
mux.HandleFunc("POST /issue/upload-banner", f.UploadImage(config, sessions, "issue-banner", "current-issue.html", "issue-banner-template")) mux.HandleFunc("POST /issue/upload-banner", f.UploadImage(config, store, "issue-banner", "current-issue.html", "issue-banner-template"))
mux.HandleFunc("POST /login", f.Login(config, db, sessions, sessionExpiryChan)) mux.HandleFunc("POST /login", f.Login(config, db, store))
mux.HandleFunc("POST /pdf/upload", f.UploadPDF(config, sessions)) mux.HandleFunc("POST /pdf/upload", f.UploadPDF(config, store))
mux.HandleFunc("POST /tag/add", f.AddTag(config, db, sessions)) mux.HandleFunc("POST /tag/add", f.AddTag(config, db, store))
mux.HandleFunc("POST /user/add", f.AddUser(config, db, sessions)) mux.HandleFunc("POST /user/add", f.AddUser(config, db, store))
mux.HandleFunc("POST /user/add-first", f.AddFirstUser(config, db, sessions)) mux.HandleFunc("POST /user/add-first", f.AddFirstUser(config, db, store))
mux.HandleFunc("POST /user/update/{id}", f.UpdateUser(config, db, sessions)) mux.HandleFunc("POST /user/update/{id}", f.UpdateUser(config, db, store))
mux.HandleFunc("POST /user/update/self", f.UpdateSelf(config, db, sessions)) mux.HandleFunc("POST /user/update/self", f.UpdateSelf(config, db, store))
mux.HandleFunc("POST /user/upload-profile-pic", f.UploadImage(config, sessions, "upload-profile-pic", "edit-user.html", "profile-pic-template")) mux.HandleFunc("POST /user/upload-profile-pic", f.UploadImage(config, store, "upload-profile-pic", "edit-user.html", "profile-pic-template"))
log.Fatalln(http.ListenAndServe(config.Port, mux)) log.Fatalln(http.ListenAndServe(config.Port, mux))
} }

2
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/go-sql-driver/mysql v1.8.1 github.com/go-sql-driver/mysql v1.8.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.4.0
github.com/microcosm-cc/bluemonday v1.0.27 github.com/microcosm-cc/bluemonday v1.0.27
github.com/yuin/goldmark v1.7.8 github.com/yuin/goldmark v1.7.8
golang.org/x/crypto v0.32.0 golang.org/x/crypto v0.32.0
@ -48,6 +49,7 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.33.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.33.0 // indirect

6
go.sum
View File

@ -80,6 +80,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
@ -92,6 +94,10 @@ github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrk
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=