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
|
cert.pem
|
||||||
key.pem
|
key.pem
|
||||||
photoblog/photoblog
|
photoblog/photoblog
|
||||||
|
photoblog/data
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
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>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head><title>Auth cookie</title></head>
|
<head><title>Photo blog</title></head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -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" }}
|
||||||
|
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