263 lines
6.4 KiB
Go
263 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"time"
|
|
|
|
"github.com/georgysavva/scany/v2/pgxscan"
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// Upload saves a file to the storage directory.
|
|
func Upload(file *multipart.FileHeader, from_admin bool) (string, error) {
|
|
log.Printf("Uploading file: %s", file.Filename)
|
|
log.Printf("DB instance: %v", Db.Dbpool)
|
|
var newId string
|
|
err := Db.Dbpool.QueryRow(Db.Ctx, "INSERT INTO public.storageindex(filename)VALUES (null) RETURNING id;").Scan(&newId)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if newId == "" {
|
|
return "", errors.New("failed to create new id")
|
|
}
|
|
err = Storage.Add(file, newId)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
//if !from_admin , expires in 60 days
|
|
var expiresAt *time.Time
|
|
if !from_admin {
|
|
exp := time.Now().AddDate(0, 2, 0) // 60 days
|
|
expiresAt = &exp
|
|
} else {
|
|
expiresAt = nil
|
|
}
|
|
|
|
_, err = Db.Dbpool.Exec(Db.Ctx, "UPDATE public.storageindex set filename= $2, ext = $3, from_admin = $4, expires_at = $5 where id = $1", newId, file.Filename, filepath.Ext(file.Filename), from_admin, expiresAt)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
_, err = Db.Dbpool.Exec(Db.Ctx, "DELETE FROM public.storageindex WHERE filename IS NULL")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return newId, nil
|
|
|
|
}
|
|
|
|
func StorageAuth(c echo.Context) error {
|
|
token := c.Request().URL.Query().Get("token")
|
|
if len(token) < 1 {
|
|
return c.String(http.StatusUnauthorized, "Unauthorized")
|
|
}
|
|
var tk []string
|
|
err := pgxscan.Select(Db.Ctx, Db.Dbpool, &tk, "SELECT token FROM public.temp_tokens WHERE token = $1 and elapse > $2", token, time.Now())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tk == nil {
|
|
return errors.New("invalid token")
|
|
}
|
|
|
|
_, err = Db.Dbpool.Exec(Db.Ctx, "DELETE FROM public.temp_tokens WHERE elapse < $1", time.Now())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func RemoveFromDb(ref string) error {
|
|
|
|
_, err := Db.Dbpool.Exec(Db.Ctx, "DELETE FROM public.storageindex WHERE id = $1", ref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func RemoveExpired() error {
|
|
//get all expired files
|
|
var items_db []Storageindex
|
|
err := pgxscan.Select(Db.Ctx, Db.Dbpool, &items_db, "SELECT * FROM public.storageindex WHERE expires_at is not null and expires_at < NOW()")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get expired items: %w", err)
|
|
}
|
|
for _, item := range items_db {
|
|
formattedName := item.Id + item.Ext
|
|
err = Storage.Delete(formattedName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete file %s: %w", formattedName, err)
|
|
}
|
|
err = RemoveFromDb(item.Id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to remove item from db: %w", err)
|
|
}
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CheckConcruency() error {
|
|
|
|
var items_db []struct {
|
|
Id string
|
|
Ext string
|
|
}
|
|
|
|
err := pgxscan.Select(Db.Ctx, Db.Dbpool, &items_db, "SELECT id, ext FROM public.storageindex")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
files, err := Storage.List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(items_db) != len(files) {
|
|
var founds []string
|
|
for _, item := range items_db {
|
|
formattedName := item.Id + item.Ext
|
|
found := slices.Contains(files, formattedName)
|
|
if found {
|
|
founds = append(founds, formattedName)
|
|
} else {
|
|
|
|
_, err := Db.Dbpool.Exec(Db.Ctx, "DELETE FROM public.storageindex WHERE id = $1", item.Id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
}
|
|
for _, file := range files {
|
|
found := slices.Contains(founds, file)
|
|
if !found {
|
|
fmt.Printf("File %v not found in db\n", file)
|
|
//TODO decidere se eliminarlo
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TempFolderCleaner() error {
|
|
files, err := os.ReadDir(filepath.Join(storagepath, "/tmp/"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Temp files: ", len(files))
|
|
if len(files) >= tempStorageLimit {
|
|
|
|
var oldest_Time time.Time
|
|
var oldest_File string
|
|
for _, file := range files {
|
|
file_infos, err := file.Info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if oldest_Time.IsZero() || file_infos.ModTime().Before(oldest_Time) {
|
|
oldest_Time = file_infos.ModTime()
|
|
oldest_File = file.Name()
|
|
}
|
|
}
|
|
err = os.Remove(filepath.Join(storagepath, "/tmp/", oldest_File))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
// ROUTES
|
|
func StorageRoutes(e *echo.Echo) {
|
|
|
|
e.GET("/storage/list", func(c echo.Context) error {
|
|
filenames, err := Storage.List()
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, err.Error())
|
|
}
|
|
return c.JSON(http.StatusOK, filenames)
|
|
})
|
|
|
|
e.GET("/storage/get/:filename", func(c echo.Context) error {
|
|
err := TempFolderCleaner()
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to clean temp folder: "+err.Error())
|
|
}
|
|
err = StorageAuth(c)
|
|
if err != nil {
|
|
return c.JSON(http.StatusUnauthorized, "Unauthorized: "+err.Error())
|
|
}
|
|
|
|
filename := c.Param("filename")
|
|
path, err := Storage.Get(filename)
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to get file: "+err.Error())
|
|
}
|
|
|
|
return c.File(path)
|
|
})
|
|
e.POST("/storage/upload", func(c echo.Context) error {
|
|
fmt.Println("Uploading file")
|
|
|
|
err := StorageAuth(c)
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to verify Auth: "+err.Error())
|
|
}
|
|
|
|
from_admin := c.QueryParam("from_admin") == "true"
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fileId, err := Upload(file, from_admin)
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to upload file: "+err.Error())
|
|
}
|
|
|
|
return c.String(http.StatusOK, fileId)
|
|
})
|
|
e.DELETE("/storage/delete/:filename", func(c echo.Context) error {
|
|
err := StorageAuth(c)
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to verify Auth: "+err.Error())
|
|
}
|
|
filename := c.Param("filename")
|
|
|
|
err = Storage.Delete(filename)
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to remove file / file not found: "+err.Error())
|
|
}
|
|
return c.String(http.StatusOK, "File removed")
|
|
|
|
})
|
|
e.GET("/storage/remove-expired", func(c echo.Context) error {
|
|
|
|
err := RemoveExpired()
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to remove files / files not found: "+err.Error())
|
|
}
|
|
return c.String(http.StatusOK, "File removed")
|
|
|
|
})
|
|
e.GET("/storage/test", func(c echo.Context) error {
|
|
err := CheckConcruency()
|
|
if err != nil {
|
|
return c.String(http.StatusInternalServerError, "Failed to check concruency: "+err.Error())
|
|
}
|
|
return c.String(http.StatusOK, "OK")
|
|
})
|
|
}
|