mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-24 09:25:15 +00:00
Basic API structure
This commit is contained in:
parent
28d8d3491a
commit
529ac7ab84
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,7 +5,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
.env
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
|
@ -6,6 +6,8 @@ Currently building on Node V15.X & Go V1.16.X
|
||||
|
||||
With a prebuilt binary - you will still need the migrations folder + web/build folder on prod.
|
||||
|
||||
Copy .env.example to .env and set variables. You can use https://www.grc.com/passwords.htm to generate a JWT_SECRET.
|
||||
|
||||
|
||||
## Setup MySQL
|
||||
create user 'goscrobble'@'%' identified by 'supersecurepass';
|
||||
@ -13,10 +15,11 @@ With a prebuilt binary - you will still need the migrations folder + web/build f
|
||||
grant all privileges on goscrobble.* to 'goscrobble'@'%';
|
||||
|
||||
## Local build/run
|
||||
cp .env.example .env # Fill in the blanks
|
||||
cd web && npm install && npm start
|
||||
# In another terminal
|
||||
go mod tidy
|
||||
CGO_ENABLED=0 MYSQL_HOST=127.0.0.1 MYSQL_USER=goscrobble MYSQL_PASS=supersecurepass MYSQL_DB=goscrobble go run cmd/go-scrobble/*.go
|
||||
CGO_ENABLED=0 go run cmd/go-scrobble/*.go
|
||||
|
||||
Access dev frontend @ http://127.0.0.1:3000 + API @ http://127.0.0.1:42069/api/v1
|
||||
|
||||
@ -25,7 +28,7 @@ Access dev frontend @ http://127.0.0.1:3000 + API @ http://127.0.0.1:42069/api/v
|
||||
|
||||
## Prod deployment
|
||||
We need to build NPM package, and then ship web/build with the binary.
|
||||
|
||||
cp .env.example .env # Fill in the blanks
|
||||
cd web npm install --production && npm run build
|
||||
go build -o goscrobble cmd/go-scrobble/*.go
|
||||
MYSQL_HOST=127.0.0.1 MYSQL_USER=goscrobble MYSQL_PASS=supersecurepass MYSQL_DB=goscrobble ./goscrobble
|
||||
./goscrobble
|
@ -1,10 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.m2.nz/go-scrobble/internal/goscrobble"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
// Store JWT secret
|
||||
goscrobble.JwtToken = []byte(os.Getenv("JWT_SECRET"))
|
||||
|
||||
// // Boot up DB connection for life of application
|
||||
goscrobble.InitDb()
|
||||
defer goscrobble.CloseDbConn()
|
||||
|
2
go.mod
2
go.mod
@ -5,6 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 // indirect
|
||||
github.com/containerd/containerd v1.4.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
@ -14,6 +15,7 @@ require (
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -8,6 +8,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
|
||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8=
|
||||
@ -47,6 +49,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
@ -5,10 +5,6 @@ After=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
Environment="MYSQL_HOST=127.0.0.1"
|
||||
Environment="MYSQL_USER=goscrobble"
|
||||
Environment="MYSQL_PASS=supersecurepass"
|
||||
Environment="MYSQL_DB=goscrobble"
|
||||
ExecStart=/opt/goscrobble/goscrobble
|
||||
Restart=on-failure
|
||||
|
||||
|
44
internal/goscrobble/jwt.go
Normal file
44
internal/goscrobble/jwt.go
Normal file
@ -0,0 +1,44 @@
|
||||
package goscrobble
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// JwtToken - Store token from .env
|
||||
var JwtToken []byte
|
||||
|
||||
// Store custom claims here
|
||||
type Claims struct {
|
||||
UUID string `json:"uuid"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// verifyToken - Verifies the JWT is valid
|
||||
func verifyToken(token string, w http.ResponseWriter) bool {
|
||||
// Initialize a new instance of `Claims`
|
||||
claims := &Claims{}
|
||||
|
||||
tkn, err := jwt.ParseWithClaims(token, claims, func(JwtToken *jwt.Token) (interface{}, error) {
|
||||
return JwtToken, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%v", err)
|
||||
if err == jwt.ErrSignatureInvalid {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return false
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return false
|
||||
}
|
||||
if !tkn.Valid {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -2,6 +2,7 @@ package goscrobble
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
@ -17,24 +18,29 @@ type spaHandler struct {
|
||||
indexPath string
|
||||
}
|
||||
|
||||
type jsonResponse struct {
|
||||
Err string `json:"error"`
|
||||
}
|
||||
|
||||
// HandleRequests - Boot HTTP!
|
||||
func HandleRequests() {
|
||||
// Create a new router
|
||||
r := mux.NewRouter().StrictSlash(true)
|
||||
|
||||
v1 := r.PathPrefix("/api/v1").Subrouter()
|
||||
// STATIC TOKEN AUTH
|
||||
// httpRouter.HandleFunc("/api/v1/ingress/jellyfin", serveEndpoint)
|
||||
|
||||
// JWT SESSION AUTH?
|
||||
// httpRouter.HandleFunc("/api/v1/profile/{id}", serveEndpoint)
|
||||
// Static Token for /ingress
|
||||
v1.HandleFunc("/ingress/jellyfin", tokenMiddleware(serveEndpoint))
|
||||
|
||||
// NO AUTH
|
||||
// JWT Auth
|
||||
v1.HandleFunc("/profile/{id}", jwtMiddleware(serveEndpoint))
|
||||
|
||||
// No Auth
|
||||
v1.HandleFunc("/register", serveEndpoint).Methods("POST")
|
||||
v1.HandleFunc("/login", serveEndpoint).Methods("POST")
|
||||
v1.HandleFunc("/logout", serveEndpoint).Methods("POST")
|
||||
|
||||
// This just prevents it serving frontend over /api
|
||||
// This just prevents it serving frontend stuff over /api
|
||||
r.PathPrefix("/api")
|
||||
|
||||
// SERVE FRONTEND - NO AUTH
|
||||
@ -45,6 +51,39 @@ func HandleRequests() {
|
||||
log.Fatal(http.ListenAndServe(":42069", r))
|
||||
}
|
||||
|
||||
// MIDDLEWARE
|
||||
|
||||
// tokenMiddleware - Validates token to a user
|
||||
func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(res http.ResponseWriter, req *http.Request) {
|
||||
jr := jsonResponse{
|
||||
Err: "Invalid API Token",
|
||||
}
|
||||
js, _ := json.Marshal(&jr)
|
||||
err := errors.New(string(js))
|
||||
http.Error(res, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
// next(res, req)
|
||||
}
|
||||
}
|
||||
|
||||
// jwtMiddleware - Validates middleware to a user
|
||||
func jwtMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(res http.ResponseWriter, req *http.Request) {
|
||||
jr := jsonResponse{
|
||||
Err: "Invalid JWT Token",
|
||||
}
|
||||
js, _ := json.Marshal(&jr)
|
||||
err := errors.New(string(js))
|
||||
http.Error(res, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
// next(res, req)
|
||||
}
|
||||
}
|
||||
|
||||
// ENDPOINT HANDLING
|
||||
|
||||
// serveEndpoint - API stuffs
|
||||
func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var jsonInput map[string]interface{}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
@ -59,6 +98,8 @@ func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "{}")
|
||||
}
|
||||
|
||||
// FRONTEND HANDLING
|
||||
|
||||
// ServerHTTP - Frontend server
|
||||
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the absolute path to prevent directory traversal
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"short_name": "GoScrobble",
|
||||
"name": "GoScrobble - Go based scrobbler",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
Loading…
Reference in New Issue
Block a user