pogo/router/router.go
gmemstr ba9c7c7a5d Reimplemented AddUser() function to use SQLite3
Aha! Screw you JSON headaches! I have banished thee to the realm of impossibility, and turn to the promised land of SQLite3, where the sky is blue and the grass long.

...or something. Rewrite AddUser() to insert into database, frontend soon.
2017-10-25 21:34:15 -07:00

257 lines
6.2 KiB
Go

package router
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"golang.org/x/crypto/bcrypt"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"github.com/gorilla/mux"
"github.com/gmemstr/pogo/admin"
"github.com/gmemstr/pogo/auth"
"github.com/gmemstr/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(),
)).Methods("GET", "POST")
r.Handle("/login", Handle(
loginHandler(),
)).Methods("GET", "POST")
r.Handle("/admin/publish", Handle(
auth.RequireAuthorization(),
admin.CreateEpisode(),
)).Methods("POST")
r.Handle("/admin/newuser", Handle(
auth.RequireAuthorization(),
admin.AddUser(),
)).Methods("POST")
r.Handle("/admin/edit", Handle(
auth.RequireAuthorization(),
admin.EditEpisode(),
)).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")
r.Handle("/setup", Handle(
serveSetup(),
)).Methods("GET", "POST")
return r
}
func loginHandler() common.Handler {
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
db, err := sql.Open("sqlite3", "assets/config/users.db")
if err != nil {
return &common.HTTPError{
Message: fmt.Sprintf("error in reading user database: %v", err),
StatusCode: http.StatusInternalServerError,
}
}
statement, err := db.Prepare("SELECT * FROM users WHERE username=?")
if _, err := auth.DecryptCookie(r); err == nil {
http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect)
return nil
}
if r.Method == "GET" {
w.Header().Set("Content-Type", "text/html")
return common.ReadAndServeFile("assets/web/login.html", w)
}
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")
rows, err := statement.Query(username)
if username == "" || password == "" {
return &common.HTTPError{
Message: "username or password is empty",
StatusCode: http.StatusBadRequest,
}
}
var id int
var dbun string
var dbhsh string
var dbrn string
var dbem string
for rows.Next() {
err := rows.Scan(&id,&dbun,&dbhsh,&dbrn,&dbem)
if err != nil {
return &common.HTTPError{
Message: fmt.Sprintf("error in decoding sql data", err),
StatusCode: http.StatusBadRequest,
}
}
}
// Create a cookie here because the credentials are correct
if dbun == username && bcrypt.CompareHashAndPassword([]byte(dbhsh), []byte(password)) == nil {
c, err := auth.CreateSession(&common.User{
Username: username,
})
if err != nil {
return &common.HTTPError{
Message: err.Error(),
StatusCode: http.StatusInternalServerError,
}
}
// r.AddCookie(c)
w.Header().Add("Set-Cookie", c.String())
// And now redirect the user to admin page
http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect)
db.Close()
return nil
}
return &common.HTTPError{
Message: "Invalid credentials!",
StatusCode: http.StatusUnauthorized,
}
}
}
// 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
}
}