197 lines
6.6 KiB
Go
197 lines
6.6 KiB
Go
|
package uploader
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"database/sql"
|
||
|
"embed"
|
||
|
"encoding/hex"
|
||
|
"encoding/json"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
|
||
|
"git.tijl.dev/tijl/tijl.dev-core/internal/config"
|
||
|
"git.tijl.dev/tijl/tijl.dev-core/internal/queries"
|
||
|
"git.tijl.dev/tijl/tijl.dev-core/internal/user"
|
||
|
"git.tijl.dev/tijl/tijl.dev-core/modules/db"
|
||
|
"git.tijl.dev/tijl/tijl.dev-core/modules/i18n"
|
||
|
"git.tijl.dev/tijl/tijl.dev-core/modules/web"
|
||
|
"github.com/gofiber/fiber/v2"
|
||
|
)
|
||
|
|
||
|
//go:embed locales/*
|
||
|
var Embed embed.FS
|
||
|
|
||
|
func Setup() {
|
||
|
i18n.RegisterTranslations(Embed, "locales")
|
||
|
|
||
|
var uploadDir string = filepath.Join(config.Config.DataLocation, "./uploader")
|
||
|
|
||
|
|
||
|
web.RegisterAppSetupFunc(func(a *fiber.App) {
|
||
|
a.Get("/app/uploader", func(c *fiber.Ctx) error {
|
||
|
data := *web.Common(c)
|
||
|
data["Title"] = "tmp"
|
||
|
return c.Render("apps/uploader/index", data, "layouts/base")
|
||
|
})
|
||
|
a.Get("/app/uploader/:key", func(c *fiber.Ctx) error {
|
||
|
encryptionKey, err := hex.DecodeString(c.Params("key"))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
storageKey := hashKey(encryptionKey)
|
||
|
|
||
|
row, err := db.Queries.AppUploaderGet(context.TODO(), storageKey)
|
||
|
if err != nil {
|
||
|
return c.Next()
|
||
|
}
|
||
|
|
||
|
if row.Expire.UnixMilli() < time.Now().UnixMilli() {
|
||
|
return c.Next()
|
||
|
}
|
||
|
|
||
|
accessCount, err := db.Queries.AppUploaderAccessCount(context.TODO(), row.ID)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if accessCount >= int64(row.MaxVisits) {
|
||
|
return c.Next()
|
||
|
}
|
||
|
|
||
|
createLog := queries.AppUploaderAccessCreateParams{
|
||
|
FileID: row.ID,
|
||
|
IpAddress: c.IP(),
|
||
|
Agent: string(c.Context().UserAgent()),
|
||
|
}
|
||
|
|
||
|
user, err := user.GetSession(c)
|
||
|
if err == nil {
|
||
|
createLog.Uid = sql.NullString{
|
||
|
Valid: true,
|
||
|
String: user,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
db.Queries.AppUploaderAccessCreate(context.TODO(), createLog)
|
||
|
|
||
|
encryptedContent, err := os.ReadFile(filepath.Join(uploadDir, row.ID.String()))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
encryptedMetadata, err := os.ReadFile(filepath.Join(uploadDir, row.ID.String()+".meta"))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
decryptedContent, err := decryptData(encryptedContent, encryptionKey)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
decryptedMetadata, err := decryptData(encryptedMetadata, encryptionKey)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
var metadata map[string]interface{}
|
||
|
if err := json.Unmarshal(decryptedMetadata, &metadata); err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to parse metadata")
|
||
|
}
|
||
|
|
||
|
c.Set("Content-Disposition", "attachment; filename="+metadata["filename"].(string))
|
||
|
c.Set("Content-Type", metadata["content_type"].(string))
|
||
|
|
||
|
return c.Send(decryptedContent)
|
||
|
})
|
||
|
|
||
|
|
||
|
a.Post("/app/uploader", func(c *fiber.Ctx) error {
|
||
|
uid, err := user.GetSession(c)
|
||
|
if err != nil {
|
||
|
return c.SendStatus(http.StatusUnauthorized)
|
||
|
}
|
||
|
|
||
|
expireDays, err := strconv.Atoi(c.FormValue("expire_days"))
|
||
|
if err != nil {return err}
|
||
|
maxDownloads, err := strconv.Atoi(c.FormValue("max_downloads"))
|
||
|
if err != nil {return err}
|
||
|
|
||
|
file, err := c.FormFile("file")
|
||
|
if err != nil {
|
||
|
return c.Status(fiber.StatusBadRequest).SendString("Failed to read file")
|
||
|
}
|
||
|
|
||
|
src, err := file.Open()
|
||
|
if err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to open file")
|
||
|
}
|
||
|
defer src.Close()
|
||
|
|
||
|
// Read file content into a byte slice
|
||
|
fileContent := make([]byte, file.Size)
|
||
|
if _, err := src.Read(fileContent); err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to read file content")
|
||
|
}
|
||
|
|
||
|
// Generate encryption key
|
||
|
encryptionKey, err := generateEncryptionKey()
|
||
|
if err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to generate encryption key")
|
||
|
}
|
||
|
|
||
|
// Encrypt the file content
|
||
|
encryptedContent, err := encryptData(fileContent, encryptionKey)
|
||
|
if err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to encrypt file content")
|
||
|
}
|
||
|
|
||
|
storageKey := hashKey(encryptionKey)
|
||
|
|
||
|
id, err := db.Queries.AppUploaderCreate(context.TODO(), queries.AppUploaderCreateParams{
|
||
|
Uid: uid,
|
||
|
FileCrypto: storageKey,
|
||
|
Expire: time.Now().AddDate(0, 0, expireDays),
|
||
|
MaxVisits: int32(maxDownloads),
|
||
|
})
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Save encrypted file content
|
||
|
if err := os.WriteFile(filepath.Join(uploadDir, id.String()), encryptedContent, 0644); err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to save encrypted file")
|
||
|
}
|
||
|
|
||
|
metadata := map[string]interface{}{
|
||
|
"filename": file.Filename,
|
||
|
"content_type": file.Header.Get("Content-Type"),
|
||
|
}
|
||
|
|
||
|
metadataBytes, err := json.Marshal(metadata)
|
||
|
if err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to marshal metadata")
|
||
|
}
|
||
|
|
||
|
encryptedMetadata, err := encryptData(metadataBytes, encryptionKey)
|
||
|
if err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to encrypt metadata")
|
||
|
}
|
||
|
|
||
|
if err := os.WriteFile(filepath.Join(uploadDir, id.String()+".meta"), encryptedMetadata, 0644); err != nil {
|
||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to save encrypted metadata")
|
||
|
}
|
||
|
|
||
|
data := *web.Common(c)
|
||
|
data["Title"] = "tmp"
|
||
|
data["Key"] = hex.EncodeToString(encryptionKey)
|
||
|
return c.Render("apps/uploader/uploaded", data, "layouts/base")
|
||
|
})
|
||
|
|
||
|
|
||
|
}, 1000)
|
||
|
}
|