More
This commit is contained in:
parent
3fd6e7b957
commit
45d7adbd8b
BIN
cmd/imagini.db
BIN
cmd/imagini.db
Binary file not shown.
@ -4,11 +4,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"reichard.io/imagini/internal/db"
|
"reichard.io/imagini/internal/db"
|
||||||
"reichard.io/imagini/internal/api"
|
"reichard.io/imagini/internal/api"
|
||||||
"reichard.io/imagini/internal/auth"
|
"reichard.io/imagini/internal/auth"
|
||||||
"reichard.io/imagini/internal/config"
|
"reichard.io/imagini/internal/config"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
@ -13,11 +13,6 @@ import (
|
|||||||
"reichard.io/imagini/internal/models"
|
"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) {
|
func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed)
|
errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed)
|
||||||
@ -38,34 +33,6 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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
|
// Do login
|
||||||
resp, user := api.Auth.AuthenticateUser(creds)
|
resp, user := api.Auth.AuthenticateUser(creds)
|
||||||
if !resp {
|
if !resp {
|
||||||
@ -73,8 +40,13 @@ func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create New Device
|
// Upsert device
|
||||||
device, err := api.DB.CreateDevice(models.Device{Name: deviceHeader, Type: deviceType})
|
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
|
// Create Tokens
|
||||||
accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
|
accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
|
||||||
@ -96,19 +68,13 @@ func (api *API) logoutHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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
|
successJSON(w, "Logout success.", http.StatusOK)
|
||||||
cookie := &http.Cookie{
|
|
||||||
Name: "Token",
|
|
||||||
Value: "",
|
|
||||||
Path: "/",
|
|
||||||
Expires: time.Unix(0, 0),
|
|
||||||
HttpOnly: true,
|
|
||||||
}
|
|
||||||
http.SetCookie(w, cookie)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) refreshLoginHandler(w http.ResponseWriter, r *http.Request) {
|
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
|
// Response success
|
||||||
successJSON(w, "Refresh success.", http.StatusOK)
|
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() {
|
func (api *API) registerRoutes() {
|
||||||
api.Router.HandleFunc("/MediaItems", multipleMiddleware(
|
api.Router.HandleFunc("/api/v1/MediaItems", multipleMiddleware(
|
||||||
api.mediaItemsHandler,
|
api.mediaItemsHandler,
|
||||||
api.authMiddleware,
|
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.uploadHandler,
|
||||||
api.authMiddleware,
|
api.authMiddleware,
|
||||||
))
|
))
|
||||||
api.Router.HandleFunc("/Albums", multipleMiddleware(
|
api.Router.HandleFunc("/api/v1/Albums", multipleMiddleware(
|
||||||
api.albumsHandler,
|
api.albumsHandler,
|
||||||
api.authMiddleware,
|
api.authMiddleware,
|
||||||
))
|
))
|
||||||
api.Router.HandleFunc("/Users", multipleMiddleware(
|
api.Router.HandleFunc("/api/v1/Users", multipleMiddleware(
|
||||||
api.usersHandler,
|
api.usersHandler,
|
||||||
api.authMiddleware,
|
api.authMiddleware,
|
||||||
))
|
))
|
||||||
api.Router.HandleFunc("/Tags", multipleMiddleware(
|
api.Router.HandleFunc("/api/v1/Tags", multipleMiddleware(
|
||||||
api.tagsHandler,
|
api.tagsHandler,
|
||||||
api.authMiddleware,
|
api.authMiddleware,
|
||||||
))
|
))
|
||||||
api.Router.HandleFunc("/Info", multipleMiddleware(
|
api.Router.HandleFunc("/api/v1/Info", multipleMiddleware(
|
||||||
api.infoHandler,
|
api.infoHandler,
|
||||||
api.authMiddleware,
|
api.authMiddleware,
|
||||||
))
|
))
|
||||||
api.Router.HandleFunc("/Me", multipleMiddleware(
|
api.Router.HandleFunc("/api/v1/Me", multipleMiddleware(
|
||||||
api.meHandler,
|
api.meHandler,
|
||||||
api.authMiddleware,
|
api.authMiddleware,
|
||||||
))
|
))
|
||||||
|
|
||||||
api.Router.HandleFunc("/Logout", api.logoutHandler)
|
api.Router.HandleFunc("/api/v1/Logout", api.logoutHandler)
|
||||||
api.Router.HandleFunc("/Login", api.loginHandler)
|
api.Router.HandleFunc("/api/v1/Login", api.loginHandler)
|
||||||
api.Router.HandleFunc("/RefreshLogin", api.refreshLoginHandler)
|
api.Router.HandleFunc("/api/v1/RefreshLogin", api.refreshLoginHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/59764037
|
// https://stackoverflow.com/a/59764037
|
||||||
func errorJSON(w http.ResponseWriter, err string, code int) {
|
func errorJSON(w http.ResponseWriter, err string, code int) {
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
@ -2,7 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
log "github.com/sirupsen/logrus"
|
// log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) usersHandler(w http.ResponseWriter, r *http.Request) {
|
func (api *API) usersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -62,26 +62,11 @@ func (auth *AuthManager) AuthenticateUser(creds models.APICredentials) (bool, mo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (auth *AuthManager) getRole(user models.User) string {
|
func (auth *AuthManager) getRole(user models.User) string {
|
||||||
// TODO: Lookup role of user
|
// TODO: Lookup role of user
|
||||||
return "User"
|
return "User"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthManager) ValidateJWTAccessToken(accessJWT string) (jwt.Token, bool) {
|
|
||||||
byteAccessJWT := []byte(accessJWT)
|
|
||||||
verifiedToken, err := jwt.ParseBytes(byteAccessJWT, jwt.WithVerify(jwa.HS256, []byte(auth.Config.JWTSecret)))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("failed to parse payload: ", err)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return verifiedToken, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (auth *AuthManager) RevokeRefreshToken() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token, bool) {
|
func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token, bool) {
|
||||||
byteRefreshJWT := []byte(refreshJWT)
|
byteRefreshJWT := []byte(refreshJWT)
|
||||||
|
|
||||||
@ -100,8 +85,11 @@ func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token,
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Token
|
// Verify & Validate Token
|
||||||
verifiedToken, err := jwt.ParseBytes(byteRefreshJWT, jwt.WithVerify(jwa.HS256, []byte(device.RefreshKey)))
|
verifiedToken, err := jwt.ParseBytes(byteRefreshJWT,
|
||||||
|
jwt.WithValidate(true),
|
||||||
|
jwt.WithVerify(jwa.HS256, []byte(device.RefreshKey)),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("failed to parse payload: ", err)
|
fmt.Println("failed to parse payload: ", err)
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -109,11 +97,17 @@ func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token,
|
|||||||
return verifiedToken, true
|
return verifiedToken, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthManager) UpdateRefreshToken(deviceID string) error {
|
func (auth *AuthManager) ValidateJWTAccessToken(accessJWT string) (jwt.Token, bool) {
|
||||||
// TODO:
|
byteAccessJWT := []byte(accessJWT)
|
||||||
// - Remove Refresh token from Session AND DB
|
verifiedToken, err := jwt.ParseBytes(byteAccessJWT,
|
||||||
// - Call CreateRefreshToken
|
jwt.WithValidate(true),
|
||||||
return nil
|
jwt.WithVerify(jwa.HS256, []byte(auth.Config.JWTSecret)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to parse payload: ", err)
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return verifiedToken, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthManager) CreateJWTRefreshToken(user models.User, device models.Device) (string, error) {
|
func (auth *AuthManager) CreateJWTRefreshToken(user models.User, device models.Device) (string, error) {
|
||||||
@ -123,13 +117,15 @@ func (auth *AuthManager) CreateJWTRefreshToken(user models.User, device models.D
|
|||||||
// Create New Token
|
// Create New Token
|
||||||
tm := time.Now()
|
tm := time.Now()
|
||||||
t := jwt.New()
|
t := jwt.New()
|
||||||
t.Set(`did`, device.UUID) // Device ID
|
t.Set(`did`, device.UUID.String()) // Device ID
|
||||||
t.Set(jwt.SubjectKey, user.UUID) // User ID
|
t.Set(jwt.SubjectKey, user.UUID.String()) // User ID
|
||||||
t.Set(jwt.AudienceKey, `imagini`) // App ID
|
t.Set(jwt.AudienceKey, `imagini`) // App ID
|
||||||
t.Set(jwt.IssuedAtKey, tm) // Issued At
|
t.Set(jwt.IssuedAtKey, tm) // Issued At
|
||||||
|
|
||||||
// TODO: Depends on Device
|
// iOS & Android = Never Expiring Refresh Token
|
||||||
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 24)) // 1 Day Access Key
|
if device.Type != "iOS" && device.Type != "Android" {
|
||||||
|
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 24)) // 1 Day Access Key
|
||||||
|
}
|
||||||
|
|
||||||
// Validate Token Creation
|
// Validate Token Creation
|
||||||
_, err := json.MarshalIndent(t, "", " ")
|
_, err := json.MarshalIndent(t, "", " ")
|
||||||
@ -150,18 +146,15 @@ func (auth *AuthManager) CreateJWTRefreshToken(user models.User, device models.D
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthManager) CreateJWTAccessToken(user models.User, device models.Device) (string, error) {
|
func (auth *AuthManager) CreateJWTAccessToken(user models.User, device models.Device) (string, error) {
|
||||||
// Acquire Role
|
|
||||||
role := auth.getRole(user)
|
|
||||||
|
|
||||||
// Create New Token
|
// Create New Token
|
||||||
tm := time.Now()
|
tm := time.Now()
|
||||||
t := jwt.New()
|
t := jwt.New()
|
||||||
t.Set(`did`, device.UUID) // Device ID
|
t.Set(`did`, device.UUID.String()) // Device ID
|
||||||
t.Set(`role`, role) // User Role (Admin / User)
|
t.Set(`role`, auth.getRole(user)) // User Role (Admin / User)
|
||||||
t.Set(jwt.SubjectKey, user.UUID) // User ID
|
t.Set(jwt.SubjectKey, user.UUID.String()) // User ID
|
||||||
t.Set(jwt.AudienceKey, `imagini`) // App ID
|
t.Set(jwt.AudienceKey, `imagini`) // App ID
|
||||||
t.Set(jwt.IssuedAtKey, tm) // Issued At
|
t.Set(jwt.IssuedAtKey, tm) // Issued At
|
||||||
t.Set(jwt.ExpirationKey, tm.Add(time.Minute * 30)) // 30 Minute Access Key
|
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 2)) // 2 Hour Access Key
|
||||||
|
|
||||||
// Validate Token Creation
|
// Validate Token Creation
|
||||||
_, err := json.MarshalIndent(t, "", " ")
|
_, err := json.MarshalIndent(t, "", " ")
|
||||||
|
0
internal/db/albums.go
Normal file
0
internal/db/albums.go
Normal file
@ -62,25 +62,3 @@ func (dbm *DBManager) bootstrapDatabase() {
|
|||||||
log.Fatal("[query] Unable to bootstrap database.")
|
log.Fatal("[query] Unable to bootstrap database.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) ItemsFromAlbum(user models.User, album models.Album) []models.MediaItem {
|
|
||||||
var mediaItems []models.MediaItem
|
|
||||||
// db.Table("media_albums").
|
|
||||||
// Select("media_item.*").
|
|
||||||
// Joins("INNER JOIN media_items ON media_albums.ID = media_items.Albums").
|
|
||||||
// Where("media_albums.album_id = ? AND media_items.User = ?", albumID, userID).
|
|
||||||
|
|
||||||
|
|
||||||
dbm.db.
|
|
||||||
//Where("album = ? AND user = ?", albumID, userID).
|
|
||||||
Find(&mediaItems)
|
|
||||||
return mediaItems
|
|
||||||
|
|
||||||
// db.Raw(`
|
|
||||||
// SELECT
|
|
||||||
// MediaItems.*
|
|
||||||
// FROM
|
|
||||||
// MediaAlbums
|
|
||||||
// INNER JOIN MediaItems ON MediaAlbums.mediaID = MediaItems.mediaID
|
|
||||||
// WHERE MediaAlbums.albumID = ? AND MediaItems.userID = ?`, albumID, userID)
|
|
||||||
}
|
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
"reichard.io/imagini/internal/models"
|
"reichard.io/imagini/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dbm *DBManager) CreateDevice(device models.Device) (models.Device, error) {
|
func (dbm *DBManager) CreateDevice (device models.Device) (models.Device, error) {
|
||||||
log.Info("[query] Creating device: ", device.Name)
|
log.Info("[db] Creating device: ", device.Name)
|
||||||
device.RefreshKey = uuid.New().String()
|
device.RefreshKey = uuid.New().String()
|
||||||
err := dbm.db.Create(&device).Error
|
err := dbm.db.Create(&device).Error
|
||||||
return device, err
|
return device, err
|
||||||
|
35
internal/db/media_items.go
Normal file
35
internal/db/media_items.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"reichard.io/imagini/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (dbm *DBManager) CreateMediaItem (mediaItem models.MediaItem) (models.MediaItem, error) {
|
||||||
|
log.Info("[db] Creating media item: ", mediaItem.RelPath)
|
||||||
|
err := dbm.db.Create(&mediaItem).Error
|
||||||
|
return mediaItem, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbm *DBManager) MediaItemsFromAlbum(user models.User, album models.Album) ([]models.MediaItem, error) {
|
||||||
|
var mediaItems []models.MediaItem
|
||||||
|
// db.Table("media_albums").
|
||||||
|
// Select("media_item.*").
|
||||||
|
// Joins("INNER JOIN media_items ON media_albums.ID = media_items.Albums").
|
||||||
|
// Where("media_albums.album_id = ? AND media_items.User = ?", albumID, userID).
|
||||||
|
|
||||||
|
|
||||||
|
err := dbm.db.
|
||||||
|
//Where("album = ? AND user = ?", albumID, userID).
|
||||||
|
Find(&mediaItems).Error
|
||||||
|
return mediaItems, err
|
||||||
|
|
||||||
|
// db.Raw(`
|
||||||
|
// SELECT
|
||||||
|
// MediaItems.*
|
||||||
|
// FROM
|
||||||
|
// MediaAlbums
|
||||||
|
// INNER JOIN MediaItems ON MediaAlbums.mediaID = MediaItems.mediaID
|
||||||
|
// WHERE MediaAlbums.albumID = ? AND MediaItems.userID = ?`, albumID, userID)
|
||||||
|
}
|
0
internal/db/tags.go
Normal file
0
internal/db/tags.go
Normal file
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (dbm *DBManager) CreateUser(user models.User) (models.User, error) {
|
func (dbm *DBManager) CreateUser(user models.User) (models.User, error) {
|
||||||
log.Info("[query] Creating user: ", user.Username)
|
log.Info("[db] Creating user: ", user.Username)
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
@ -28,9 +28,9 @@ type ServerSetting struct {
|
|||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
Base
|
Base
|
||||||
User User `json:"user" gorm:"ForeignKey:UUID"`
|
User User `json:"user" gorm:"ForeignKey:UUID;not null"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name" gorm:"not null"`
|
||||||
Type string `json:"type"` // Android, iOS, Chrome, FireFox, Edge, etc
|
Type string `json:"type" gorm:"not null"` // Android, iOS, Chrome, FireFox, Edge, etc
|
||||||
RefreshKey string `json:"-"`
|
RefreshKey string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +40,7 @@ type User struct {
|
|||||||
Username string `json:"username" gorm:"unique"`
|
Username string `json:"username" gorm:"unique"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
|
Role string `json:"role"`
|
||||||
AuthType string `json:"auth_type" gorm:"default:Local;not null"`
|
AuthType string `json:"auth_type" gorm:"default:Local;not null"`
|
||||||
Password string `json:"-"`
|
Password string `json:"-"`
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user