diff --git a/backend/internal/routes/media.go b/backend/internal/routes/media.go index 619c9ef..78c6ed1 100644 --- a/backend/internal/routes/media.go +++ b/backend/internal/routes/media.go @@ -36,4 +36,42 @@ func RegisterMediaRoutes(rg *gin.RouterGroup) { } c.JSON(http.StatusOK, gin.H{"media": media}) }) + media.GET("/getAllMediaInAlbum", func(c *gin.Context) { + accessToken := c.GetHeader("Authorization") + albumID := c.Query("albumId") + media, err := services.GetAllMediaInAlbum(albumID, accessToken) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"media": media}) + }) + media.GET("/:albumId/:mediaId", func(c *gin.Context) { + albumID := c.Param("albumId") + mediaID := c.Param("mediaId") + if albumID == "root" { + albumID = "" + } + accessToken := c.GetHeader("Authorization") + userID, err := services.ValidateAccessToken(accessToken) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + accessLevel, err := services.CheckUserAlbumAccess(userID, albumID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + if accessLevel < 0 { + c.JSON(http.StatusForbidden, gin.H{"error": "user does not have permission to view media in this album"}) + return + } + mediaData, err := services.GetMedia(albumID, mediaID) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.File(mediaData.Path) + }) } diff --git a/backend/internal/services/media.go b/backend/internal/services/media.go index 9fbd764..ff0859f 100644 --- a/backend/internal/services/media.go +++ b/backend/internal/services/media.go @@ -38,3 +38,32 @@ func UploadMedia(file *multipart.FileHeader, albumID string, accessToken string) } return media, nil } + +func GetAllMediaInAlbum(albumID string, accessToken string) ([]models.Media, error) { + userID, err := ValidateAccessToken(accessToken) + if err != nil { + return []models.Media{}, err + } + accessLevel, err := CheckUserAlbumAccess(userID, albumID) + if err != nil { + return []models.Media{}, err + } + if accessLevel < 0 { + return []models.Media{}, fmt.Errorf("user does not have permission to view media in this album") + } + media := []models.Media{} + result := db.GetDB().Where("album_id = ?", albumID).Find(&media) + if result.Error != nil { + return []models.Media{}, result.Error + } + return media, nil +} + +func GetMedia(albumID string, mediaID string) (models.Media, error) { + media := models.Media{} + result := db.GetDB().First(&media, "album_id = ? AND id = ?", albumID, mediaID) + if result.Error != nil { + return models.Media{}, result.Error + } + return media, nil +} diff --git a/frontend/src/components/AuthImage.jsx b/frontend/src/components/AuthImage.jsx new file mode 100644 index 0000000..211a727 --- /dev/null +++ b/frontend/src/components/AuthImage.jsx @@ -0,0 +1,65 @@ +import { useState, useEffect } from 'react' + +export default function AuthImage({ src, token, alt, className, ...props }) { + const [imageSrc, setImageSrc] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(false) + + useEffect(() => { + let objectUrl = null + let active = true + + const fetchImage = async () => { + setLoading(true) + setError(false) + try { + const response = await fetch(src, { + headers: { + 'Authorization': token + } + }) + + if (!response.ok) { + throw new Error('Failed to load image') + } + + const blob = await response.blob() + if (active) { + objectUrl = URL.createObjectURL(blob) + setImageSrc(objectUrl) + setLoading(false) + } + } catch (err) { + if (active) { + console.error("Error loading image:", err) + setError(true) + setLoading(false) + } + } + } + + if (src && token) { + fetchImage() + } else { + setLoading(false) + } + + return () => { + active = false + if (objectUrl) { + URL.revokeObjectURL(objectUrl) + } + } + }, [src, token]) + + if (loading) { + return
+ } + + if (error || !imageSrc) { + returnNo media in this album
)} - {/* Render media items here */} + {media.map((media) => ( +