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"
 | 
			
		||||
    "context"
 | 
			
		||||
    "net/http"
 | 
			
		||||
    log "github.com/sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
    "reichard.io/imagini/internal/db"
 | 
			
		||||
    "reichard.io/imagini/internal/api"
 | 
			
		||||
    "reichard.io/imagini/internal/auth"
 | 
			
		||||
    "reichard.io/imagini/internal/config"
 | 
			
		||||
    log "github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Server struct {
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -62,26 +62,11 @@ func (auth *AuthManager) AuthenticateUser(creds models.APICredentials) (bool, mo
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func (auth *AuthManager) getRole(user models.User) string {
 | 
			
		||||
    // TODO: Lookup role of 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) {
 | 
			
		||||
    byteRefreshJWT := []byte(refreshJWT)
 | 
			
		||||
 | 
			
		||||
@ -100,8 +85,11 @@ func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token,
 | 
			
		||||
        return nil, false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Verify Token
 | 
			
		||||
    verifiedToken, err := jwt.ParseBytes(byteRefreshJWT, jwt.WithVerify(jwa.HS256, []byte(device.RefreshKey)))
 | 
			
		||||
    // Verify & Validate Token
 | 
			
		||||
    verifiedToken, err := jwt.ParseBytes(byteRefreshJWT,
 | 
			
		||||
        jwt.WithValidate(true),
 | 
			
		||||
        jwt.WithVerify(jwa.HS256, []byte(device.RefreshKey)),
 | 
			
		||||
    )
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        fmt.Println("failed to parse payload: ", err)
 | 
			
		||||
        return nil, false
 | 
			
		||||
@ -109,11 +97,17 @@ func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token,
 | 
			
		||||
    return verifiedToken, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *AuthManager) UpdateRefreshToken(deviceID string) error {
 | 
			
		||||
    // TODO:
 | 
			
		||||
    //  - Remove Refresh token from Session AND DB
 | 
			
		||||
    //  - Call CreateRefreshToken
 | 
			
		||||
    return nil
 | 
			
		||||
func (auth *AuthManager) ValidateJWTAccessToken(accessJWT string) (jwt.Token, bool) {
 | 
			
		||||
    byteAccessJWT := []byte(accessJWT)
 | 
			
		||||
    verifiedToken, err := jwt.ParseBytes(byteAccessJWT,
 | 
			
		||||
        jwt.WithValidate(true),
 | 
			
		||||
        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) {
 | 
			
		||||
@ -123,13 +117,15 @@ func (auth *AuthManager) CreateJWTRefreshToken(user models.User, device models.D
 | 
			
		||||
    // Create New Token
 | 
			
		||||
    tm := time.Now()
 | 
			
		||||
    t := jwt.New()
 | 
			
		||||
    t.Set(`did`, device.UUID)                           // Device ID
 | 
			
		||||
    t.Set(jwt.SubjectKey, user.UUID)                    // User ID
 | 
			
		||||
    t.Set(`did`, device.UUID.String())          // Device ID
 | 
			
		||||
    t.Set(jwt.SubjectKey, user.UUID.String())   // User ID
 | 
			
		||||
    t.Set(jwt.AudienceKey, `imagini`)           // App ID
 | 
			
		||||
    t.Set(jwt.IssuedAtKey, tm)                  // Issued At
 | 
			
		||||
 | 
			
		||||
    // TODO: Depends on Device
 | 
			
		||||
    // iOS & Android = Never Expiring Refresh Token
 | 
			
		||||
    if device.Type != "iOS" && device.Type != "Android" {
 | 
			
		||||
        t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 24))    // 1 Day Access Key
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Validate Token Creation
 | 
			
		||||
    _, 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) {
 | 
			
		||||
    // Acquire Role
 | 
			
		||||
    role := auth.getRole(user)
 | 
			
		||||
 | 
			
		||||
    // Create New Token
 | 
			
		||||
    tm := time.Now()
 | 
			
		||||
    t := jwt.New()
 | 
			
		||||
    t.Set(`did`, device.UUID)                           // Device ID
 | 
			
		||||
    t.Set(`role`, role)                                 // User Role (Admin / User)
 | 
			
		||||
    t.Set(jwt.SubjectKey, user.UUID)                    // User ID
 | 
			
		||||
    t.Set(`did`, device.UUID.String())                  // Device ID
 | 
			
		||||
    t.Set(`role`, auth.getRole(user))                   // User Role (Admin / User)
 | 
			
		||||
    t.Set(jwt.SubjectKey, user.UUID.String())           // User ID
 | 
			
		||||
    t.Set(jwt.AudienceKey, `imagini`)                   // App ID
 | 
			
		||||
    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
 | 
			
		||||
    _, 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.")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (dbm *DBManager) CreateDevice(device models.Device) (models.Device, error) {
 | 
			
		||||
    log.Info("[query] Creating device: ", device.Name)
 | 
			
		||||
func (dbm *DBManager) CreateDevice (device models.Device) (models.Device, error) {
 | 
			
		||||
    log.Info("[db] Creating device: ", device.Name)
 | 
			
		||||
    device.RefreshKey = uuid.New().String()
 | 
			
		||||
    err := dbm.db.Create(&device).Error
 | 
			
		||||
    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) {
 | 
			
		||||
    log.Info("[query] Creating user: ", user.Username)
 | 
			
		||||
    log.Info("[db] Creating user: ", user.Username)
 | 
			
		||||
    hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
        log.Error(err)
 | 
			
		||||
 | 
			
		||||
@ -28,9 +28,9 @@ type ServerSetting struct {
 | 
			
		||||
 | 
			
		||||
type Device struct {
 | 
			
		||||
    Base
 | 
			
		||||
    User         User    `json:"user" gorm:"ForeignKey:UUID"`
 | 
			
		||||
    Name         string  `json:"name"`
 | 
			
		||||
    Type         string  `json:"type"`   // Android, iOS, Chrome, FireFox, Edge, etc
 | 
			
		||||
    User         User    `json:"user" gorm:"ForeignKey:UUID;not null"`
 | 
			
		||||
    Name         string  `json:"name" gorm:"not null"`
 | 
			
		||||
    Type         string  `json:"type" gorm:"not null"`   // Android, iOS, Chrome, FireFox, Edge, etc
 | 
			
		||||
    RefreshKey   string  `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,7 @@ type User struct {
 | 
			
		||||
    Username        string  `json:"username" gorm:"unique"`
 | 
			
		||||
    FirstName       string  `json:"first_name"`
 | 
			
		||||
    LastName        string  `json:"last_name"`
 | 
			
		||||
    Role            string  `json:"role"`
 | 
			
		||||
    AuthType        string  `json:"auth_type" gorm:"default:Local;not null"`
 | 
			
		||||
    Password        string  `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user