Photo blog 2: unstyled, no auth

This commit is contained in:
Meutel 2017-07-29 13:54:23 +02:00
parent f20e3d6aed
commit ee2bfd95cb
7 changed files with 207 additions and 15 deletions

1
.gitignore vendored
View File

@ -64,3 +64,4 @@ authcookie/authcookie
cert.pem cert.pem
key.pem key.pem
photoblog/photoblog photoblog/photoblog
photoblog/data

View File

@ -24,6 +24,7 @@ func (app *AuthCookie) LoginPage(res http.ResponseWriter, req *http.Request) {
if password == "" { if password == "" {
formErr["password"] = "Empty password" formErr["password"] = "Empty password"
} }
// FIXME verifiy password with file in data dir
if len(formErr) == 0 { if len(formErr) == 0 {
app.SaveUsername(username, res, req) app.SaveUsername(username, res, req)
RedirectHome(res, req) RedirectHome(res, req)
@ -47,6 +48,17 @@ func (app *AuthCookie) SaveUsername(username string, res http.ResponseWriter, re
session.Values["username"] = username session.Values["username"] = username
session.Save(req, res) 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) { func RedirectHome(res http.ResponseWriter, req *http.Request) {
http.Redirect(res, req, "/", http.StatusSeeOther) http.Redirect(res, req, "/", http.StatusSeeOther)
} }

View File

@ -1,38 +1,55 @@
package main package main
import ( import (
"fmt"
"html/template" "html/template"
"log" "log"
"net/http" "net/http"
"os"
"strings"
"meutel.net/meutel/go-examples/photoblog/admin" "meutel.net/meutel/go-examples/photoblog/admin"
"meutel.net/meutel/go-examples/photoblog/photo"
"github.com/gorilla/sessions" "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() { func main() {
tpl, err := template.ParseGlob("templates/*.html") tpl, err := template.ParseGlob("templates/*.html")
if err != nil { if err != nil {
log.Fatalln(err) 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{ admin := admin.AuthCookie{
Templates: tpl, Templates: tpl,
Store: sessions.NewCookieStore([]byte("flQ6QzM/c3Jtdl9ycDx6OXRIfFgK")), Store: sessions.NewCookieStore([]byte("flQ6QzM/c3Jtdl9ycDx6OXRIfFgK")),
} }
app := PhotoBlog{ app := photo.PhotoBlog{
AuthCookie: &admin, AuthCookie: &admin,
Templates: tpl, 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("/login", app.LoginPage)
http.HandleFunc("/logout", app.LogoutPage) http.HandleFunc("/logout", app.LogoutPage)
http.ListenAndServeTLS(":9443", "../cert.pem", "../key.pem", nil) http.ListenAndServeTLS(":9443", "../cert.pem", "../key.pem", nil)

134
photoblog/photo/photo.go Normal file
View File

@ -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
}

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head><title>Auth cookie</title></head> <head><title>Photo blog</title></head>
<body> <body>

View File

@ -1,10 +1,24 @@
{{ template "header.html" }} {{ template "header.html" }}
<div> <header>
{{ if .username }} {{ if .Username }}
<p>Hello {{ .Username }}</p>
<a href="/logout">Logout</a> <a href="/logout">Logout</a>
{{ else }} {{ else }}
<p>You are not logged in<p>
<a href="/login">Login</a> <a href="/login">Login</a>
{{ end }} {{ end }}
</header>
<div>
</div> </div>
<h1>Hello {{ .username }}</h1> <h1>Photo blog</h1>
{{ if .Username }}
<a href="upload">Add photo</a>
{{ end }}
<ul>
{{ range .Photos }}
<li><img src="{{ . }}" style="width:320px;height:200px;"></li>
{{ else }}
<li>No photos</li>
{{ end }}
</ul>
{{ template "footer.html" }} {{ template "footer.html" }}

View File

@ -0,0 +1,14 @@
{{ template "header.html" }}
<h1>Send photo</h1>
<form action="/upload" method="POST" enctype="multipart/form-data">
{{ if .message }}<p class="message">{{ .message }}</p>{{ end }}
{{ if .error }}<p class="errormsg">{{ .error }}</p>{{ end }}
<label>
Select photo: {{ if .file }}<b>{{ .file }}</b>{{ end }}
<input type="file" name="file">
</label>
<br>
<input type="submit">
</form>
<a href="/">Back</a>
{{ template "footer.html" }}