mirror of
https://github.com/wisplite/raster.git
synced 2026-05-01 06:32:44 -05:00
add file upload logic (it's kinda broken rn)
This commit is contained in:
+2
-1
@@ -1 +1,2 @@
|
|||||||
backend/raster.db
|
backend/raster.db
|
||||||
|
backend/media
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user