mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-24 17:35:16 +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
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
.env
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.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.
|
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
|
## Setup MySQL
|
||||||
create user 'goscrobble'@'%' identified by 'supersecurepass';
|
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'@'%';
|
grant all privileges on goscrobble.* to 'goscrobble'@'%';
|
||||||
|
|
||||||
## Local build/run
|
## Local build/run
|
||||||
|
cp .env.example .env # Fill in the blanks
|
||||||
cd web && npm install && npm start
|
cd web && npm install && npm start
|
||||||
# In another terminal
|
# In another terminal
|
||||||
go mod tidy
|
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
|
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
|
## Prod deployment
|
||||||
We need to build NPM package, and then ship web/build with the binary.
|
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
|
cd web npm install --production && npm run build
|
||||||
go build -o goscrobble cmd/go-scrobble/*.go
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
"git.m2.nz/go-scrobble/internal/goscrobble"
|
"git.m2.nz/go-scrobble/internal/goscrobble"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
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
|
// // Boot up DB connection for life of application
|
||||||
goscrobble.InitDb()
|
goscrobble.InitDb()
|
||||||
defer goscrobble.CloseDbConn()
|
defer goscrobble.CloseDbConn()
|
||||||
|
2
go.mod
2
go.mod
@ -5,6 +5,7 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 // indirect
|
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 // indirect
|
||||||
github.com/containerd/containerd v1.4.1 // 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/distribution v2.7.1+incompatible // indirect
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+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
|
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-migrate/migrate v3.5.4+incompatible
|
||||||
github.com/golang/protobuf v1.4.3 // indirect
|
github.com/golang/protobuf v1.4.3 // indirect
|
||||||
github.com/gorilla/mux v1.8.0
|
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/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||||
github.com/pkg/errors v0.9.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 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
|
||||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
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/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 h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
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=
|
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/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 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
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/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/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=
|
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]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=www-data
|
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
|
ExecStart=/opt/goscrobble/goscrobble
|
||||||
Restart=on-failure
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -17,24 +18,29 @@ type spaHandler struct {
|
|||||||
indexPath string
|
indexPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonResponse struct {
|
||||||
|
Err string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
// HandleRequests - Boot HTTP!
|
// HandleRequests - Boot HTTP!
|
||||||
func HandleRequests() {
|
func HandleRequests() {
|
||||||
// Create a new router
|
// Create a new router
|
||||||
r := mux.NewRouter().StrictSlash(true)
|
r := mux.NewRouter().StrictSlash(true)
|
||||||
|
|
||||||
v1 := r.PathPrefix("/api/v1").Subrouter()
|
v1 := r.PathPrefix("/api/v1").Subrouter()
|
||||||
// STATIC TOKEN AUTH
|
|
||||||
// httpRouter.HandleFunc("/api/v1/ingress/jellyfin", serveEndpoint)
|
|
||||||
|
|
||||||
// JWT SESSION AUTH?
|
// Static Token for /ingress
|
||||||
// httpRouter.HandleFunc("/api/v1/profile/{id}", serveEndpoint)
|
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("/register", serveEndpoint).Methods("POST")
|
||||||
v1.HandleFunc("/login", serveEndpoint).Methods("POST")
|
v1.HandleFunc("/login", serveEndpoint).Methods("POST")
|
||||||
v1.HandleFunc("/logout", 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")
|
r.PathPrefix("/api")
|
||||||
|
|
||||||
// SERVE FRONTEND - NO AUTH
|
// SERVE FRONTEND - NO AUTH
|
||||||
@ -45,6 +51,39 @@ func HandleRequests() {
|
|||||||
log.Fatal(http.ListenAndServe(":42069", r))
|
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) {
|
func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
var jsonInput map[string]interface{}
|
var jsonInput map[string]interface{}
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
@ -59,6 +98,8 @@ func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
fmt.Fprintf(w, "{}")
|
fmt.Fprintf(w, "{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FRONTEND HANDLING
|
||||||
|
|
||||||
// ServerHTTP - Frontend server
|
// ServerHTTP - Frontend server
|
||||||
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// Get the absolute path to prevent directory traversal
|
// Get the absolute path to prevent directory traversal
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"short_name": "React App",
|
"short_name": "GoScrobble",
|
||||||
"name": "Create React App Sample",
|
"name": "GoScrobble - Go based scrobbler",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
Loading…
Reference in New Issue
Block a user