Photo blog 2: unstyled, no auth
This commit is contained in:
parent
f20e3d6aed
commit
ee2bfd95cb
1
.gitignore
vendored
1
.gitignore
vendored
@ -64,3 +64,4 @@ authcookie/authcookie
|
||||
cert.pem
|
||||
key.pem
|
||||
photoblog/photoblog
|
||||
photoblog/data
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
134
photoblog/photo/photo.go
Normal file
134
photoblog/photo/photo.go
Normal 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
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Auth cookie</title></head>
|
||||
<head><title>Photo blog</title></head>
|
||||
<body>
|
||||
|
@ -1,10 +1,24 @@
|
||||
{{ template "header.html" }}
|
||||
<div>
|
||||
{{ if .username }}
|
||||
<header>
|
||||
{{ if .Username }}
|
||||
<p>Hello {{ .Username }}</p>
|
||||
<a href="/logout">Logout</a>
|
||||
{{ else }}
|
||||
<p>You are not logged in<p>
|
||||
<a href="/login">Login</a>
|
||||
{{ end }}
|
||||
</header>
|
||||
<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" }}
|
||||
|
14
photoblog/templates/upload.html
Normal file
14
photoblog/templates/upload.html
Normal 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" }}
|
Loading…
Reference in New Issue
Block a user