1
All checks were successful
Deploy API / deploy (push) Successful in 50s

This commit is contained in:
2026-05-26 18:11:47 +08:00
parent 90184d40c7
commit 590088cccb
3 changed files with 111 additions and 24 deletions

View File

@@ -0,0 +1,110 @@
package handlers
import (
"context"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
type postAttachmentDownloadInfo struct {
URL string
Filename string
Mime string
}
func loadPublishedPostAttachment(ctx context.Context, pool *pgxpool.Pool, postID, aid uuid.UUID) (postAttachmentDownloadInfo, error) {
var info postAttachmentDownloadInfo
err := pool.QueryRow(ctx, `
SELECT pa.url, COALESCE(NULLIF(TRIM(pa.filename), ''), 'download'), COALESCE(NULLIF(TRIM(pa.mime), ''), 'application/octet-stream')
FROM post_attachments pa
JOIN posts p ON p.id = pa.post_id
WHERE pa.id = $1 AND pa.post_id = $2
AND `+publicPostWhere, aid, postID,
).Scan(&info.URL, &info.Filename, &info.Mime)
if err != nil {
return info, err
}
info.URL = strings.TrimSpace(info.URL)
if info.URL == "" {
return info, pgx.ErrNoRows
}
return info, nil
}
func incrPostDownloadCount(ctx context.Context, pool *pgxpool.Pool, postID uuid.UUID) error {
cmd, err := pool.Exec(ctx, `
UPDATE posts SET download_count = download_count + 1, updated_at = NOW()
WHERE id = $1 AND `+publicPostWhere, postID)
if err != nil {
return err
}
if cmd.RowsAffected() == 0 {
return pgx.ErrNoRows
}
return nil
}
func handlePostAttachmentDownload(w http.ResponseWriter, r *http.Request, redirect bool) {
pool := poolFrom(r)
postID, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "bad id", http.StatusBadRequest)
return
}
aid, err := uuid.Parse(chi.URLParam(r, "aid"))
if err != nil {
http.Error(w, "bad attachment id", http.StatusBadRequest)
return
}
ctx := r.Context()
info, err := loadPublishedPostAttachment(ctx, pool, postID, aid)
if err != nil {
if err == pgx.ErrNoRows {
http.NotFound(w, r)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := incrPostDownloadCount(ctx, pool, postID); err != nil {
if err == pgx.ErrNoRows {
http.NotFound(w, r)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if redirect {
http.Redirect(w, r, info.URL, http.StatusFound)
return
}
writeJSON(w, map[string]any{
"ok": true,
"url": info.URL,
"filename": info.Filename,
"mime": info.Mime,
})
}
// PostAttachmentDownload increments download_count and returns the attachment file URL for the client to open/save.
func PostAttachmentDownload(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
handlePostAttachmentDownload(w, r, false)
}
// GetPostAttachmentDownload increments download_count and redirects the browser to the attachment URL.
func GetPostAttachmentDownload(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
handlePostAttachmentDownload(w, r, true)
}

View File

@@ -93,30 +93,6 @@ func GetPost(w http.ResponseWriter, r *http.Request) {
writeJSON(w, dto)
}
func PostAttachmentDownload(w http.ResponseWriter, r *http.Request) {
pool := poolFrom(r)
postID, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "bad id", http.StatusBadRequest)
return
}
aid, err := uuid.Parse(chi.URLParam(r, "aid"))
if err != nil {
http.Error(w, "bad attachment id", http.StatusBadRequest)
return
}
cmd, err := pool.Exec(r.Context(), `
UPDATE posts SET download_count = download_count + 1, updated_at = NOW()
WHERE id = $1 AND status = 'published' AND is_public = TRUE
AND (published_at IS NULL OR published_at <= NOW())`, postID)
if err != nil || cmd.RowsAffected() == 0 {
http.NotFound(w, r)
return
}
writeJSON(w, map[string]any{"ok": true})
_ = aid
}
func listPostsQuery(w http.ResponseWriter, r *http.Request, searchMode bool) {
pool := poolFrom(r)
limit := postLimitDef(r, 20, 50)