From ee2bfd95cbce260a82c311cb22f54b089a105ab0 Mon Sep 17 00:00:00 2001 From: Meutel Date: Sat, 29 Jul 2017 13:54:23 +0200 Subject: [PATCH] Photo blog 2: unstyled, no auth --- .gitignore | 1 + photoblog/admin/admin.go | 12 +++ photoblog/main.go | 39 +++++++--- photoblog/photo/photo.go | 134 ++++++++++++++++++++++++++++++++ photoblog/templates/header.html | 2 +- photoblog/templates/home.html | 20 ++++- photoblog/templates/upload.html | 14 ++++ 7 files changed, 207 insertions(+), 15 deletions(-) create mode 100644 photoblog/photo/photo.go create mode 100644 photoblog/templates/upload.html diff --git a/.gitignore b/.gitignore index 9603335..b48c102 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,4 @@ authcookie/authcookie cert.pem key.pem photoblog/photoblog +photoblog/data diff --git a/photoblog/admin/admin.go b/photoblog/admin/admin.go index ba4ef01..589ed61 100644 --- a/photoblog/admin/admin.go +++ b/photoblog/admin/admin.go @@ -24,6 +24,7 @@ func (app *AuthCookie) LoginPage(res http.ResponseWriter, req *http.Request) { if password == "" { formErr["password"] = "Empty password" } + // FIXME verifiy password with file in data dir if len(formErr) == 0 { app.SaveUsername(username, res, req) RedirectHome(res, req) @@ -47,6 +48,17 @@ func (app *AuthCookie) SaveUsername(username string, res http.ResponseWriter, re session.Values["username"] = username session.Save(req, res) } +func (app *AuthCookie) IsLoggedIn(res http.ResponseWriter, req *http.Request) bool { + session := app.CurrentSession(res, req) + return session != nil && session.Values["username"] != "" +} +func (app *AuthCookie) Username(res http.ResponseWriter, req *http.Request) string { + session := app.CurrentSession(res, req) + if session == nil { + return "" + } + return session.Values["username"].(string) +} func RedirectHome(res http.ResponseWriter, req *http.Request) { http.Redirect(res, req, "/", http.StatusSeeOther) } diff --git a/photoblog/main.go b/photoblog/main.go index 658367d..119fbc3 100644 --- a/photoblog/main.go +++ b/photoblog/main.go @@ -1,38 +1,55 @@ package main import ( + "fmt" "html/template" "log" "net/http" + "os" + "strings" "meutel.net/meutel/go-examples/photoblog/admin" + "meutel.net/meutel/go-examples/photoblog/photo" "github.com/gorilla/sessions" ) -type PhotoBlog struct { - *admin.AuthCookie - Templates *template.Template -} - -func (app *PhotoBlog) HomePage(res http.ResponseWriter, req *http.Request) { - app.Templates.ExecuteTemplate(res, "home.html", app.CurrentSession(res, req).Values) -} - func main() { tpl, err := template.ParseGlob("templates/*.html") if err != nil { log.Fatalln(err) } + dataInfo, err := os.Stat("data") + if !dataInfo.IsDir() { + log.Fatalln("data is not a directory") + } + data, err := os.Open("data") + if err != nil { + log.Fatalln(err) + } + defer data.Close() + admin := admin.AuthCookie{ Templates: tpl, Store: sessions.NewCookieStore([]byte("flQ6QzM/c3Jtdl9ycDx6OXRIfFgK")), } - app := PhotoBlog{ + app := photo.PhotoBlog{ AuthCookie: &admin, Templates: tpl, + DataDir: data, } - http.HandleFunc("/", app.HomePage) + + fileServer := http.FileServer(http.Dir(data.Name())) + + http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { + fmt.Println(req.RequestURI) + if strings.HasPrefix(req.RequestURI, "/data") { + http.StripPrefix("/data/", fileServer).ServeHTTP(res, req) + } else { + app.HomePage(res, req) + } + }) + http.HandleFunc("/upload", app.UploadPage) http.HandleFunc("/login", app.LoginPage) http.HandleFunc("/logout", app.LogoutPage) http.ListenAndServeTLS(":9443", "../cert.pem", "../key.pem", nil) diff --git a/photoblog/photo/photo.go b/photoblog/photo/photo.go new file mode 100644 index 0000000..1fe8dc2 --- /dev/null +++ b/photoblog/photo/photo.go @@ -0,0 +1,134 @@ +package photo + +import ( + "fmt" + "html/template" + "io" + "net/http" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "meutel.net/meutel/go-examples/photoblog/admin" +) + +const ( + DISPLAY_SIZE = 5 // number of photos displayed on home +) + +var ( + PHOTOEXT = [3]string{".jpg", ".jpeg", ".png"} +) + +type AppData struct { + Username string + Photos []string + errors []error + messages []string +} + +type TimedFile struct { + Mod time.Time + Path string +} + +type timeMap []TimedFile + +func (m timeMap) Len() int { + return len(m) +} +func (m timeMap) Less(i, j int) bool { + return m[i].Mod.After(m[j].Mod) +} +func (m timeMap) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} + +type PhotoBlog struct { + *admin.AuthCookie + Templates *template.Template + DataDir *os.File +} + +func RedirectHome(res http.ResponseWriter, req *http.Request) { + // FIXME duplicate admin + http.Redirect(res, req, "/", http.StatusSeeOther) +} +func IsPhoto(path string) bool { + for _, ext := range PHOTOEXT { + if strings.HasSuffix(path, ext) { + return true + } + } + return false +} + +func Latest(photos timeMap) []string { + latest := make([]string, 0, DISPLAY_SIZE) + sort.Sort(photos) + for i, v := range photos { + if i > DISPLAY_SIZE { + break + } + latest = append(latest, v.Path) + } + return latest +} + +func (app *PhotoBlog) HomePage(res http.ResponseWriter, req *http.Request) { + data := AppData{ + Username: app.Username(res, req), + } + photos := make([]TimedFile, 0) + filepath.Walk(app.DataDir.Name(), func(path string, info os.FileInfo, err error) error { + if !info.IsDir() && IsPhoto(path) { + photos = append(photos, TimedFile{ + info.ModTime(), + path, + }) + } + return nil + }) + data.Photos = Latest(photos) + app.Templates.ExecuteTemplate(res, "home.html", data) +} +func (app *PhotoBlog) UploadPage(res http.ResponseWriter, req *http.Request) { + if !app.IsLoggedIn(res, req) { + RedirectHome(res, req) + return + } + data := make(map[string]string) + switch req.Method { + case "POST": + message, err := app.NewPhoto(res, req) + if err != nil { + data["error"] = err.Error() + } + data["message"] = message + fallthrough + case "GET": + app.Templates.ExecuteTemplate(res, "upload.html", data) + } +} +func (app *PhotoBlog) NewPhoto(res http.ResponseWriter, req *http.Request) (string, error) { + file, header, err := req.FormFile("file") + if err != nil { + return "", err + } + defer file.Close() + + // FIXME check type + tmpFile, err := os.Create(filepath.Join(app.DataDir.Name(), app.Username(res, req), header.Filename)) + if err != nil { + return "", err + } + defer tmpFile.Close() + + sz, err := io.Copy(tmpFile, file) + if err != nil { + return "", err + } + return fmt.Sprintf("File uploaded (%d bytes)", sz), nil +} diff --git a/photoblog/templates/header.html b/photoblog/templates/header.html index 0fd7698..ec83884 100644 --- a/photoblog/templates/header.html +++ b/photoblog/templates/header.html @@ -1,4 +1,4 @@ - Auth cookie + Photo blog diff --git a/photoblog/templates/home.html b/photoblog/templates/home.html index 737ae4f..cce94c8 100644 --- a/photoblog/templates/home.html +++ b/photoblog/templates/home.html @@ -1,10 +1,24 @@ {{ template "header.html" }} -
-{{ if .username }} +
+{{ if .Username }} +

Hello {{ .Username }}

Logout {{ else }} +

You are not logged in

Login {{ end }} +

+
-

Hello {{ .username }}

+

Photo blog

+{{ if .Username }} +Add photo +{{ end }} + {{ template "footer.html" }} diff --git a/photoblog/templates/upload.html b/photoblog/templates/upload.html new file mode 100644 index 0000000..e658a6a --- /dev/null +++ b/photoblog/templates/upload.html @@ -0,0 +1,14 @@ +{{ template "header.html" }} +

Send photo

+
+ {{ if .message }}

{{ .message }}

{{ end }} + {{ if .error }}

{{ .error }}

{{ end }} + +
+ +
+ Back +{{ template "footer.html" }}