youp0m/api.go

98 lines
2.4 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strings"
)
type DispatchFunction func(*User, []string, io.ReadCloser) (interface{}, error)
type apiHandler struct {
PE *PictureExplorer
Authenticate func(*http.Request) *User
routes map[string](map[string]struct {
AuthFunction
DispatchFunction
})
}
func NewAPIHandler(pe *PictureExplorer, authenticate func(*http.Request) *User) *apiHandler {
return &apiHandler{
pe,
authenticate,
map[string](map[string]struct {
AuthFunction
DispatchFunction
}){
"images": ApiImagesRouting(pe),
"next": ApiNextImagesRouting(pe),
"version": ApiVersionRouting,
},
}
}
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user := a.Authenticate(r)
log.Printf("Handling %s API request from %s: %s %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, user, r.UserAgent())
// Extract URL arguments
var sURL = strings.Split(r.URL.Path[1:], "/")
if sURL[len(sURL)-1] == "" && len(sURL) > 0 {
// Remove trailing /
sURL = sURL[:len(sURL)-1]
}
w.Header().Set("Content-Type", "application/json")
var ret interface{}
var err error = nil
// Refuse too large requests
if r.ContentLength < 0 || r.ContentLength > 10485760 || ((r.Method == "DELETE" || r.Method == "GET" || r.Method == "HEAD") && r.ContentLength > 0) {
http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge)
return
}
// Route request
if len(sURL) == 0 {
err = errors.New(fmt.Sprintf("No action provided"))
} else if h, ok := a.routes[sURL[0]]; ok {
if f, ok := h[r.Method]; ok {
if f.AuthFunction(user, sURL[1:]) {
ret, err = f.DispatchFunction(user, sURL[1:], r.Body)
} else {
w.Header().Set("WWW-Authenticate", "Basic realm=\"YouP0m\"")
http.Error(w, "{errmsg:\"You are not allowed to do this request.\"}", http.StatusUnauthorized)
return
}
} else {
err = errors.New(fmt.Sprintf("Invalid action (%s) provided for %s.", r.Method, sURL[0]))
}
}
// Format response
resStatus := http.StatusOK
if err != nil {
ret = map[string]string{"errmsg": err.Error()}
resStatus = http.StatusBadRequest
}
if ret == nil {
ret = map[string]string{"errmsg": "Page not found"}
resStatus = http.StatusNotFound
}
if j, err := json.Marshal(ret); err != nil {
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusInternalServerError)
} else {
w.WriteHeader(resStatus)
w.Write(j)
}
}