More
This commit is contained in:
@@ -13,11 +13,6 @@ import (
|
||||
"reichard.io/imagini/internal/models"
|
||||
)
|
||||
|
||||
// https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/
|
||||
// https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html
|
||||
// https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1#333c
|
||||
// https://www.alexedwards.net/blog/organising-database-access <---- best
|
||||
// - TLDR: Do what you're doing, but use closeures for the handlers
|
||||
func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed)
|
||||
@@ -38,34 +33,6 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Verify Device Name Exists
|
||||
deviceHeader := r.Header.Get("X-Imagini-DeviceName")
|
||||
if deviceHeader == "" {
|
||||
errorJSON(w, "Missing 'X-Imagini-DeviceName' header.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive Device Type
|
||||
var deviceType string
|
||||
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
|
||||
if strings.HasPrefix(userAgent, "ios-imagini"){
|
||||
deviceType = "iOS"
|
||||
} else if strings.HasPrefix(userAgent, "android-imagini"){
|
||||
deviceType = "Android"
|
||||
} else if strings.HasPrefix(userAgent, "chrome"){
|
||||
deviceType = "Chrome"
|
||||
} else if strings.HasPrefix(userAgent, "firefox"){
|
||||
deviceType = "Firefox"
|
||||
} else if strings.HasPrefix(userAgent, "msie"){
|
||||
deviceType = "Internet Explorer"
|
||||
} else if strings.HasPrefix(userAgent, "edge"){
|
||||
deviceType = "Edge"
|
||||
} else if strings.HasPrefix(userAgent, "safari"){
|
||||
deviceType = "Safari"
|
||||
}else {
|
||||
deviceType = "Unknown"
|
||||
}
|
||||
|
||||
// Do login
|
||||
resp, user := api.Auth.AuthenticateUser(creds)
|
||||
if !resp {
|
||||
@@ -73,8 +40,13 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create New Device
|
||||
device, err := api.DB.CreateDevice(models.Device{Name: deviceHeader, Type: deviceType})
|
||||
// Upsert device
|
||||
device, err := api.upsertRequestedDevice(user, r)
|
||||
if err != nil {
|
||||
log.Error("[api] loginHandler - Failed to upsert device: ", err)
|
||||
errorJSON(w, "DB error. Unable to proceed.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Create Tokens
|
||||
accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
|
||||
@@ -96,19 +68,13 @@ func (api *API) logoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Do logout
|
||||
// TODO: Reset Refresh Key
|
||||
|
||||
// TODO: Clear Session Server Side
|
||||
// Clear Cookies
|
||||
http.SetCookie(w, &http.Cookie{Name: "AccessToken", Expires: time.Unix(0, 0)})
|
||||
http.SetCookie(w, &http.Cookie{Name: "RefreshToken", Expires: time.Unix(0, 0)})
|
||||
|
||||
// Tell Client to Expire Token
|
||||
cookie := &http.Cookie{
|
||||
Name: "Token",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
Expires: time.Unix(0, 0),
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
successJSON(w, "Logout success.", http.StatusOK)
|
||||
}
|
||||
|
||||
func (api *API) refreshLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -162,3 +128,96 @@ func (api *API) refreshLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Response success
|
||||
successJSON(w, "Refresh success.", http.StatusOK)
|
||||
}
|
||||
|
||||
/**
|
||||
* This will find or create the requested device based on ID and User.
|
||||
**/
|
||||
func (api *API) upsertRequestedDevice(user models.User, r *http.Request) (models.Device, error) {
|
||||
requestedDevice := deriveRequestedDevice(r)
|
||||
requestedDevice.Type = deriveDeviceType(r)
|
||||
requestedDevice.User = user
|
||||
|
||||
if requestedDevice.UUID == uuid.Nil {
|
||||
createdDevice, err := api.DB.CreateDevice(requestedDevice)
|
||||
return createdDevice, err
|
||||
}
|
||||
|
||||
foundDevice, err := api.DB.Device(models.Device{
|
||||
Base: models.Base{ UUID: requestedDevice.UUID },
|
||||
User: user,
|
||||
})
|
||||
|
||||
return foundDevice, err
|
||||
}
|
||||
|
||||
func deriveDeviceType(r *http.Request) string {
|
||||
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
|
||||
if strings.HasPrefix(userAgent, "ios-imagini"){
|
||||
return "iOS"
|
||||
} else if strings.HasPrefix(userAgent, "android-imagini"){
|
||||
return "Android"
|
||||
} else if strings.HasPrefix(userAgent, "chrome"){
|
||||
return "Chrome"
|
||||
} else if strings.HasPrefix(userAgent, "firefox"){
|
||||
return "Firefox"
|
||||
} else if strings.HasPrefix(userAgent, "msie"){
|
||||
return "Internet Explorer"
|
||||
} else if strings.HasPrefix(userAgent, "edge"){
|
||||
return "Edge"
|
||||
} else if strings.HasPrefix(userAgent, "safari"){
|
||||
return "Safari"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
func deriveRequestedDevice(r *http.Request) models.Device {
|
||||
deviceSkeleton := models.Device{}
|
||||
authHeader := r.Header.Get("X-Imagini-Authorization")
|
||||
splitAuthInfo := strings.Split(authHeader, ",")
|
||||
|
||||
// For each Key - Value pair
|
||||
for i := range splitAuthInfo {
|
||||
|
||||
// Split Key - Value
|
||||
item := strings.TrimSpace(splitAuthInfo[i])
|
||||
splitItem := strings.SplitN(item, "=", 2)
|
||||
if len(splitItem) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Derive Key
|
||||
key := strings.ToLower(strings.TrimSpace(splitItem[0]))
|
||||
if key != "deviceuuid" && key != "devicename" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Derive Value
|
||||
val := trimQuotes(strings.ToLower(strings.TrimSpace(splitItem[1])))
|
||||
if key == "deviceuuid" {
|
||||
parsedDeviceUUID, err := uuid.Parse(val)
|
||||
if err != nil {
|
||||
log.Warn("[auth] deriveRequestedDevice - Unable to parse requested DeviceUUID: ", val)
|
||||
continue
|
||||
}
|
||||
deviceSkeleton.Base = models.Base{UUID: parsedDeviceUUID}
|
||||
} else if key == "devicename" {
|
||||
deviceSkeleton.Name = val
|
||||
}
|
||||
}
|
||||
|
||||
// If name not set, set to type
|
||||
if deviceSkeleton.Name == "" {
|
||||
deviceSkeleton.Name = deviceSkeleton.Type
|
||||
}
|
||||
|
||||
return deviceSkeleton
|
||||
}
|
||||
|
||||
func trimQuotes(s string) string {
|
||||
if len(s) >= 2 {
|
||||
if s[0] == '"' && s[len(s)-1] == '"' {
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
9
internal/api/devices.go
Normal file
9
internal/api/devices.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (api *API) devicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
@@ -6,40 +6,46 @@ import (
|
||||
)
|
||||
|
||||
func (api *API) registerRoutes() {
|
||||
api.Router.HandleFunc("/MediaItems", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/MediaItems", multipleMiddleware(
|
||||
api.mediaItemsHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/Upload", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/Devices", multipleMiddleware(
|
||||
api.devicesHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/api/v1/Upload", multipleMiddleware(
|
||||
api.uploadHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/Albums", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/Albums", multipleMiddleware(
|
||||
api.albumsHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/Users", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/Users", multipleMiddleware(
|
||||
api.usersHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/Tags", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/Tags", multipleMiddleware(
|
||||
api.tagsHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/Info", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/Info", multipleMiddleware(
|
||||
api.infoHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
api.Router.HandleFunc("/Me", multipleMiddleware(
|
||||
api.Router.HandleFunc("/api/v1/Me", multipleMiddleware(
|
||||
api.meHandler,
|
||||
api.authMiddleware,
|
||||
))
|
||||
|
||||
api.Router.HandleFunc("/Logout", api.logoutHandler)
|
||||
api.Router.HandleFunc("/Login", api.loginHandler)
|
||||
api.Router.HandleFunc("/RefreshLogin", api.refreshLoginHandler)
|
||||
api.Router.HandleFunc("/api/v1/Logout", api.logoutHandler)
|
||||
api.Router.HandleFunc("/api/v1/Login", api.loginHandler)
|
||||
api.Router.HandleFunc("/api/v1/RefreshLogin", api.refreshLoginHandler)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// https://stackoverflow.com/a/59764037
|
||||
func errorJSON(w http.ResponseWriter, err string, code int) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
@@ -2,7 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
log "github.com/sirupsen/logrus"
|
||||
// log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (api *API) usersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user