add file upload logic (it's kinda broken rn)

This commit is contained in:
wisplite
2025-11-23 00:27:57 -06:00
parent f764e888d6
commit b61bd2f8d6
6 changed files with 102 additions and 20 deletions
+2 -1
View File
@@ -1 +1,2 @@
backend/raster.db backend/raster.db
backend/media
+1
View File
@@ -23,6 +23,7 @@ func Init() bool {
&models.User{}, &models.User{},
&models.AccessToken{}, &models.AccessToken{},
&models.UserAlbumAccess{}, &models.UserAlbumAccess{},
&models.Media{},
) )
if err != nil { if err != nil {
log.Fatal("failed to migrate database: ", err) log.Fatal("failed to migrate database: ", err)
+39
View File
@@ -0,0 +1,39 @@
package routes
import (
"net/http"
"os"
"path/filepath"
"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})
})
}
+1
View File
@@ -6,4 +6,5 @@ func RegisterRoutes(r *gin.Engine) {
rg := r.Group("/api") rg := r.Group("/api")
RegisterAlbumRoutes(rg) RegisterAlbumRoutes(rg)
RegisterUserRoutes(rg) RegisterUserRoutes(rg)
RegisterMediaRoutes(rg)
} }
+40
View File
@@ -0,0 +1,40 @@
package services
import (
"fmt"
"github.com/google/uuid"
"github.com/wisplite/raster/internal/db"
"github.com/wisplite/raster/internal/models"
"mime/multipart"
)
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"
}
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"),
}
result := db.GetDB().Create(&media)
if result.Error != nil {
return models.Media{}, result.Error
}
return media, nil
}
@@ -2,12 +2,13 @@ import Modal from '../../components/Modal'
import { useAccount } from '../../contexts/useAccount' import { useAccount } from '../../contexts/useAccount'
import { useState, useRef } from 'react' import { useState, useRef } from 'react'
import { X, Upload, FileImage, FileVideo, Trash2 } from 'lucide-react' import { X, Upload, FileImage, FileVideo, Trash2 } from 'lucide-react'
import { useNotifier } from '../../contexts/useNotifier'
import { getServerUrl } from '../../hooks/getConstants'
export default function MediaUploadModal({ open, onOpenChange, trigger, albumName, albumId }) { export default function MediaUploadModal({ open, onOpenChange, trigger, albumName, albumId }) {
const { getAccessToken } = useAccount() const { getAccessToken } = useAccount()
const [files, setFiles] = useState([]) const [files, setFiles] = useState([])
const fileInputRef = useRef(null) const fileInputRef = useRef(null)
const { showError, showSuccess } = useNotifier()
const handleFileSelect = (e) => { const handleFileSelect = (e) => {
if (e.target.files) { if (e.target.files) {
const newFiles = Array.from(e.target.files).map(file => ({ const newFiles = Array.from(e.target.files).map(file => ({
@@ -38,24 +39,23 @@ export default function MediaUploadModal({ open, onOpenChange, trigger, albumNam
// formData.append('albumId', albumId) // formData.append('albumId', albumId)
// await fetch('/api/upload', { method: 'POST', body: formData, ... }) // await fetch('/api/upload', { method: 'POST', body: formData, ... })
// Simulation: const formData = new FormData()
return new Promise((resolve) => { formData.append('file', fileWrapper.file)
let progress = 0 formData.append('albumId', albumId)
const interval = setInterval(() => { const response = await fetch(`${getServerUrl()}/api/media/uploadMedia`, {
progress += 5 method: 'POST',
setFiles(prev => prev.map(f => { body: formData,
if (f.id === fileWrapper.id) { headers: {
if (progress >= 100) { 'Authorization': getAccessToken(),
clearInterval(interval) },
resolve()
return { ...f, progress: 100, status: 'completed' }
}
return { ...f, progress, status: 'uploading' }
}
return f
}))
}, 100)
}) })
const data = await response.json()
if (data.error) {
showError(data.error)
} else {
setFiles(prev => prev.map(f => f.id === fileWrapper.id ? { ...f, status: 'completed' } : f))
showSuccess('Media uploaded successfully')
}
} }
// Start uploads // Start uploads