161 lines
3.7 KiB
Go
161 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
const maxChatAttachmentSize = 10 << 20
|
|
|
|
func (s *Server) handleUploadChatAttachment(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := userFromContext(r.Context())
|
|
if !ok {
|
|
writeError(w, http.StatusUnauthorized, "Unauthorized")
|
|
return
|
|
}
|
|
|
|
r.Body = http.MaxBytesReader(w, r.Body, maxChatAttachmentSize+(1<<20))
|
|
if err := r.ParseMultipartForm(maxChatAttachmentSize); err != nil {
|
|
writeError(w, http.StatusBadRequest, "Datei ist zu groß oder ungültig")
|
|
return
|
|
}
|
|
|
|
file, header, err := r.FormFile("file")
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, "Datei fehlt")
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
data, err := io.ReadAll(io.LimitReader(file, maxChatAttachmentSize+1))
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, "Datei konnte nicht gelesen werden")
|
|
return
|
|
}
|
|
if len(data) == 0 {
|
|
writeError(w, http.StatusBadRequest, "Datei ist leer")
|
|
return
|
|
}
|
|
if len(data) > maxChatAttachmentSize {
|
|
writeError(w, http.StatusBadRequest, "Datei darf höchstens 10 MB groß sein")
|
|
return
|
|
}
|
|
|
|
fileName := strings.TrimSpace(filepath.Base(header.Filename))
|
|
if fileName == "" || fileName == "." {
|
|
fileName = "Anhang"
|
|
}
|
|
|
|
contentType := strings.TrimSpace(header.Header.Get("Content-Type"))
|
|
if contentType == "" {
|
|
contentType = http.DetectContentType(data)
|
|
}
|
|
|
|
var attachment ChatAttachment
|
|
err = s.db.QueryRow(
|
|
r.Context(),
|
|
`
|
|
INSERT INTO message_attachments (
|
|
uploader_id,
|
|
file_name,
|
|
content_type,
|
|
size_bytes,
|
|
data
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id::TEXT, file_name, content_type, size_bytes
|
|
`,
|
|
user.ID,
|
|
fileName,
|
|
contentType,
|
|
len(data),
|
|
data,
|
|
).Scan(
|
|
&attachment.ID,
|
|
&attachment.FileName,
|
|
&attachment.ContentType,
|
|
&attachment.SizeBytes,
|
|
)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, "Datei konnte nicht gespeichert werden")
|
|
return
|
|
}
|
|
|
|
attachment.URL = "/chat/attachments/" + attachment.ID
|
|
writeJSON(w, http.StatusCreated, map[string]any{"attachment": attachment})
|
|
}
|
|
|
|
func (s *Server) handleDownloadChatAttachment(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := userFromContext(r.Context())
|
|
if !ok {
|
|
writeError(w, http.StatusUnauthorized, "Unauthorized")
|
|
return
|
|
}
|
|
|
|
attachmentID := strings.TrimSpace(r.PathValue("id"))
|
|
var uploaderID string
|
|
var messageID string
|
|
var conversationID string
|
|
var fileName string
|
|
var contentType string
|
|
var data []byte
|
|
|
|
err := s.db.QueryRow(
|
|
r.Context(),
|
|
`
|
|
SELECT
|
|
a.uploader_id::TEXT,
|
|
COALESCE(a.message_id::TEXT, ''),
|
|
COALESCE(m.conversation_id::TEXT, ''),
|
|
a.file_name,
|
|
a.content_type,
|
|
a.data
|
|
FROM message_attachments a
|
|
LEFT JOIN messages m ON m.id = a.message_id
|
|
WHERE a.id = $1
|
|
`,
|
|
attachmentID,
|
|
).Scan(
|
|
&uploaderID,
|
|
&messageID,
|
|
&conversationID,
|
|
&fileName,
|
|
&contentType,
|
|
&data,
|
|
)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, "Datei wurde nicht gefunden")
|
|
return
|
|
}
|
|
|
|
allowed := messageID == "" && uploaderID == user.ID
|
|
if messageID != "" {
|
|
allowed = s.canAccessConversation(r.Context(), conversationID, user.ID)
|
|
}
|
|
if !allowed {
|
|
writeError(w, http.StatusForbidden, "Kein Zugriff auf diese Datei")
|
|
return
|
|
}
|
|
|
|
if contentType == "" {
|
|
contentType = "application/octet-stream"
|
|
}
|
|
|
|
dispositionType := "attachment"
|
|
if strings.HasPrefix(strings.ToLower(contentType), "image/") {
|
|
dispositionType = "inline"
|
|
}
|
|
disposition := mime.FormatMediaType(dispositionType, map[string]string{
|
|
"filename": fileName,
|
|
})
|
|
w.Header().Set("Content-Type", contentType)
|
|
w.Header().Set("Content-Disposition", disposition)
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(data)
|
|
}
|