mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-08-28 11:40:04 +00:00
Version 3 starter
This commit is contained in:
54
backend/internal/api/handler/auth.go
Normal file
54
backend/internal/api/handler/auth.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/entity/auth"
|
||||
"npm/internal/logger"
|
||||
)
|
||||
|
||||
// SetAuth ...
|
||||
// Route: POST /users/:userID/auth
|
||||
func SetAuth() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
// TODO:
|
||||
// delete old auth for user
|
||||
// test endpoint
|
||||
|
||||
var newAuth auth.Model
|
||||
err := json.Unmarshal(bodyBytes, &newAuth)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
userID, _, userIDErr := getUserIDFromRequest(r)
|
||||
if userIDErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
newAuth.UserID = userID
|
||||
if newAuth.Type == auth.TypePassword {
|
||||
err := newAuth.SetPassword(newAuth.Secret)
|
||||
if err != nil {
|
||||
logger.Error("SetPasswordError", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = newAuth.Save(); err != nil {
|
||||
logger.Error("AuthSaveError", err)
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save Authentication for User", nil)
|
||||
return
|
||||
}
|
||||
|
||||
newAuth.Secret = ""
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newAuth)
|
||||
}
|
||||
}
|
126
backend/internal/api/handler/certificate_authorities.go
Normal file
126
backend/internal/api/handler/certificate_authorities.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/certificateauthority"
|
||||
)
|
||||
|
||||
// GetCertificateAuthorities will return a list of Certificate Authorities
|
||||
// Route: GET /certificate-authorities
|
||||
func GetCertificateAuthorities() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
certificates, err := certificateauthority.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, certificates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetCertificateAuthority will return a single Certificate Authority
|
||||
// Route: GET /certificate-authorities/{caID}
|
||||
func GetCertificateAuthority() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var caID int
|
||||
if caID, err = getURLParamInt(r, "caID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := certificateauthority.GetByID(caID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCertificateAuthority will create a Certificate Authority
|
||||
// Route: POST /certificate-authorities
|
||||
func CreateCertificateAuthority() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newCA certificateauthority.Model
|
||||
err := json.Unmarshal(bodyBytes, &newCA)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = newCA.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Certificate Authority: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newCA)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateCertificateAuthority ...
|
||||
// Route: PUT /certificate-authorities/{caID}
|
||||
func UpdateCertificateAuthority() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var caID int
|
||||
if caID, err = getURLParamInt(r, "caID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
ca, err := certificateauthority.GetByID(caID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &ca)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = ca.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, ca)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCertificateAuthority ...
|
||||
// Route: DELETE /certificate-authorities/{caID}
|
||||
func DeleteCertificateAuthority() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var caID int
|
||||
if caID, err = getURLParamInt(r, "caID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := certificateauthority.GetByID(caID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, cert.Delete())
|
||||
}
|
||||
}
|
||||
}
|
145
backend/internal/api/handler/certificates.go
Normal file
145
backend/internal/api/handler/certificates.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/api/schema"
|
||||
"npm/internal/entity/certificate"
|
||||
)
|
||||
|
||||
// GetCertificates will return a list of Certificates
|
||||
// Route: GET /certificates
|
||||
func GetCertificates() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
certificates, err := certificate.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, certificates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetCertificate will return a single Certificate
|
||||
// Route: GET /certificates/{certificateID}
|
||||
func GetCertificate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var certificateID int
|
||||
if certificateID, err = getURLParamInt(r, "certificateID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := certificate.GetByID(certificateID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCertificate will create a Certificate
|
||||
// Route: POST /certificates
|
||||
func CreateCertificate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newCertificate certificate.Model
|
||||
err := json.Unmarshal(bodyBytes, &newCertificate)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
|
||||
newCertificate.UserID = userID
|
||||
|
||||
if err = newCertificate.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Certificate: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newCertificate)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateCertificate ...
|
||||
// Route: PUT /certificates/{certificateID}
|
||||
func UpdateCertificate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var certificateID int
|
||||
if certificateID, err = getURLParamInt(r, "certificateID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
certificateObject, err := certificate.GetByID(certificateID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
|
||||
// This is a special endpoint, as it needs to verify the schema payload
|
||||
// based on the certificate type, without being given a type in the payload.
|
||||
// The middleware would normally handle this.
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
schemaErrors, jsonErr := middleware.CheckRequestSchema(r.Context(), schema.UpdateCertificate(certificateObject.Type), bodyBytes)
|
||||
if jsonErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, fmt.Sprintf("Schema Fatal: %v", jsonErr), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if len(schemaErrors) > 0 {
|
||||
h.ResultSchemaErrorJSON(w, r, schemaErrors)
|
||||
return
|
||||
}
|
||||
|
||||
err := json.Unmarshal(bodyBytes, &certificateObject)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = certificateObject.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, certificateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCertificate ...
|
||||
// Route: DELETE /certificates/{certificateID}
|
||||
func DeleteCertificate() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var certificateID int
|
||||
if certificateID, err = getURLParamInt(r, "certificateID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := certificate.GetByID(certificateID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, cert.Delete())
|
||||
}
|
||||
}
|
||||
}
|
15
backend/internal/api/handler/config.go
Normal file
15
backend/internal/api/handler/config.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/config"
|
||||
)
|
||||
|
||||
// Config returns the entire configuration, for debug purposes
|
||||
// Route: GET /config
|
||||
func Config() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, config.Configuration)
|
||||
}
|
||||
}
|
129
backend/internal/api/handler/dns_providers.go
Normal file
129
backend/internal/api/handler/dns_providers.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/dnsprovider"
|
||||
)
|
||||
|
||||
// GetDNSProviders will return a list of DNS Providers
|
||||
// Route: GET /dns-providers
|
||||
func GetDNSProviders() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
items, err := dnsprovider.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetDNSProvider will return a single DNS Provider
|
||||
// Route: GET /dns-providers/{providerID}
|
||||
func GetDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var providerID int
|
||||
if providerID, err = getURLParamInt(r, "providerID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := dnsprovider.GetByID(providerID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateDNSProvider will create a DNS Provider
|
||||
// Route: POST /dns-providers
|
||||
func CreateDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newItem dnsprovider.Model
|
||||
err := json.Unmarshal(bodyBytes, &newItem)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
|
||||
newItem.UserID = userID
|
||||
|
||||
if err = newItem.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save DNS Provider: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newItem)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateDNSProvider ...
|
||||
// Route: PUT /dns-providers/{providerID}
|
||||
func UpdateDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var providerID int
|
||||
if providerID, err = getURLParamInt(r, "providerID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := dnsprovider.GetByID(providerID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &item)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = item.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteDNSProvider ...
|
||||
// Route: DELETE /dns-providers/{providerID}
|
||||
func DeleteDNSProvider() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var providerID int
|
||||
if providerID, err = getURLParamInt(r, "providerID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
item, err := dnsprovider.GetByID(providerID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
|
||||
}
|
||||
}
|
||||
}
|
31
backend/internal/api/handler/health.go
Normal file
31
backend/internal/api/handler/health.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/config"
|
||||
)
|
||||
|
||||
type healthCheckResponse struct {
|
||||
Version string `json:"version"`
|
||||
Commit string `json:"commit"`
|
||||
Healthy bool `json:"healthy"`
|
||||
IsSetup bool `json:"setup"`
|
||||
ErrorReporting bool `json:"error_reporting"`
|
||||
}
|
||||
|
||||
// Health returns the health of the api
|
||||
// Route: GET /health
|
||||
func Health() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
health := healthCheckResponse{
|
||||
Version: config.Version,
|
||||
Commit: config.Commit,
|
||||
Healthy: true,
|
||||
IsSetup: config.IsSetup,
|
||||
ErrorReporting: config.ErrorReporting,
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, health)
|
||||
}
|
||||
}
|
151
backend/internal/api/handler/helpers.go
Normal file
151
backend/internal/api/handler/helpers.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"npm/internal/model"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
const defaultLimit = 10
|
||||
|
||||
func getPageInfoFromRequest(r *http.Request) (model.PageInfo, error) {
|
||||
var pageInfo model.PageInfo
|
||||
var err error
|
||||
|
||||
pageInfo.FromDate, pageInfo.ToDate, err = getDateRanges(r)
|
||||
if err != nil {
|
||||
return pageInfo, err
|
||||
}
|
||||
|
||||
pageInfo.Offset, pageInfo.Limit, err = getPagination(r)
|
||||
if err != nil {
|
||||
return pageInfo, err
|
||||
}
|
||||
|
||||
pageInfo.Sort = getSortParameter(r)
|
||||
|
||||
return pageInfo, nil
|
||||
}
|
||||
|
||||
func getDateRanges(r *http.Request) (time.Time, time.Time, error) {
|
||||
queryValues := r.URL.Query()
|
||||
from := queryValues.Get("from")
|
||||
fromDate := time.Now().AddDate(0, -1, 0) // 1 month ago by default
|
||||
to := queryValues.Get("to")
|
||||
toDate := time.Now()
|
||||
|
||||
if from != "" {
|
||||
var fromErr error
|
||||
fromDate, fromErr = time.Parse(time.RFC3339, from)
|
||||
if fromErr != nil {
|
||||
return fromDate, toDate, fmt.Errorf("From date is not in correct format: %v", strings.ReplaceAll(time.RFC3339, "Z", "+"))
|
||||
}
|
||||
}
|
||||
|
||||
if to != "" {
|
||||
var toErr error
|
||||
toDate, toErr = time.Parse(time.RFC3339, to)
|
||||
if toErr != nil {
|
||||
return fromDate, toDate, fmt.Errorf("To date is not in correct format: %v", strings.ReplaceAll(time.RFC3339, "Z", "+"))
|
||||
}
|
||||
}
|
||||
|
||||
return fromDate, toDate, nil
|
||||
}
|
||||
|
||||
func getSortParameter(r *http.Request) []model.Sort {
|
||||
var sortFields []model.Sort
|
||||
|
||||
queryValues := r.URL.Query()
|
||||
sortString := queryValues.Get("sort")
|
||||
if sortString == "" {
|
||||
return sortFields
|
||||
}
|
||||
|
||||
// Split sort fields up in to slice
|
||||
sorts := strings.Split(sortString, ",")
|
||||
for _, sortItem := range sorts {
|
||||
if strings.Contains(sortItem, ".") {
|
||||
theseItems := strings.Split(sortItem, ".")
|
||||
|
||||
switch strings.ToLower(theseItems[1]) {
|
||||
case "desc":
|
||||
fallthrough
|
||||
case "descending":
|
||||
theseItems[1] = "DESC"
|
||||
default:
|
||||
theseItems[1] = "ASC"
|
||||
}
|
||||
|
||||
sortFields = append(sortFields, model.Sort{
|
||||
Field: theseItems[0],
|
||||
Direction: theseItems[1],
|
||||
})
|
||||
} else {
|
||||
sortFields = append(sortFields, model.Sort{
|
||||
Field: sortItem,
|
||||
Direction: "ASC",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return sortFields
|
||||
}
|
||||
|
||||
func getQueryVarInt(r *http.Request, varName string, required bool, defaultValue int) (int, error) {
|
||||
queryValues := r.URL.Query()
|
||||
varValue := queryValues.Get(varName)
|
||||
|
||||
if varValue == "" && required {
|
||||
return 0, fmt.Errorf("%v was not supplied in the request", varName)
|
||||
} else if varValue == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
varInt, intErr := strconv.Atoi(varValue)
|
||||
if intErr != nil {
|
||||
return 0, fmt.Errorf("%v is not a valid number", varName)
|
||||
}
|
||||
|
||||
return varInt, nil
|
||||
}
|
||||
|
||||
func getURLParamInt(r *http.Request, varName string) (int, error) {
|
||||
required := true
|
||||
defaultValue := 0
|
||||
paramStr := chi.URLParam(r, varName)
|
||||
var err error
|
||||
var paramInt int
|
||||
|
||||
if paramStr == "" && required {
|
||||
return 0, fmt.Errorf("%v was not supplied in the request", varName)
|
||||
} else if paramStr == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
if paramInt, err = strconv.Atoi(paramStr); err != nil {
|
||||
return 0, fmt.Errorf("%v is not a valid number", varName)
|
||||
}
|
||||
|
||||
return paramInt, nil
|
||||
}
|
||||
|
||||
func getPagination(r *http.Request) (int, int, error) {
|
||||
var err error
|
||||
offset, err := getQueryVarInt(r, "offset", false, 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
limit, err := getQueryVarInt(r, "limit", false, defaultLimit)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return offset, limit, nil
|
||||
}
|
135
backend/internal/api/handler/hosts.go
Normal file
135
backend/internal/api/handler/hosts.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/host"
|
||||
"npm/internal/validator"
|
||||
)
|
||||
|
||||
// GetHosts will return a list of Hosts
|
||||
// Route: GET /hosts
|
||||
func GetHosts() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
hosts, err := host.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, hosts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetHost will return a single Host
|
||||
// Route: GET /hosts/{hostID}
|
||||
func GetHost() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID int
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := host.GetByID(hostID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateHost will create a Host
|
||||
// Route: POST /hosts
|
||||
func CreateHost() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newHost host.Model
|
||||
err := json.Unmarshal(bodyBytes, &newHost)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
|
||||
newHost.UserID = userID
|
||||
|
||||
if err = validator.ValidateHost(newHost); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = newHost.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Host: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newHost)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateHost ...
|
||||
// Route: PUT /hosts/{hostID}
|
||||
func UpdateHost() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID int
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := host.GetByID(hostID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &host)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = host.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteHost ...
|
||||
// Route: DELETE /hosts/{hostID}
|
||||
func DeleteHost() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID int
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := host.GetByID(hostID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host.Delete())
|
||||
}
|
||||
}
|
||||
}
|
14
backend/internal/api/handler/not_allowed.go
Normal file
14
backend/internal/api/handler/not_allowed.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
h "npm/internal/api/http"
|
||||
)
|
||||
|
||||
// NotAllowed is a json error handler for when method is not allowed
|
||||
func NotAllowed() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
h.ResultErrorJSON(w, r, http.StatusNotFound, "Not allowed", nil)
|
||||
}
|
||||
}
|
65
backend/internal/api/handler/not_found.go
Normal file
65
backend/internal/api/handler/not_found.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
h "npm/internal/api/http"
|
||||
)
|
||||
|
||||
//go:embed assets
|
||||
var assets embed.FS
|
||||
var assetsSub fs.FS
|
||||
|
||||
var errIsDir = errors.New("path is dir")
|
||||
|
||||
// NotFound is a json error handler for 404's and method not allowed.
|
||||
// It also serves the react frontend as embedded files in the golang binary.
|
||||
func NotFound() func(http.ResponseWriter, *http.Request) {
|
||||
assetsSub, _ = fs.Sub(assets, "assets")
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
path := strings.TrimLeft(r.URL.Path, "/")
|
||||
if path == "" {
|
||||
path = "index.html"
|
||||
}
|
||||
|
||||
err := tryRead(assetsSub, path, w)
|
||||
if err == errIsDir {
|
||||
err = tryRead(assetsSub, "index.html", w)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusNotFound, "Not found", nil)
|
||||
}
|
||||
} else if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultErrorJSON(w, r, http.StatusNotFound, "Not found", nil)
|
||||
}
|
||||
}
|
||||
|
||||
func tryRead(folder fs.FS, requestedPath string, w http.ResponseWriter) error {
|
||||
f, err := folder.Open(requestedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// nolint: errcheck
|
||||
defer f.Close()
|
||||
|
||||
stat, _ := f.Stat()
|
||||
if stat.IsDir() {
|
||||
return errIsDir
|
||||
}
|
||||
|
||||
contentType := mime.TypeByExtension(filepath.Ext(requestedPath))
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
_, err = io.Copy(w, f)
|
||||
return err
|
||||
}
|
99
backend/internal/api/handler/schema.go
Normal file
99
backend/internal/api/handler/schema.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"npm/doc"
|
||||
"npm/internal/api/schema"
|
||||
"npm/internal/config"
|
||||
"npm/internal/logger"
|
||||
|
||||
jsref "github.com/jc21/jsref"
|
||||
"github.com/jc21/jsref/provider"
|
||||
)
|
||||
|
||||
var swaggerSchema []byte
|
||||
|
||||
// Schema simply reads the swagger schema from disk and returns is raw
|
||||
// Route: GET /schema
|
||||
func Schema() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, string(getSchema()))
|
||||
}
|
||||
}
|
||||
|
||||
func getSchema() []byte {
|
||||
if swaggerSchema == nil {
|
||||
// nolint:gosec
|
||||
swaggerSchema, _ = doc.SwaggerFiles.ReadFile("api.swagger.json")
|
||||
|
||||
// Replace {{VERSION}} with Config Version
|
||||
swaggerSchema = []byte(strings.ReplaceAll(string(swaggerSchema), "{{VERSION}}", config.Version))
|
||||
|
||||
// Dereference the JSON Schema:
|
||||
var schema interface{}
|
||||
if err := json.Unmarshal(swaggerSchema, &schema); err != nil {
|
||||
logger.Error("SwaggerUnmarshalError", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
provider := provider.NewIoFS(doc.SwaggerFiles, "")
|
||||
resolver := jsref.New()
|
||||
err := resolver.AddProvider(provider)
|
||||
if err != nil {
|
||||
logger.Error("SchemaProviderError", err)
|
||||
}
|
||||
|
||||
result, err := resolver.Resolve(schema, "", []jsref.Option{jsref.WithRecursiveResolution(true)}...)
|
||||
if err != nil {
|
||||
logger.Error("SwaggerResolveError", err)
|
||||
} else {
|
||||
var marshalErr error
|
||||
swaggerSchema, marshalErr = json.MarshalIndent(result, "", " ")
|
||||
if marshalErr != nil {
|
||||
logger.Error("SwaggerMarshalError", err)
|
||||
}
|
||||
}
|
||||
// End dereference
|
||||
|
||||
// Replace incoming schemas with those we actually use in code
|
||||
swaggerSchema = replaceIncomingSchemas(swaggerSchema)
|
||||
}
|
||||
return swaggerSchema
|
||||
}
|
||||
|
||||
func replaceIncomingSchemas(swaggerSchema []byte) []byte {
|
||||
str := string(swaggerSchema)
|
||||
|
||||
// Remember to include the double quotes in the replacement!
|
||||
str = strings.ReplaceAll(str, `"{{schema.SetAuth}}"`, schema.SetAuth())
|
||||
str = strings.ReplaceAll(str, `"{{schema.GetToken}}"`, schema.GetToken())
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateCertificateAuthority}}"`, schema.CreateCertificateAuthority())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateCertificateAuthority}}"`, schema.UpdateCertificateAuthority())
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateCertificate}}"`, schema.CreateCertificate())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateCertificate}}"`, schema.UpdateCertificate(""))
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateSetting}}"`, schema.CreateSetting())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateSetting}}"`, schema.UpdateSetting())
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateUser}}"`, schema.CreateUser())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateUser}}"`, schema.UpdateUser())
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateHost}}"`, schema.CreateHost())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateHost}}"`, schema.UpdateHost())
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateStream}}"`, schema.CreateStream())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateStream}}"`, schema.UpdateStream())
|
||||
|
||||
str = strings.ReplaceAll(str, `"{{schema.CreateDNSProvider}}"`, schema.CreateDNSProvider())
|
||||
str = strings.ReplaceAll(str, `"{{schema.UpdateDNSProvider}}"`, schema.UpdateDNSProvider())
|
||||
|
||||
return []byte(str)
|
||||
}
|
98
backend/internal/api/handler/settings.go
Normal file
98
backend/internal/api/handler/settings.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/setting"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// GetSettings will return a list of Settings
|
||||
// Route: GET /settings
|
||||
func GetSettings() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
settings, err := setting.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSetting will return a single Setting
|
||||
// Route: GET /settings/{name}
|
||||
func GetSetting() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
name := chi.URLParam(r, "name")
|
||||
|
||||
sett, err := setting.GetByName(name)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, sett)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSetting will create a Setting
|
||||
// Route: POST /settings
|
||||
func CreateSetting() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newSetting setting.Model
|
||||
err := json.Unmarshal(bodyBytes, &newSetting)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = newSetting.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Setting: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newSetting)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSetting ...
|
||||
// Route: PUT /settings/{name}
|
||||
func UpdateSetting() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
settingName := chi.URLParam(r, "name")
|
||||
|
||||
setting, err := setting.GetByName(settingName)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &setting)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = setting.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, setting)
|
||||
}
|
||||
}
|
||||
}
|
129
backend/internal/api/handler/streams.go
Normal file
129
backend/internal/api/handler/streams.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/entity/stream"
|
||||
)
|
||||
|
||||
// GetStreams will return a list of Streams
|
||||
// Route: GET /hosts/streams
|
||||
func GetStreams() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
hosts, err := stream.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, hosts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetStream will return a single Streams
|
||||
// Route: GET /hosts/streams/{hostID}
|
||||
func GetStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID int
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := stream.GetByID(hostID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateStream will create a Stream
|
||||
// Route: POST /hosts/steams
|
||||
func CreateStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newHost stream.Model
|
||||
err := json.Unmarshal(bodyBytes, &newHost)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get userID from token
|
||||
userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
|
||||
newHost.UserID = userID
|
||||
|
||||
if err = newHost.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Stream: %s", err.Error()), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newHost)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateStream ...
|
||||
// Route: PUT /hosts/streams/{hostID}
|
||||
func UpdateStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID int
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := stream.GetByID(hostID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &host)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = host.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteStream ...
|
||||
// Route: DELETE /hosts/streams/{hostID}
|
||||
func DeleteStream() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var hostID int
|
||||
if hostID, err = getURLParamInt(r, "hostID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
host, err := stream.GetByID(hostID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, host.Delete())
|
||||
}
|
||||
}
|
||||
}
|
77
backend/internal/api/handler/tokens.go
Normal file
77
backend/internal/api/handler/tokens.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
h "npm/internal/api/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
"npm/internal/entity/auth"
|
||||
"npm/internal/entity/user"
|
||||
njwt "npm/internal/jwt"
|
||||
)
|
||||
|
||||
// tokenPayload is the structure we expect from a incoming login request
|
||||
type tokenPayload struct {
|
||||
Type string `json:"type"`
|
||||
Identity string `json:"identity"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
// NewToken Also known as a Login, requesting a new token with credentials
|
||||
// Route: POST /tokens
|
||||
func NewToken() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Read the bytes from the body
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var payload tokenPayload
|
||||
err := json.Unmarshal(bodyBytes, &payload)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Find user
|
||||
userObj, userErr := user.GetByEmail(payload.Identity)
|
||||
if userErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, userErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Get Auth
|
||||
authObj, authErr := auth.GetByUserIDType(userObj.ID, payload.Type)
|
||||
if userErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, authErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify Auth
|
||||
validateErr := authObj.ValidateSecret(payload.Secret)
|
||||
if validateErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, validateErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if response, err := njwt.Generate(&userObj); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RefreshToken an existing token by given them a new one with the same claims
|
||||
// Route: GET /tokens
|
||||
func RefreshToken() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO: Use your own methods to verify an existing user is
|
||||
// able to refresh their token and then give them a new one
|
||||
userObj, _ := user.GetByEmail("jc@jc21.com")
|
||||
if response, err := njwt.Generate(&userObj); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, response)
|
||||
}
|
||||
}
|
||||
}
|
206
backend/internal/api/handler/users.go
Normal file
206
backend/internal/api/handler/users.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
c "npm/internal/api/context"
|
||||
h "npm/internal/api/http"
|
||||
"npm/internal/api/middleware"
|
||||
"npm/internal/config"
|
||||
"npm/internal/entity/auth"
|
||||
"npm/internal/entity/user"
|
||||
"npm/internal/errors"
|
||||
"npm/internal/logger"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// GetUsers ...
|
||||
// Route: GET /users
|
||||
func GetUsers() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
pageInfo, err := getPageInfoFromRequest(r)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := user.List(pageInfo, middleware.GetFiltersFromContext(r))
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, users)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetUser ...
|
||||
// Route: GET /users/{userID}
|
||||
func GetUser() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
userID, _, userIDErr := getUserIDFromRequest(r)
|
||||
if userIDErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := user.GetByID(userID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUser ...
|
||||
// Route: PUT /users/{userID}
|
||||
func UpdateUser() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, self, userIDErr := getUserIDFromRequest(r)
|
||||
if userIDErr != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := user.GetByID(userID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
err := json.Unmarshal(bodyBytes, &user)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if user.IsDisabled && self {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot disable yourself!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = user.Save(); err != nil {
|
||||
if err == errors.ErrDuplicateEmailUser {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save User", nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteUser ...
|
||||
// Route: DELETE /users/{userID}
|
||||
func DeleteUser() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var userID int
|
||||
var err error
|
||||
if userID, err = getURLParamInt(r, "userID"); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
myUserID, _ := r.Context().Value(c.UserIDCtxKey).(int)
|
||||
if myUserID == userID {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot delete yourself!", nil)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := user.GetByID(userID)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, user.Delete())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUser ...
|
||||
// Route: POST /users
|
||||
func CreateUser() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
||||
|
||||
var newUser user.Model
|
||||
err := json.Unmarshal(bodyBytes, &newUser)
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err = newUser.Save(); err != nil {
|
||||
if err == errors.ErrDuplicateEmailUser {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save User", nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// newUser has been saved, now save their auth
|
||||
if newUser.Auth.Secret != "" && newUser.Auth.ID == 0 {
|
||||
newUser.Auth.UserID = newUser.ID
|
||||
if newUser.Auth.Type == auth.TypePassword {
|
||||
err = newUser.Auth.SetPassword(newUser.Auth.Secret)
|
||||
if err != nil {
|
||||
logger.Error("SetPasswordError", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = newUser.Auth.Save(); err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save Authentication for User", nil)
|
||||
return
|
||||
}
|
||||
|
||||
newUser.Auth.Secret = ""
|
||||
}
|
||||
|
||||
if !config.IsSetup {
|
||||
config.IsSetup = true
|
||||
logger.Info("A new user was created, leaving Setup Mode")
|
||||
}
|
||||
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, newUser)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteUsers is only available in debug mode for cypress tests
|
||||
// Route: DELETE /users
|
||||
func DeleteUsers() func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
err := user.DeleteAll()
|
||||
if err != nil {
|
||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||
} else {
|
||||
// also change setup to true
|
||||
config.IsSetup = false
|
||||
logger.Info("Users have been wiped, entering Setup Mode")
|
||||
h.ResultResponseJSON(w, r, http.StatusOK, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getUserIDFromRequest(r *http.Request) (int, bool, error) {
|
||||
userIDstr := chi.URLParam(r, "userID")
|
||||
|
||||
var userID int
|
||||
self := false
|
||||
if userIDstr == "me" {
|
||||
// Get user id from Token
|
||||
userID, _ = r.Context().Value(c.UserIDCtxKey).(int)
|
||||
self = true
|
||||
} else {
|
||||
var userIDerr error
|
||||
if userID, userIDerr = getURLParamInt(r, "userID"); userIDerr != nil {
|
||||
return 0, false, userIDerr
|
||||
}
|
||||
}
|
||||
return userID, self, nil
|
||||
}
|
Reference in New Issue
Block a user