small updates, for some reason it thinks every file is modified

This commit is contained in:
2026-02-19 04:21:31 -06:00
parent 1b394a57a9
commit e829f6b405
32 changed files with 6142 additions and 6115 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ func main() {
// Configure CORS middleware
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000", "http://localhost:5173"},
AllowOrigins: []string{"http://localhost:3000", "http://localhost:5173", "http://192.168.1.130:5173"},
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
+20 -20
View File
@@ -1,20 +1,20 @@
package models
import (
"time"
"gorm.io/datatypes"
)
type Media struct {
ID string `gorm:"primaryKey"`
Title string `gorm:"not null"`
Description string `gorm:"not null"`
Tags datatypes.JSON `gorm:"type:json"`
AlbumID string `gorm:"not null"`
Path string `gorm:"not null"`
Type string `gorm:"not null"` // MIME type
Metadata datatypes.JSON `gorm:"type:json"` // Metadata about the media.
CreatedAt time.Time
UpdatedAt time.Time
}
package models
import (
"time"
"gorm.io/datatypes"
)
type Media struct {
ID string `gorm:"primaryKey"`
Title string `gorm:"not null"`
Description string `gorm:"not null"`
Tags datatypes.JSON `gorm:"type:json"`
AlbumID string `gorm:"not null"`
Path string `gorm:"not null"`
Type string `gorm:"not null"` // MIME type
Metadata datatypes.JSON `gorm:"type:json"` // Metadata about the media.
CreatedAt time.Time
UpdatedAt time.Time
}
+156 -156
View File
@@ -1,156 +1,156 @@
package routes
import (
"net/http"
"os"
"path/filepath"
"strconv"
"github.com/gin-gonic/gin"
"github.com/wisplite/raster/internal/services"
)
func RegisterMediaRoutes(rg *gin.RouterGroup) {
media := rg.Group("/media")
media.POST("/uploadMedia", func(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
accessToken := c.GetHeader("Authorization")
albumID := c.PostForm("albumId")
media, err := services.UploadMedia(file, albumID, accessToken)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := os.MkdirAll(filepath.Dir(media.Path), 0755); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create directory"})
return
}
if err := c.SaveUploadedFile(file, media.Path); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save file"})
return
}
c.JSON(http.StatusOK, gin.H{"media": media})
})
media.GET("/getAllMediaInAlbum", func(c *gin.Context) {
accessToken := c.GetHeader("Authorization")
albumID := c.Query("albumId")
isPublic, err := services.IsAlbumPublic(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if isPublic {
media, err := services.GetAllMediaInPublicAlbum(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"media": media})
return
} else {
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 = ""
}
isPublic, err := services.IsAlbumPublic(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if isPublic {
mediaData, err := services.GetMedia(albumID, mediaID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.File(mediaData.Path)
return
}
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)
})
media.GET("/thumb/:albumId/:mediaId", func(c *gin.Context) {
albumID := c.Param("albumId")
mediaID := c.Param("mediaId")
widthStr := c.Query("width")
heightStr := c.Query("height")
width, _ := strconv.Atoi(widthStr)
height, _ := strconv.Atoi(heightStr)
if albumID == "root" {
albumID = ""
}
isPublic, err := services.IsAlbumPublic(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
allowed := false
if isPublic {
allowed = true
} else {
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 {
allowed = true
} else {
c.JSON(http.StatusForbidden, gin.H{"error": "user does not have permission to view media in this album"})
return
}
}
if allowed {
thumbPath, err := services.GetThumbnail(albumID, mediaID, width, height)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.File(thumbPath)
}
})
}
package routes
import (
"net/http"
"os"
"path/filepath"
"strconv"
"github.com/gin-gonic/gin"
"github.com/wisplite/raster/internal/services"
)
func RegisterMediaRoutes(rg *gin.RouterGroup) {
media := rg.Group("/media")
media.POST("/uploadMedia", func(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
accessToken := c.GetHeader("Authorization")
albumID := c.PostForm("albumId")
media, err := services.UploadMedia(file, albumID, accessToken)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := os.MkdirAll(filepath.Dir(media.Path), 0755); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create directory"})
return
}
if err := c.SaveUploadedFile(file, media.Path); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save file"})
return
}
c.JSON(http.StatusOK, gin.H{"media": media})
})
media.GET("/getAllMediaInAlbum", func(c *gin.Context) {
accessToken := c.GetHeader("Authorization")
albumID := c.Query("albumId")
isPublic, err := services.IsAlbumPublic(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if isPublic {
media, err := services.GetAllMediaInPublicAlbum(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"media": media})
return
} else {
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 = ""
}
isPublic, err := services.IsAlbumPublic(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if isPublic {
mediaData, err := services.GetMedia(albumID, mediaID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.File(mediaData.Path)
return
}
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)
})
media.GET("/thumb/:albumId/:mediaId", func(c *gin.Context) {
albumID := c.Param("albumId")
mediaID := c.Param("mediaId")
widthStr := c.Query("width")
heightStr := c.Query("height")
width, _ := strconv.Atoi(widthStr)
height, _ := strconv.Atoi(heightStr)
if albumID == "root" {
albumID = ""
}
isPublic, err := services.IsAlbumPublic(albumID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
allowed := false
if isPublic {
allowed = true
} else {
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 {
allowed = true
} else {
c.JSON(http.StatusForbidden, gin.H{"error": "user does not have permission to view media in this album"})
return
}
}
if allowed {
thumbPath, err := services.GetThumbnail(albumID, mediaID, width, height)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.File(thumbPath)
}
})
}
+196 -196
View File
@@ -1,196 +1,196 @@
package services
import (
"encoding/json"
"fmt"
"image"
_ "image/jpeg"
_ "image/png"
"mime/multipart"
"os"
"path/filepath"
"github.com/disintegration/imaging"
"github.com/google/uuid"
"github.com/rwcarlsen/goexif/exif"
"github.com/wisplite/raster/internal/db"
"github.com/wisplite/raster/internal/models"
"gorm.io/datatypes"
)
func UploadMedia(file *multipart.FileHeader, 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 < 1 {
return models.Media{}, fmt.Errorf("user does not have permission to upload media to this album")
}
albumPath := albumID
if albumID == "" {
albumPath = "root"
}
// Extract metadata (dimensions and file info)
var meta datatypes.JSON
metadataMap := map[string]interface{}{
"originalFilename": file.Filename,
"fileSize": file.Size,
"contentType": file.Header.Get("Content-Type"),
}
src, err := file.Open()
if err == nil {
defer src.Close()
cfg, format, err := image.DecodeConfig(src)
if err == nil {
metadataMap["width"] = cfg.Width
metadataMap["height"] = cfg.Height
metadataMap["format"] = format
// Check for EXIF orientation
src.Seek(0, 0)
x, err := exif.Decode(src)
if err == nil {
orient, err := x.Get(exif.Orientation)
if err == nil {
val, err := orient.Int(0)
if err == nil {
if val >= 5 && val <= 8 {
metadataMap["width"] = cfg.Height
metadataMap["height"] = cfg.Width
}
}
}
}
}
}
b, _ := json.Marshal(metadataMap)
meta = datatypes.JSON(b)
mediaID := uuid.New().String()
mediaPath := fmt.Sprintf("media/%s/%s.%s", albumPath, mediaID, file.Filename)
media := models.Media{
ID: mediaID,
AlbumID: albumID,
Path: mediaPath,
Type: file.Header.Get("Content-Type"),
Title: file.Filename,
Description: "",
Metadata: meta,
}
result := db.GetDB().Create(&media)
if result.Error != nil {
return models.Media{}, result.Error
}
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 GetAllMediaInPublicAlbum(albumID string) ([]models.Media, error) {
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
}
func GetThumbnail(albumID string, mediaID string, width int, height int) (string, error) {
media, err := GetMedia(albumID, mediaID)
if err != nil {
return "", err
}
if width == 0 {
width = 200
}
if height == 0 {
height = 200
}
cacheDir := "cache/thumbnails"
if err := os.MkdirAll(cacheDir, 0755); err != nil {
return "", err
}
ext := filepath.Ext(media.Path)
if ext == "" {
ext = ".jpg"
}
thumbName := fmt.Sprintf("%s_%dx%d%s", mediaID, width, height, ext)
thumbPath := filepath.Join(cacheDir, thumbName)
if _, err := os.Stat(thumbPath); err == nil {
return thumbPath, nil
}
srcImage, err := imaging.Open(media.Path)
if err != nil {
return "", err
}
f, err := os.Open(media.Path)
if err == nil {
x, err := exif.Decode(f)
f.Close()
if err == nil {
orient, err := x.Get(exif.Orientation)
if err == nil {
val, err := orient.Int(0)
if err == nil {
switch val {
case 3:
srcImage = imaging.Rotate180(srcImage)
case 6:
srcImage = imaging.Rotate270(srcImage)
case 8:
srcImage = imaging.Rotate90(srcImage)
}
}
}
}
}
dstImage := imaging.Fit(srcImage, width, height, imaging.Lanczos)
err = imaging.Save(dstImage, thumbPath)
if err != nil {
return "", err
}
return thumbPath, nil
}
package services
import (
"encoding/json"
"fmt"
"image"
_ "image/jpeg"
_ "image/png"
"mime/multipart"
"os"
"path/filepath"
"github.com/disintegration/imaging"
"github.com/google/uuid"
"github.com/rwcarlsen/goexif/exif"
"github.com/wisplite/raster/internal/db"
"github.com/wisplite/raster/internal/models"
"gorm.io/datatypes"
)
func UploadMedia(file *multipart.FileHeader, 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 < 1 {
return models.Media{}, fmt.Errorf("user does not have permission to upload media to this album")
}
albumPath := albumID
if albumID == "" {
albumPath = "root"
}
// Extract metadata (dimensions and file info)
var meta datatypes.JSON
metadataMap := map[string]interface{}{
"originalFilename": file.Filename,
"fileSize": file.Size,
"contentType": file.Header.Get("Content-Type"),
}
src, err := file.Open()
if err == nil {
defer src.Close()
cfg, format, err := image.DecodeConfig(src)
if err == nil {
metadataMap["width"] = cfg.Width
metadataMap["height"] = cfg.Height
metadataMap["format"] = format
// Check for EXIF orientation
src.Seek(0, 0)
x, err := exif.Decode(src)
if err == nil {
orient, err := x.Get(exif.Orientation)
if err == nil {
val, err := orient.Int(0)
if err == nil {
if val >= 5 && val <= 8 {
metadataMap["width"] = cfg.Height
metadataMap["height"] = cfg.Width
}
}
}
}
}
}
b, _ := json.Marshal(metadataMap)
meta = datatypes.JSON(b)
mediaID := uuid.New().String()
mediaPath := fmt.Sprintf("media/%s/%s.%s", albumPath, mediaID, file.Filename)
media := models.Media{
ID: mediaID,
AlbumID: albumID,
Path: mediaPath,
Type: file.Header.Get("Content-Type"),
Title: file.Filename,
Description: "",
Metadata: meta,
}
result := db.GetDB().Create(&media)
if result.Error != nil {
return models.Media{}, result.Error
}
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 GetAllMediaInPublicAlbum(albumID string) ([]models.Media, error) {
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
}
func GetThumbnail(albumID string, mediaID string, width int, height int) (string, error) {
media, err := GetMedia(albumID, mediaID)
if err != nil {
return "", err
}
if width == 0 {
width = 200
}
if height == 0 {
height = 200
}
cacheDir := "cache/thumbnails"
if err := os.MkdirAll(cacheDir, 0755); err != nil {
return "", err
}
ext := filepath.Ext(media.Path)
if ext == "" {
ext = ".jpg"
}
thumbName := fmt.Sprintf("%s_%dx%d%s", mediaID, width, height, ext)
thumbPath := filepath.Join(cacheDir, thumbName)
if _, err := os.Stat(thumbPath); err == nil {
return thumbPath, nil
}
srcImage, err := imaging.Open(media.Path)
if err != nil {
return "", err
}
f, err := os.Open(media.Path)
if err == nil {
x, err := exif.Decode(f)
f.Close()
if err == nil {
orient, err := x.Get(exif.Orientation)
if err == nil {
val, err := orient.Int(0)
if err == nil {
switch val {
case 3:
srcImage = imaging.Rotate180(srcImage)
case 6:
srcImage = imaging.Rotate270(srcImage)
case 8:
srcImage = imaging.Rotate90(srcImage)
}
}
}
}
}
dstImage := imaging.Fit(srcImage, width, height, imaging.Lanczos)
err = imaging.Save(dstImage, thumbPath)
if err != nil {
return "", err
}
return thumbPath, nil
}