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) }