✨ Instant upload
This commit is contained in:
parent
93d959e7a6
commit
fb0c7860e0
@ -23,6 +23,7 @@ type Attachment struct {
|
|||||||
Filesize int64 `json:"filesize"`
|
Filesize int64 `json:"filesize"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Mimetype string `json:"mimetype"`
|
Mimetype string `json:"mimetype"`
|
||||||
|
Hashcode string `json:"hashcode"`
|
||||||
Type AttachmentType `json:"type"`
|
Type AttachmentType `json:"type"`
|
||||||
ExternalUrl string `json:"external_url"`
|
ExternalUrl string `json:"external_url"`
|
||||||
Author Account `json:"author"`
|
Author Account `json:"author"`
|
||||||
|
@ -18,12 +18,16 @@ func readAttachment(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
func uploadAttachment(c *fiber.Ctx) error {
|
func uploadAttachment(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
|
hashcode := c.FormValue("hashcode")
|
||||||
|
if len(hashcode) != 64 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "please provide a SHA256 hashcode, length should be 64 characters")
|
||||||
|
}
|
||||||
file, err := c.FormFile("attachment")
|
file, err := c.FormFile("attachment")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
attachment, err := services.NewAttachment(user, file)
|
attachment, err := services.NewAttachment(user, file, hashcode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
@ -39,10 +43,10 @@ func uploadAttachment(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func deleteAttachment(c *fiber.Ctx) error {
|
func deleteAttachment(c *fiber.Ctx) error {
|
||||||
id := c.Params("fileId")
|
id, _ := c.ParamsInt("id", 0)
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
|
|
||||||
attachment, err := services.GetAttachmentByUUID(id)
|
attachment, err := services.GetAttachmentByID(uint(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
} else if attachment.AuthorID != user.ID {
|
} else if attachment.AuthorID != user.ID {
|
||||||
|
@ -68,7 +68,7 @@ func NewServer() {
|
|||||||
CacheControl: true,
|
CacheControl: true,
|
||||||
}), readAttachment)
|
}), readAttachment)
|
||||||
api.Post("/attachments", authMiddleware, uploadAttachment)
|
api.Post("/attachments", authMiddleware, uploadAttachment)
|
||||||
api.Delete("/attachments/:fileId", authMiddleware, deleteAttachment)
|
api.Delete("/attachments/:id", authMiddleware, deleteAttachment)
|
||||||
|
|
||||||
api.Get("/feed", listFeed)
|
api.Get("/feed", listFeed)
|
||||||
|
|
||||||
|
@ -13,6 +13,16 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GetAttachmentByID(id uint) (models.Attachment, error) {
|
||||||
|
var attachment models.Attachment
|
||||||
|
if err := database.C.Where(models.Attachment{
|
||||||
|
BaseModel: models.BaseModel{ID: id},
|
||||||
|
}).First(&attachment).Error; err != nil {
|
||||||
|
return attachment, err
|
||||||
|
}
|
||||||
|
return attachment, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetAttachmentByUUID(fileId string) (models.Attachment, error) {
|
func GetAttachmentByUUID(fileId string) (models.Attachment, error) {
|
||||||
var attachment models.Attachment
|
var attachment models.Attachment
|
||||||
if err := database.C.Where(models.Attachment{
|
if err := database.C.Where(models.Attachment{
|
||||||
@ -23,11 +33,26 @@ func GetAttachmentByUUID(fileId string) (models.Attachment, error) {
|
|||||||
return attachment, nil
|
return attachment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAttachment(user models.Account, header *multipart.FileHeader) (models.Attachment, error) {
|
func GetAttachmentByHashcode(hashcode string) (models.Attachment, error) {
|
||||||
attachment := models.Attachment{
|
var attachment models.Attachment
|
||||||
|
if err := database.C.Where(models.Attachment{
|
||||||
|
Hashcode: hashcode,
|
||||||
|
}).First(&attachment).Error; err != nil {
|
||||||
|
return attachment, err
|
||||||
|
}
|
||||||
|
return attachment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAttachment(user models.Account, header *multipart.FileHeader, hashcode string) (models.Attachment, error) {
|
||||||
|
var attachment models.Attachment
|
||||||
|
existsAttachment, err := GetAttachmentByHashcode(hashcode)
|
||||||
|
if err != nil {
|
||||||
|
// Upload the new file
|
||||||
|
attachment = models.Attachment{
|
||||||
FileID: uuid.NewString(),
|
FileID: uuid.NewString(),
|
||||||
Filesize: header.Size,
|
Filesize: header.Size,
|
||||||
Filename: header.Filename,
|
Filename: header.Filename,
|
||||||
|
Hashcode: hashcode,
|
||||||
Mimetype: "unknown/unknown",
|
Mimetype: "unknown/unknown",
|
||||||
Type: models.AttachmentOthers,
|
Type: models.AttachmentOthers,
|
||||||
AuthorID: user.ID,
|
AuthorID: user.ID,
|
||||||
@ -58,6 +83,18 @@ func NewAttachment(user models.Account, header *multipart.FileHeader) (models.At
|
|||||||
default:
|
default:
|
||||||
attachment.Type = models.AttachmentOthers
|
attachment.Type = models.AttachmentOthers
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Instant upload, build link with the exists file
|
||||||
|
attachment = models.Attachment{
|
||||||
|
FileID: existsAttachment.FileID,
|
||||||
|
Filesize: header.Size,
|
||||||
|
Filename: header.Filename,
|
||||||
|
Hashcode: hashcode,
|
||||||
|
Mimetype: existsAttachment.Mimetype,
|
||||||
|
Type: existsAttachment.Type,
|
||||||
|
AuthorID: user.ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Save into database
|
// Save into database
|
||||||
err = database.C.Save(&attachment).Error
|
err = database.C.Save(&attachment).Error
|
||||||
@ -66,9 +103,20 @@ func NewAttachment(user models.Account, header *multipart.FileHeader) (models.At
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteAttachment(item models.Attachment) error {
|
func DeleteAttachment(item models.Attachment) error {
|
||||||
|
var dupeCount int64
|
||||||
|
if err := database.C.
|
||||||
|
Where(&models.Attachment{Hashcode: item.Hashcode}).
|
||||||
|
Model(&models.Attachment{}).
|
||||||
|
Count(&dupeCount).Error; err != nil {
|
||||||
|
dupeCount = -1
|
||||||
|
}
|
||||||
|
|
||||||
if err := database.C.Delete(&item).Error; err != nil {
|
if err := database.C.Delete(&item).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if dupeCount != -1 && dupeCount <= 1 {
|
||||||
|
// Safe for deletion the physics file
|
||||||
basepath := viper.GetString("content")
|
basepath := viper.GetString("content")
|
||||||
fullpath := filepath.Join(basepath, item.FileID)
|
fullpath := filepath.Join(basepath, item.FileID)
|
||||||
|
|
||||||
|
@ -171,6 +171,7 @@ function pasteMedia(evt: ClipboardEvent) {
|
|||||||
watch(editor.related, (val) => {
|
watch(editor.related, (val) => {
|
||||||
if (val.edit_to && val.edit_to.model_type === "moment") {
|
if (val.edit_to && val.edit_to.model_type === "moment") {
|
||||||
data.value = val.edit_to
|
data.value = val.edit_to
|
||||||
|
data.value.attachments = val.edit_to.attachments ?? []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -60,6 +60,8 @@ async function upload(file?: any) {
|
|||||||
data.set("attachment", file)
|
data.set("attachment", file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.set("hashcode", await calculateHashCode(picked.value[0]))
|
||||||
|
|
||||||
emits("update:uploading", true)
|
emits("update:uploading", true)
|
||||||
const res = await request("/api/attachments", {
|
const res = await request("/api/attachments", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -83,16 +85,33 @@ async function dispose(idx: number) {
|
|||||||
const item = media.splice(idx)[0]
|
const item = media.splice(idx)[0]
|
||||||
emits("update:value", media)
|
emits("update:value", media)
|
||||||
|
|
||||||
const res = await request(`/api/attachments/${item.file_id}`, {
|
const res = await request(`/api/attachments/${item.id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: { Authorization: `Bearer ${getAtk()}` },
|
headers: { Authorization: `Bearer ${getAtk()}` }
|
||||||
})
|
})
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
error.value = await res.text()
|
error.value = await res.text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ upload })
|
defineExpose({ upload, dispose })
|
||||||
|
|
||||||
|
async function calculateHashCode(file: File): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = async () => {
|
||||||
|
const buffer = reader.result as ArrayBuffer
|
||||||
|
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer)
|
||||||
|
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||||
|
const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("")
|
||||||
|
resolve(hashHex)
|
||||||
|
}
|
||||||
|
reader.onerror = () => {
|
||||||
|
reject(reader.error)
|
||||||
|
}
|
||||||
|
reader.readAsArrayBuffer(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function getFileName(item: any) {
|
function getFileName(item: any) {
|
||||||
return item.filename.replace(/\.[^/.]+$/, "")
|
return item.filename.replace(/\.[^/.]+$/, "")
|
||||||
|
Loading…
Reference in New Issue
Block a user