Commit 65460f3a authored by Janne Koschinski's avatar Janne Koschinski

Attempt at a more modular codebase

parent 958c8d7b
This diff is collapsed.
package main
import (
"net/http"
"fmt"
"time"
"path"
)
type AlbumDetailData struct {
User UserInfo
Album Album
IsMine bool
}
func pageAlbumDetail(ctx PageContext) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := parseUser(r)
_, albumId := path.Split(r.URL.Path)
result, err := ctx.Database.Query(`
SELECT
id,
owner,
coalesce(title, ''),
coalesce(description, ''),
coalesce(created_at, to_timestamp(0))
FROM albums
WHERE id = $1
`, albumId)
if err != nil {
panic(err)
}
var info Album
if result.Next() {
var owner string
err := result.Scan(&info.Id, &owner, &info.Title, &info.Description, &info.CreatedAt)
if err != nil {
panic(err)
}
result, err := ctx.Database.Query(`
SELECT
image,
title,
description,
position
FROM album_images
WHERE album = $1
ORDER BY position ASC
`, albumId)
if err != nil {
panic(err)
}
for result.Next() {
var image AlbumImage
err := result.Scan(&image.Id, &owner, &image.Title, &image.Description, &image.Position)
if err != nil {
panic(err)
}
info.Images = append(info.Images, image)
}
if err = formatTemplate(w, "album_detail.html", AlbumDetailData{
user,
info,
owner == user.Id,
}); err != nil {
panic(err)
}
return
}
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Album not found")
})
}
package main
import (
"net/http"
)
func pageAlbumList(ctx PageContext) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})
}
package main
import (
"fmt"
_ "github.com/lib/pq"
"net/http"
"os"
"path"
)
type ImageDetailData struct {
User UserInfo
Image Image
IsMine bool
}
func pageImageDetail(ctx PageContext) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := parseUser(r)
_, imageId := path.Split(r.URL.Path)
result, err := ctx.Database.Query(`
SELECT
id,
owner,
coalesce(title, ''),
coalesce(description, ''),
coalesce(created_at, to_timestamp(0)),
coalesce(original_name, ''),
coalesce(type, '')
FROM images
WHERE id = $1
`, imageId)
if err != nil {
panic(err)
}
var info Image
if result.Next() {
var owner string
err := result.Scan(&info.Id, &owner, &info.Title, &info.Description, &info.CreatedAt, &info.OriginalName, &info.MimeType)
if err != nil {
panic(err)
}
switch r.PostFormValue("action") {
case "update":
_, err = ctx.Database.Exec(
"UPDATE images SET title = $1, description = $2 WHERE id = $3 AND owner = $4",
r.PostFormValue("title"),
r.PostFormValue("description"),
info.Id,
user.Id,
)
if err != nil {
panic(err)
}
return
case "delete":
_, err = ctx.Database.Exec("DELETE FROM images WHERE id = $1 AND owner = $2", info.Id, user.Id)
if err != nil {
panic(err)
}
for _, definition := range ctx.Config.Sizes {
os.Remove(path.Join(ctx.Config.TargetFolder, fmt.Sprintf("%s%s", info.Id, definition.Suffix)))
}
return
default:
if err = formatTemplate(w, "image_detail.html", ImageDetailData{
user,
info,
owner == user.Id,
}); err != nil {
panic(err)
}
return
}
}
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Image not found")
})
}
package main
import (
"net/http"
)
type ImageListData struct {
User UserInfo
Images []Image
}
func pageImageList(ctx PageContext) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := parseUser(r)
result, err := ctx.Database.Query(`
SELECT
id,
coalesce(title, ''),
coalesce(description, ''),
coalesce(created_at, to_timestamp(0)),
coalesce(original_name, ''),
coalesce(type, '')
FROM images
WHERE owner = $1
`, user.Id)
if err != nil {
panic(err)
}
var images []Image
for result.Next() {
var info Image
err := result.Scan(&info.Id, &info.Title, &info.Description, &info.CreatedAt, &info.OriginalName, &info.MimeType)
if err != nil {
panic(err)
}
images = append(images, info)
}
if err = formatTemplate(w, "image_list.html", ImageListData{
user,
images,
}); err != nil {
panic(err)
}
})
}
package main
import (
"net/http"
)
type IndexData struct {
User UserInfo
}
func pageIndex(ctx PageContext) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
user := parseUser(r)
if err := formatTemplate(w, "index.html", IndexData{
user,
}); err != nil {
panic(err)
}
} else {
ctx.Images.ServeHTTP(w, r)
}
})
}
package main
import (
"net/http"
"fmt"
"time"
"encoding/json"
"io"
"mime/multipart"
"path/filepath"
"os"
"encoding/base64"
"crypto/rand"
)
type UploadData struct {
User UserInfo
Results []Result
}
func detectMimeType(path string) (string, error) {
file, err := os.Open(path)
if err != nil {
return "", err
}
buffer := make([]byte, 512)
_, err = file.Read(buffer)
if err != nil {
return "", err
}
return http.DetectContentType(buffer), nil
}
func generateId() string {
buffer := make([]byte, 4)
rand.Read(buffer)
return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(buffer)
}
func writeBody(reader io.ReadCloser, path string) error {
out, err := os.Create(path)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, reader)
if err != nil {
return err
}
return out.Close()
}
func createImage(config *Config, body io.ReadCloser, fileHeader *multipart.FileHeader) (Image, error) {
id := generateId()
path := filepath.Join(config.SourceFolder, id)
err := writeBody(body, path)
if err != nil {
return Image{}, err
}
mimeType, err := detectMimeType(path)
if err != nil {
return Image{}, err
}
image := Image{
Id: id,
OriginalName: filepath.Base(fileHeader.Filename),
CreatedAt: time.Now(),
MimeType: mimeType,
}
return image, nil
}
func pageUpload(ctx PageContext) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
user := parseUser(r)
err := r.ParseMultipartForm(32 << 20)
if err != nil {
if err = formatTemplate(w, "upload.html", UploadData{
user,
[]Result{{
Success: false,
Errors: []string{err.Error()},
}},
}); err != nil {
panic(err)
}
}
var images []Image
var ids []string
m := r.MultipartForm
files := m.File["file"]
for _, header := range files {
file, err := header.Open()
if err != nil {
if err = formatTemplate(w, "upload.html", UploadData{
user,
[]Result{{
Success: false,
Errors: []string{err.Error()},
}},
}); err != nil {
panic(err)
}
return
}
image, err := createImage(ctx.Config, file, header)
if err != nil {
if err = formatTemplate(w, "upload.html", UploadData{
user,
[]Result{{
Success: false,
Errors: []string{err.Error()},
}},
}); err != nil {
panic(err)
}
return
}
images = append(images, image)
ids = append(ids, image.Id)
}
pubsub := ctx.Redis.Subscribe(ctx.Config.ResultChannel)
waiting := make(map[string]bool)
for _, image := range images {
_, err = ctx.Database.Exec("INSERT INTO images (id, owner, created_at, original_name, type) VALUES ($1, $2, $3, $4, $5)", image.Id, user.Id, image.CreatedAt, image.OriginalName, image.MimeType)
if err != nil {
panic(err)
}
data, err := json.Marshal(image)
if err != nil {
if err = formatTemplate(w, "upload.html", UploadData{
user,
[]Result{{
Success: false,
Errors: []string{err.Error()},
}},
}); err != nil {
panic(err)
}
return
}
fmt.Printf("Created task %s at %d\n", image.Id, time.Now().Unix())
ctx.Redis.RPush(fmt.Sprintf("queue:%s", ctx.Config.ImageQueue), data)
fmt.Printf("Submitted task %s at %d\n", image.Id, time.Now().Unix())
waiting[image.Id] = true
}
var results []Result
for len(waiting) != 0 {
message, err := pubsub.ReceiveMessage()
if err != nil {
if err = formatTemplate(w, "upload.html", UploadData{
user,
[]Result{{
Success: false,
Errors: []string{err.Error()},
}},
}); err != nil {
panic(err)
}
return
}
result := Result{}
err = json.Unmarshal([]byte(message.Payload), &result)
if err != nil {
if err = formatTemplate(w, "upload.html", UploadData{
user,
[]Result{{
Success: false,
Errors: []string{err.Error()},
}},
}); err != nil {
panic(err)
}
return
}
fmt.Printf("Returned task %s at %d\n", result.Id, time.Now().Unix())
if _, ok := waiting[result.Id]; ok {
delete(waiting, result.Id)
results = append(results, result)
}
}
if err = formatTemplate(w, "upload.html", UploadData{
user,
results,
}); err != nil {
panic(err)
}
return
} else {
user := parseUser(r)
if err := formatTemplate(w, "upload.html", UploadData{
user,
[]Result{},
}); err != nil {
panic(err)
}
}
})
}
package main
import (
"fmt"
"net/http"
"html/template"
"time"
"database/sql"
"github.com/go-redis/redis"
"encoding/base64"
"io"
"mime/multipart"
"path/filepath"
"os"
"crypto/rand"
)
type UserInfo struct {
Id string
Name string
Email string
}
type PageContext struct {
Config *Config
Redis *redis.Client
Database *sql.DB
Images http.Handler
AssetServer http.Handler
}
type AlbumImage struct {
Id string
Title string
Description string
Position int
}
type Album struct {
Id string
Title string
Description string
CreatedAt time.Time
Images []AlbumImage
}
func parseUser(r *http.Request) UserInfo {
return UserInfo{
r.Header.Get("X-Auth-Subject"),
r.Header.Get("X-Auth-Username"),
r.Header.Get("X-Auth-Email"),
}
}
func formatTemplate(w http.ResponseWriter, templateName string, data interface{}) error {
pageTemplate, err := template.ParseFiles(
"templates/_base.html",
"templates/_header.html",
"templates/_navigation.html",
"templates/_footer.html",
fmt.Sprintf("templates/%s", templateName),
)
if err != nil {
return err
}
err = pageTemplate.Execute(w, data)
if err != nil {
return err
}
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment