Started to work on media upload and organization
This commit is contained in:
8
backend/api/core/paginated.go
Normal file
8
backend/api/core/paginated.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package core
|
||||
|
||||
type Paginated[T any] struct {
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
TotalPages int `json:"totalPages"`
|
||||
Items []T `json:"items"`
|
||||
}
|
||||
@@ -136,6 +136,17 @@ type WebsiteSettings struct {
|
||||
AutoAcceptDemand bool `bun:"auto_accept_demand,default:false" json:"autoAcceptDemand"`
|
||||
}
|
||||
|
||||
type Media struct {
|
||||
ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"`
|
||||
AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"`
|
||||
Author *User `bun:"rel:belongs-to,join:author_id=user_id" json:"author,omitempty"`
|
||||
Type string `bun:"media_type" json:"type"` // Image, Video, GIF etc. Add support for PDFs?
|
||||
Alt string `bun:"media_alt" json:"alt"`
|
||||
Path string `bun:"media_path" json:"path"`
|
||||
Size int64 `bun:"media_size" json:"size"`
|
||||
URL string `bun:"-" json:"url"`
|
||||
}
|
||||
|
||||
func InitDatabase(dsn DSN) (*bun.DB, error) {
|
||||
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn.ToString())))
|
||||
db := bun.NewDB(sqldb, pgdialect.New())
|
||||
@@ -152,6 +163,7 @@ func InitDatabase(dsn DSN) (*bun.DB, error) {
|
||||
_, err = db.NewCreateTable().Model((*EventToUser)(nil)).IfNotExists().Exec(ctx)
|
||||
_, err = db.NewCreateTable().Model((*Blog)(nil)).IfNotExists().Exec(ctx)
|
||||
_, err = db.NewCreateTable().Model((*WebsiteSettings)(nil)).IfNotExists().Exec(ctx)
|
||||
_, err = db.NewCreateTable().Model((*Media)(nil)).IfNotExists().Exec(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
41
backend/api/delete_media.go
Normal file
41
backend/api/delete_media.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"fr.latosa-escrima/api/core"
|
||||
)
|
||||
|
||||
func HandleDeleteMedia(w http.ResponseWriter, r *http.Request) {
|
||||
uuid := r.PathValue("media_uuid")
|
||||
var media core.Media
|
||||
res, err := core.DB.NewDelete().
|
||||
Model(&media).
|
||||
Where("id = ?", uuid).
|
||||
Returning("*").
|
||||
Exec(context.Background())
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Println(res)
|
||||
err = os.Remove(media.Path)
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
core.JSONSuccess{
|
||||
Status: core.Success,
|
||||
Message: "Image successfully deleted.",
|
||||
}.Respond(w, http.StatusOK)
|
||||
}
|
||||
117
backend/api/get_media.go
Normal file
117
backend/api/get_media.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"fr.latosa-escrima/api/core"
|
||||
"fr.latosa-escrima/utils"
|
||||
)
|
||||
|
||||
func HandleGetMedia(w http.ResponseWriter, r *http.Request) {
|
||||
queryParams := r.URL.Query()
|
||||
page, err := strconv.Atoi(queryParams.Get("page"))
|
||||
limit, err := strconv.Atoi(queryParams.Get("limit"))
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
offset := (page - 1) * limit
|
||||
|
||||
total, err := core.DB.NewSelect().
|
||||
Model((*core.Media)(nil)).
|
||||
Count(context.Background())
|
||||
|
||||
totalPages := int(math.Max(1, float64(total/limit)))
|
||||
|
||||
var media []core.Media
|
||||
err = core.DB.NewSelect().
|
||||
Model(&media).
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
Scan(context.Background())
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
baseURL := utils.GetURL(r)
|
||||
media = utils.Map(media, func(m core.Media) core.Media {
|
||||
m.Author = nil
|
||||
m.URL = fmt.Sprintf("%s%s/file", baseURL, m.ID)
|
||||
return m
|
||||
})
|
||||
|
||||
core.JSONSuccess{
|
||||
Status: core.Success,
|
||||
Message: "Media successfully retrieved",
|
||||
Data: core.Paginated[core.Media]{
|
||||
Page: page,
|
||||
Limit: limit,
|
||||
TotalPages: totalPages,
|
||||
Items: media,
|
||||
},
|
||||
}.Respond(w, http.StatusOK)
|
||||
}
|
||||
|
||||
func HandleGetMediaDetails(w http.ResponseWriter, r *http.Request) {
|
||||
uuid := r.PathValue("media_uuid")
|
||||
var media core.Media
|
||||
err := core.DB.NewSelect().
|
||||
Model(&media).
|
||||
Where("id = ?", uuid).
|
||||
Limit(1).
|
||||
Scan(context.Background())
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
baseURL := utils.GetURL(r)
|
||||
media.URL = fmt.Sprintf("%s/file", baseURL)
|
||||
|
||||
media.Author = nil
|
||||
core.JSONSuccess{
|
||||
Status: core.Success,
|
||||
Message: "Media retrieved",
|
||||
Data: media,
|
||||
}.Respond(w, http.StatusOK)
|
||||
}
|
||||
|
||||
func HandleGetMediaFile(w http.ResponseWriter, r *http.Request) {
|
||||
uuid := r.PathValue("media_uuid")
|
||||
var media core.Media
|
||||
err := core.DB.NewSelect().
|
||||
Model(&media).
|
||||
Where("id = ?", uuid).
|
||||
Limit(1).
|
||||
Scan(context.Background())
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, media.Path)
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
|
||||
"fr.latosa-escrima/api/core"
|
||||
"fr.latosa-escrima/utils"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func HandleUploadMedia(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -66,6 +69,49 @@ func HandleUploadMedia(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
token, ok := r.Context().Value("token").(*jwt.Token)
|
||||
if !ok {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: "Couldn't retrieve your JWT.",
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: "Invalid token claims.",
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := uuid.Parse(claims["user_id"].(string))
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
media := &core.Media{
|
||||
AuthorID: id,
|
||||
Type: fileHeader.Header.Get("Content-Type"),
|
||||
Alt: "To be implemented",
|
||||
Path: p,
|
||||
Size: fileHeader.Size,
|
||||
}
|
||||
|
||||
_, err = core.DB.NewInsert().Model(media).Exec(context.Background())
|
||||
if err != nil {
|
||||
core.JSONError{
|
||||
Status: core.Error,
|
||||
Message: err.Error(),
|
||||
}.Respond(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
core.JSONSuccess{
|
||||
Status: core.Success,
|
||||
Message: "File uploaded successfully.",
|
||||
|
||||
@@ -120,6 +120,29 @@ func main() {
|
||||
Handler: api.HandleVerifyMedia,
|
||||
Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT},
|
||||
},
|
||||
// Paginated media response
|
||||
"/media/": {
|
||||
Handler: api.HandleGetMedia,
|
||||
Middlewares: []core.Middleware{api.Methods("GET")},
|
||||
},
|
||||
// Unique element
|
||||
"/media/{media_uuid}": {
|
||||
Handler: api.HandleGetMediaDetails,
|
||||
Middlewares: []core.Middleware{api.Methods("GET")},
|
||||
},
|
||||
// Get the image, video, GIF etc.
|
||||
"/media/{media_uuid}/file": {
|
||||
Handler: api.HandleGetMediaFile,
|
||||
Middlewares: []core.Middleware{api.Methods("GET")},
|
||||
},
|
||||
// "/media/{media_uuid}/update": {
|
||||
// Handler: api.HandleGetMediaFile,
|
||||
// Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT},
|
||||
// },
|
||||
"/media/{media_uuid}/delete": {
|
||||
Handler: api.HandleDeleteMedia,
|
||||
Middlewares: []core.Middleware{api.Methods("DELETE"), api.AuthJWT},
|
||||
},
|
||||
"/contact": {
|
||||
Handler: api.HandleContact,
|
||||
Middlewares: []core.Middleware{api.Methods("POST"), CSRFMiddleware},
|
||||
|
||||
23
backend/utils/get_url.go
Normal file
23
backend/utils/get_url.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetURL(r *http.Request) string {
|
||||
scheme := "http"
|
||||
if r.TLS != nil { // Check if the request is over HTTPS
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
// Extract the host
|
||||
host := r.Host
|
||||
|
||||
// Get the full request URI (path + query string)
|
||||
fullPath := r.URL.Path
|
||||
|
||||
// Build the full request URL
|
||||
return fmt.Sprintf("%s://%s%s", scheme, host, fullPath)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user