sliproad/router/router.go
Gabriel Simmer bf1f06b79c Refactoring file storage to enable different providers
Expanding this as we go, currently have POC Backblaze B2 support and
basic 'disk' provider as well. Still WIP, but functional for the most
part. Also moving to simplified YAML configuration.

Overall, simplifying things to be extensible down the line. Still work
to be done, but coming along nicely.
2020-02-24 18:07:47 +00:00

172 lines
4 KiB
Go

package router
import (
"database/sql"
"fmt"
"github.com/gmemstr/nas/auth"
"golang.org/x/crypto/bcrypt"
"log"
"net/http"
"github.com/gmemstr/nas/common"
"github.com/gorilla/mux"
)
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
}
}
})
}
// Actual router, define endpoints here.
func Init() *mux.Router {
r := mux.NewRouter()
// "Static" paths
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("assets/web/static"))))
// Paths that require specific handlers
r.Handle("/", Handle(
auth.RequireAuthorization(1),
rootHandler(),
)).Methods("GET")
r.Handle(`/login`, Handle(
loginHandler(),
)).Methods("POST", "GET")
r.Handle("/api/providers", Handle(
auth.RequireAuthorization(1),
ListProviders(),
)).Methods("GET")
r.Handle(`/api/files/{provider}`, Handle(
//auth.RequireAuthorization(1),
HandleProvider(),
)).Methods("GET")
r.Handle(`/api/files/{provider}/{file:[a-zA-Z0-9=\-\/\s.,&_+]+}`, Handle(
//auth.RequireAuthorization(1),
HandleProvider(),
)).Methods("GET")
return r
}
func loginHandler() common.Handler {
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
if r.Method == "GET" {
w.Header().Set("Content-Type", "text/html")
file := "assets/web/index.html"
return common.ReadAndServeFile(file, w)
}
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
}
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 == "" || err != nil {
return &common.HTTPError{
Message: "username or password is invalid",
StatusCode: http.StatusBadRequest,
}
}
var id int
var dbun string
var dbhsh string
var dbtoken sql.NullString
var dbperm int
for rows.Next() {
err := rows.Scan(&id, &dbun, &dbhsh, &dbtoken, &dbperm)
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 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, "/", http.StatusTemporaryRedirect)
db.Close()
return nil
}
return &common.HTTPError{
Message: "Invalid credentials!",
StatusCode: http.StatusUnauthorized,
}
}
}
// Handles /.
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 "/":
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)
}
}