2017-10-03 12:08:27 +01:00
|
|
|
package router
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
2017-10-05 06:48:02 +01:00
|
|
|
"github.com/ishanjain28/pogo/admin"
|
2017-10-03 12:08:27 +01:00
|
|
|
"github.com/ishanjain28/pogo/auth"
|
|
|
|
"github.com/ishanjain28/pogo/common"
|
|
|
|
)
|
|
|
|
|
|
|
|
type NewConfig struct {
|
|
|
|
Name string
|
|
|
|
Host string
|
|
|
|
Email string
|
|
|
|
Description string
|
|
|
|
Image string
|
|
|
|
PodcastURL string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle takes multiple Handler and executes them in a serial order starting from first to last.
|
|
|
|
// In case, Any middle ware returns an error, The error is logged to console and sent to the user, Middlewares further up in chain are not executed.
|
|
|
|
func Handle(handlers ...common.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
rc := &common.RouterContext{}
|
|
|
|
for _, handler := range handlers {
|
|
|
|
err := handler(rc, w, r)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("%v", err)
|
|
|
|
|
|
|
|
w.Write([]byte(http.StatusText(err.StatusCode)))
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func Init() *mux.Router {
|
|
|
|
|
|
|
|
r := mux.NewRouter()
|
|
|
|
|
|
|
|
// "Static" paths
|
|
|
|
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/web/static"))))
|
|
|
|
r.PathPrefix("/download/").Handler(http.StripPrefix("/download/", http.FileServer(http.Dir("podcasts"))))
|
|
|
|
|
|
|
|
// Paths that require specific handlers
|
|
|
|
r.Handle("/", Handle(
|
|
|
|
rootHandler(),
|
|
|
|
)).Methods("GET")
|
|
|
|
|
|
|
|
r.Handle("/rss", Handle(
|
|
|
|
rootHandler(),
|
|
|
|
)).Methods("GET")
|
|
|
|
|
|
|
|
r.Handle("/json", Handle(
|
|
|
|
rootHandler(),
|
|
|
|
)).Methods("GET")
|
|
|
|
|
|
|
|
// Authenticated endpoints should be passed to BasicAuth()
|
|
|
|
// first
|
|
|
|
r.Handle("/admin", Handle(
|
2017-10-05 06:33:57 +01:00
|
|
|
auth.RequireAuthorization(),
|
2017-10-03 12:08:27 +01:00
|
|
|
adminHandler(),
|
2017-10-05 06:33:57 +01:00
|
|
|
)).Methods("GET", "POST")
|
2017-10-04 19:28:53 +01:00
|
|
|
|
|
|
|
r.Handle("/login", Handle(
|
|
|
|
loginHandler(),
|
2017-10-05 06:33:57 +01:00
|
|
|
)).Methods("GET", "POST")
|
2017-10-04 19:28:53 +01:00
|
|
|
|
2017-10-05 06:48:02 +01:00
|
|
|
r.Handle("/admin/publish", Handle(
|
|
|
|
auth.RequireAuthorization(),
|
|
|
|
admin.CreateEpisode(),
|
|
|
|
)).Methods("POST")
|
|
|
|
|
|
|
|
r.Handle("/admin/delete", Handle(
|
|
|
|
auth.RequireAuthorization(),
|
|
|
|
admin.RemoveEpisode(),
|
|
|
|
)).Methods("GET")
|
|
|
|
|
|
|
|
r.Handle("/admin/css", Handle(
|
|
|
|
auth.RequireAuthorization(),
|
|
|
|
admin.CustomCss(),
|
|
|
|
)).Methods("GET", "POST")
|
2017-10-03 12:08:27 +01:00
|
|
|
|
|
|
|
r.Handle("/setup", Handle(
|
|
|
|
serveSetup(),
|
|
|
|
)).Methods("GET", "POST")
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2017-10-04 19:28:53 +01:00
|
|
|
func loginHandler() common.Handler {
|
|
|
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
|
|
|
|
2017-10-05 06:33:57 +01:00
|
|
|
if _, err := r.Cookie("POGO_SESSION"); err == nil {
|
|
|
|
http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-04 19:28:53 +01:00
|
|
|
if r.Method == "GET" {
|
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
|
|
return common.ReadAndServeFile("assets/web/login.html", w)
|
|
|
|
}
|
|
|
|
|
|
|
|
d, err := ioutil.ReadFile("assets/config/users.json")
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return &common.HTTPError{
|
|
|
|
Message: fmt.Sprintf("error in reading users.json: %v", err),
|
|
|
|
StatusCode: http.StatusInternalServerError,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = r.ParseForm()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return &common.HTTPError{
|
|
|
|
Message: fmt.Sprintf("error in parsing form: %v", err),
|
|
|
|
StatusCode: http.StatusBadRequest,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
username := r.Form.Get("username")
|
|
|
|
password := r.Form.Get("password")
|
|
|
|
if username == "" || password == "" {
|
|
|
|
return &common.HTTPError{
|
|
|
|
Message: "username or password is empty",
|
|
|
|
StatusCode: http.StatusBadRequest,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var u map[string]string
|
|
|
|
err = json.Unmarshal(d, &u) // Unmarshal into interface
|
|
|
|
|
|
|
|
// Iterate through map until we find matching username
|
|
|
|
for k, v := range u {
|
|
|
|
if k == username && v == password {
|
|
|
|
// Create a cookie here because the credentials are correct
|
2017-10-05 06:33:57 +01:00
|
|
|
c, err := auth.CreateSession(&common.User{
|
2017-10-04 19:28:53 +01:00
|
|
|
Username: k,
|
2017-10-05 06:33:57 +01:00
|
|
|
})
|
2017-10-04 19:28:53 +01:00
|
|
|
if err != nil {
|
|
|
|
return &common.HTTPError{
|
|
|
|
Message: err.Error(),
|
|
|
|
StatusCode: http.StatusInternalServerError,
|
|
|
|
}
|
|
|
|
}
|
2017-10-05 06:33:57 +01:00
|
|
|
|
|
|
|
// r.AddCookie(c)
|
|
|
|
w.Header().Add("Set-Cookie", c.String())
|
2017-10-04 19:28:53 +01:00
|
|
|
// And now redirect the user to admin page
|
|
|
|
http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &common.HTTPError{
|
|
|
|
Message: "Invalid credentials!",
|
|
|
|
StatusCode: http.StatusUnauthorized,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-03 12:08:27 +01:00
|
|
|
// Handles /, /feed and /json endpoints
|
|
|
|
func rootHandler() common.Handler {
|
|
|
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
|
|
|
|
|
|
|
var file string
|
|
|
|
switch r.URL.Path {
|
|
|
|
case "/rss":
|
|
|
|
w.Header().Set("Content-Type", "application/rss+xml")
|
|
|
|
file = "assets/web/feed.rss"
|
|
|
|
case "/json":
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
file = "assets/web/feed.json"
|
|
|
|
case "/":
|
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
|
|
file = "assets/web/index.html"
|
|
|
|
default:
|
|
|
|
return &common.HTTPError{
|
|
|
|
Message: fmt.Sprintf("%s: Not Found", r.URL.Path),
|
|
|
|
StatusCode: http.StatusNotFound,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return common.ReadAndServeFile(file, w)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func adminHandler() common.Handler {
|
|
|
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
|
|
|
return common.ReadAndServeFile("assets/web/admin.html", w)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serve setup.html and config parameters
|
|
|
|
func serveSetup() common.Handler {
|
|
|
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
|
|
|
if r.Method == "GET" {
|
|
|
|
return common.ReadAndServeFile("assets/web/setup.html", w)
|
|
|
|
}
|
|
|
|
r.ParseMultipartForm(32 << 20)
|
|
|
|
|
|
|
|
// Parse form and convert to JSON
|
|
|
|
cnf := NewConfig{
|
|
|
|
strings.Join(r.Form["podcastname"], ""), // Podcast name
|
|
|
|
strings.Join(r.Form["podcasthost"], ""), // Podcast host
|
|
|
|
strings.Join(r.Form["podcastemail"], ""), // Podcast host email
|
|
|
|
"", // Podcast image
|
|
|
|
"", // Podcast location
|
|
|
|
"", // Podcast location
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := json.Marshal(cnf)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ioutil.WriteFile("assets/config/config.json", b, 0644)
|
|
|
|
w.Write([]byte("Done"))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|