mirror of
https://github.com/gmemstr/pogo.git
synced 2024-09-19 17:21:10 +01:00
Refactoring Code
This commit is contained in:
parent
5fbb1e3b4c
commit
fc7d3dd013
30
auth/auth.go
Normal file
30
auth/auth.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ishanjain28/pogo/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RequireAuthorization() common.Handler {
|
||||||
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
||||||
|
if usr := DecryptSession(r); usr != nil {
|
||||||
|
rc.User = usr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &common.HTTPError{
|
||||||
|
Message: "Unauthorized!",
|
||||||
|
StatusCode: http.StatusUnauthorized,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSession() common.Handler {
|
||||||
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecryptSession(r *http.Request) *common.User {
|
||||||
|
return nil
|
||||||
|
}
|
66
common/common.go
Normal file
66
common/common.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is the signature of HTTP Handler that is passed to Handle function
|
||||||
|
type Handler func(rc *RouterContext, w http.ResponseWriter, r *http.Request) *HTTPError
|
||||||
|
|
||||||
|
// HTTPError is any error that occurs in middlewares or the code that handles HTTP Frontend of application
|
||||||
|
// Message is logged to console and Status Code is sent in response
|
||||||
|
// If a Middleware sends an HTTPError, No middlewares further up in chain are executed
|
||||||
|
type HTTPError struct {
|
||||||
|
// Message to log in console
|
||||||
|
Message string
|
||||||
|
// Status code that'll be sent in response
|
||||||
|
StatusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterContext contains any information to be shared with middlewares.
|
||||||
|
type RouterContext struct {
|
||||||
|
User *User
|
||||||
|
}
|
||||||
|
|
||||||
|
// User struct denotes the data is stored in the cookie
|
||||||
|
type User struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAndServeFile reads the file from specified location and sends it in response
|
||||||
|
func ReadAndServeFile(name string, w http.ResponseWriter) *HTTPError {
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return &HTTPError{
|
||||||
|
Message: fmt.Sprintf("%s not found", name),
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTTPError{
|
||||||
|
Message: fmt.Sprintf("error in reading %s: %v\n", name, err),
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
stats, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error in fetching %s's stats: %v\n", name, err)
|
||||||
|
} else {
|
||||||
|
w.Header().Add("Content-Length", strconv.FormatInt(stats.Size(), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(w, f)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error in copying %s to response: %v\n", name, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
148
router/router.go
Normal file
148
router/router.go
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"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(
|
||||||
|
auth.RequireAuthorization(),
|
||||||
|
adminHandler(),
|
||||||
|
))
|
||||||
|
// r.HandleFunc("/admin/publish", BasicAuth(CreateEpisode))
|
||||||
|
// r.HandleFunc("/admin/delete", BasicAuth(RemoveEpisode))
|
||||||
|
// r.HandleFunc("/admin/css", BasicAuth(CustomCss))
|
||||||
|
|
||||||
|
r.Handle("/setup", Handle(
|
||||||
|
serveSetup(),
|
||||||
|
)).Methods("GET", "POST")
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func redirectHandler() common.Handler {
|
||||||
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
50
setup.go
50
setup.go
|
@ -1,50 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NewConfig struct {
|
|
||||||
Name string
|
|
||||||
Host string
|
|
||||||
Email string
|
|
||||||
Description string
|
|
||||||
Image string
|
|
||||||
PodcastUrl string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve setup.html and config parameters
|
|
||||||
func ServeSetup(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method == "GET" {
|
|
||||||
data, err := ioutil.ReadFile("assets/web/setup.html")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
w.Write(data)
|
|
||||||
}
|
|
||||||
if r.Method == "POST" {
|
|
||||||
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
|
|
||||||
strings.Join(r.Form["podcastdescription"], ""), // Podcast Description
|
|
||||||
"", // Podcast image
|
|
||||||
"", // Podcast location
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := json.Marshal(cnf)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ioutil.WriteFile("assets/config/config.json", b, 0644)
|
|
||||||
w.Write([]byte("Done"))
|
|
||||||
}
|
|
||||||
}
|
|
115
webserver.go
115
webserver.go
|
@ -8,49 +8,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/ishanjain28/pogo/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Prints out contents of feed.rss
|
|
||||||
func RssHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/rss+xml")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
data, err := ioutil.ReadFile("assets/web/feed.rss")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Length", fmt.Sprint(len(data)))
|
|
||||||
fmt.Fprint(w, string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does the same as above method, only with the JSON feed data
|
|
||||||
func JsonHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
data, err := ioutil.ReadFile("assets/web/feed.json")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Length", fmt.Sprint(len(data)))
|
|
||||||
fmt.Fprint(w, string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve up homepage
|
|
||||||
func HomeHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
data, err := ioutil.ReadFile("assets/web/index.html")
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
w.Write(data)
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte("Error500 - " + http.StatusText(500)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticate user using basic webserver authentication
|
// Authenticate user using basic webserver authentication
|
||||||
// @TODO: Replace this with a different for of _more secure_
|
// @TODO: Replace this with a different for of _more secure_
|
||||||
// authentication that we can POST to instead.
|
// authentication that we can POST to instead.
|
||||||
|
@ -58,63 +21,43 @@ func HomeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
* Code from stackoverflow by user Timmmm
|
* Code from stackoverflow by user Timmmm
|
||||||
* https://stackoverflow.com/questions/21936332/idiomatic-way-of-requiring-http-basic-auth-in-go/39591234#39591234
|
* https://stackoverflow.com/questions/21936332/idiomatic-way-of-requiring-http-basic-auth-in-go/39591234#39591234
|
||||||
*/
|
*/
|
||||||
func BasicAuth(handler http.HandlerFunc) http.HandlerFunc {
|
// func BasicAuth(handler http.HandlerFunc) http.HandlerFunc {
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
// return func(w http.ResponseWriter, r *http.Request) {
|
||||||
realm := "Login to Pogo admin interface"
|
// realm := "Login to Pogo admin interface"
|
||||||
user, pass, ok := r.BasicAuth()
|
// user, pass, ok := r.BasicAuth()
|
||||||
|
|
||||||
if !AuthUser(user,pass) || !ok {
|
// if !AuthUser(user, pass) || !ok {
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
|
// w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
|
||||||
w.WriteHeader(401)
|
// w.WriteHeader(401)
|
||||||
w.Write([]byte("Unauthorised.\n"))
|
// w.Write([]byte("Unauthorised.\n"))
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
handler(w, r)
|
// handler(w, r)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Handler for serving up admin page
|
// // Handler for serving up admin page
|
||||||
func AdminHandler(w http.ResponseWriter, r *http.Request) {
|
// func AdminHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data, err := ioutil.ReadFile("assets/web/admin.html")
|
// data, err := ioutil.ReadFile("assets/web/admin.html")
|
||||||
|
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
w.Write(data)
|
// w.Write(data)
|
||||||
} else {
|
// } else {
|
||||||
w.WriteHeader(500)
|
// w.WriteHeader(500)
|
||||||
w.Write([]byte("500 Something went wrong - " + http.StatusText(500)))
|
// w.Write([]byte("500 Something went wrong - " + http.StatusText(500)))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Main function that defines routes
|
// Main function that defines routes
|
||||||
func main() {
|
func main() {
|
||||||
// Start the watch() function in generate_rss.go, which
|
// Start the watch() function in generate_rss.go, which
|
||||||
// watches for file changes and regenerates the feed
|
// watches for file changes and regenerates the feed
|
||||||
go watch()
|
// go watch()
|
||||||
|
|
||||||
// Define routes
|
// Define routes
|
||||||
r := mux.NewRouter()
|
// We're live
|
||||||
|
r := router.Init()
|
||||||
// "Static" paths
|
fmt.Println("Listening on port :3000")
|
||||||
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/web/static"))))
|
log.Fatal(http.ListenAndServe(":3000", r))
|
||||||
r.PathPrefix("/download/").Handler(http.StripPrefix("/download/", http.FileServer(http.Dir("podcasts"))))
|
|
||||||
|
|
||||||
// Paths that require specific handlers
|
|
||||||
http.Handle("/", r) // Pass everything to gorilla mux
|
|
||||||
r.HandleFunc("/", HomeHandler)
|
|
||||||
r.HandleFunc("/rss", RssHandler)
|
|
||||||
r.HandleFunc("/json", JsonHandler)
|
|
||||||
|
|
||||||
// Authenticated endpoints should be passed to BasicAuth()
|
|
||||||
// first
|
|
||||||
r.HandleFunc("/admin", BasicAuth(AdminHandler))
|
|
||||||
r.HandleFunc("/admin/publish", BasicAuth(CreateEpisode))
|
|
||||||
r.HandleFunc("/admin/delete", BasicAuth(RemoveEpisode))
|
|
||||||
r.HandleFunc("/admin/css", BasicAuth(CustomCss))
|
|
||||||
|
|
||||||
r.HandleFunc("/setup", ServeSetup)
|
|
||||||
|
|
||||||
// We're live!
|
|
||||||
fmt.Println("Listening on port :8000")
|
|
||||||
log.Fatal(http.ListenAndServe(":8000", r))
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue