Split routes, router, and handlers into different files
This commit is contained in:
parent
0cde2fb6db
commit
d92ac4430e
41
src/api/routes/handlers.go
Normal file
41
src/api/routes/handlers.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/danieljsummers/myPrayerJournal/src/api/data"
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Support */
|
||||||
|
|
||||||
|
// Set the content type, the HTTP error code, and return the error message.
|
||||||
|
func sendError(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; encoding=UTF-8")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
if err := json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}); err != nil {
|
||||||
|
log.Print("Error creating error JSON: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the content type and return the JSON to the user.
|
||||||
|
func sendJSON(w http.ResponseWriter, r *http.Request, result interface{}) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; encoding=UTF-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(map[string]interface{}{"data": result}); err != nil {
|
||||||
|
sendError(w, r, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handlers */
|
||||||
|
|
||||||
|
func journal(w http.ResponseWriter, r *http.Request, _ httprouter.Params, db *sql.DB) {
|
||||||
|
reqs := data.Journal(db, "TODO: get user ID")
|
||||||
|
if reqs == nil {
|
||||||
|
reqs = []data.JournalRequest{}
|
||||||
|
}
|
||||||
|
sendJSON(w, r, reqs)
|
||||||
|
}
|
66
src/api/routes/router.go
Normal file
66
src/api/routes/router.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
auth0 "github.com/auth0-community/go-auth0"
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
jose "gopkg.in/square/go-jose.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthConfig contains the Auth0 configuration passed from the "auth" JSON object.
|
||||||
|
type AuthConfig struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
ClientID string `json:"id"`
|
||||||
|
ClientSecret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBHandler extends httprouter's handler with a DB instance
|
||||||
|
type DBHandler func(http.ResponseWriter, *http.Request, httprouter.Params, *sql.DB)
|
||||||
|
|
||||||
|
//type APIHandler func(http.ResponseWriter, *http.Request, httprouter.Params, *sql.DB, string)
|
||||||
|
|
||||||
|
func withDB(next DBHandler, db *sql.DB) httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
|
next(w, r, p, db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func withAuth(next DBHandler, cfg *AuthConfig) DBHandler {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params, db *sql.DB) {
|
||||||
|
secret := []byte(cfg.ClientSecret)
|
||||||
|
secretProvider := auth0.NewKeyProvider(secret)
|
||||||
|
audience := []string{"{YOUR-AUTH0-API-AUDIENCE}"}
|
||||||
|
|
||||||
|
configuration := auth0.NewConfiguration(secretProvider, audience, fmt.Sprintf("https://%s.auth0.com/", cfg.Domain), jose.HS256)
|
||||||
|
validator := auth0.NewValidator(configuration)
|
||||||
|
|
||||||
|
token, err := validator.ValidateRequest(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("Token is not valid:", token)
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte("Unauthorized"))
|
||||||
|
} else {
|
||||||
|
// TODO pass the user ID (sub) along; this -> doesn't work | r.Header.Add("user-id", token.Claims("sub"))
|
||||||
|
next(w, r, p, db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouter returns a configured router to handle all incoming requests.
|
||||||
|
func NewRouter(db *sql.DB, cfg *AuthConfig) *httprouter.Router {
|
||||||
|
router := httprouter.New()
|
||||||
|
for _, route := range routes {
|
||||||
|
if route.IsPublic {
|
||||||
|
router.Handle(route.Method, route.Pattern, withDB(route.Func, db))
|
||||||
|
} else {
|
||||||
|
router.Handle(route.Method, route.Pattern, withDB(withAuth(route.Func, cfg), db))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return router
|
||||||
|
}
|
@ -1,64 +1,25 @@
|
|||||||
// Package routes contains endpoint handlers for the myPrayerJournal API.
|
// Package routes contains endpoint handlers for the myPrayerJournal API.
|
||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
// Route is a route served in the application.
|
||||||
"database/sql"
|
type Route struct {
|
||||||
"encoding/json"
|
Name string
|
||||||
"log"
|
Method string
|
||||||
"net/http"
|
Pattern string
|
||||||
|
Func DBHandler
|
||||||
"github.com/danieljsummers/myPrayerJournal/src/api/data"
|
IsPublic bool
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Support */
|
|
||||||
|
|
||||||
// Set the content type, the HTTP error code, and return the error message.
|
|
||||||
func sendError(w http.ResponseWriter, r *http.Request, err error) {
|
|
||||||
m := map[string]string{"error": err.Error()}
|
|
||||||
j, jErr := json.Marshal(m)
|
|
||||||
if jErr != nil {
|
|
||||||
log.Print("Error creating error JSON: " + jErr.Error())
|
|
||||||
}
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.Write(j)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the content type and return the JSON to the user.
|
// Routes is the collection of all routes served in the application.
|
||||||
func sendJSON(w http.ResponseWriter, r *http.Request, result interface{}) {
|
type Routes []Route
|
||||||
payload, err := json.Marshal(result)
|
|
||||||
if err != nil {
|
|
||||||
sendError(w, r, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.Write([]byte("{ data: "))
|
|
||||||
w.Write(payload)
|
|
||||||
w.Write([]byte(" }"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handlers */
|
// routes is the actual list of routes for the application.
|
||||||
|
var routes = Routes{
|
||||||
func journal(w http.ResponseWriter, r *http.Request, _ httprouter.Params, db *sql.DB) {
|
Route{
|
||||||
reqs := data.Journal(db, "TODO: get user ID")
|
"Journal",
|
||||||
if reqs == nil {
|
"GET",
|
||||||
reqs = []data.JournalRequest{}
|
"/journal",
|
||||||
}
|
journal,
|
||||||
sendJSON(w, r, reqs)
|
false,
|
||||||
}
|
},
|
||||||
|
|
||||||
/* Wrappers */
|
|
||||||
|
|
||||||
func withDB(fn func(w http.ResponseWriter, r *http.Request, p httprouter.Params, db *sql.DB), db *sql.DB) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
||||||
fn(w, r, p, db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routes returns a configured router to handle all incoming requests.
|
|
||||||
func Routes(db *sql.DB) *httprouter.Router {
|
|
||||||
router := httprouter.New()
|
|
||||||
router.GET("/journal", withDB(journal, db))
|
|
||||||
return router
|
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,48 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/danieljsummers/myPrayerJournal/src/api/data"
|
"github.com/danieljsummers/myPrayerJournal/src/api/data"
|
||||||
"github.com/danieljsummers/myPrayerJournal/src/api/routes"
|
"github.com/danieljsummers/myPrayerJournal/src/api/routes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Web contains configuration for the web server.
|
||||||
|
type Web struct {
|
||||||
|
Port string `json:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings contains configuration for the myPrayerJournal API.
|
||||||
|
type Settings struct {
|
||||||
|
Data *data.Settings `json:"data"`
|
||||||
|
Web *Web `json:"web"`
|
||||||
|
Auth *routes.AuthConfig `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// readSettings parses the JSON configuration file into the Settings struct.
|
||||||
|
func readSettings(f string) *Settings {
|
||||||
|
config, err := os.Open(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer config.Close()
|
||||||
|
parser := json.NewDecoder(config)
|
||||||
|
settings := Settings{}
|
||||||
|
if err = parser.Decode(&settings); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return &settings
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
db, ok := data.Connect(&data.Settings{})
|
cfg := readSettings("config.json")
|
||||||
|
db, ok := data.Connect(cfg.Data)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatal("Unable to connect to database; exiting")
|
log.Fatal("Unable to connect to database; exiting")
|
||||||
}
|
}
|
||||||
router := routes.Routes(db)
|
log.Printf("myPrayerJournal API listening on %s", cfg.Web.Port)
|
||||||
_ = router // TODO: remove
|
log.Fatal(http.ListenAndServe(cfg.Web.Port, routes.NewRouter(db, cfg.Auth)))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user