nim/internal/handlers/post.go
2025-06-01 21:20:08 +08:00

226 lines
6.1 KiB
Go

package handlers
import (
"context"
"crypto/sha256"
"encoding/base64"
"log"
"mime"
"path"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"nim.jasinco.work/app/internal"
"nim.jasinco.work/app/nimdb"
)
type Post struct {
ID int32 `json:"id"`
Content string `json:"content"`
Heart int32 `json:"heart"`
Enclosure []string `json:"enclosure"`
Signing pgtype.Text `json:"signing"`
PostAt pgtype.Timestamptz `json:"post_at"`
IGid pgtype.Text `json:"igid"`
}
var supported_filetype = []string{"image/png", "image/jpeg", "image/webp", "image/gif", "image/heif", "video/H264", "video/H265", "video/mp4"}
func find_supported(mimetype string) bool {
for _, supported := range supported_filetype {
if strings.Compare(supported, mimetype) == 0 {
return true
}
}
return false
}
func Fetch_post(c *fiber.Ctx) error {
cursor := c.Query("cursor")
ctx := c.Context()
var rec []nimdb.GetPostRow
var err error
if len(cursor) == 0 {
rec, err = internal.NIMDB.GetPost(ctx, 10)
if err != nil {
log.Println(err)
return fiber.ErrInternalServerError
}
} else if cursor_num, err := strconv.Atoi(cursor); err == nil {
rec_cur, err := internal.NIMDB.GetPostWithCursor(ctx, nimdb.GetPostWithCursorParams{ID: int32(cursor_num), Limit: 10})
if err != nil {
log.Println(err)
return c.Status(500).SendString("Failed to Query DB")
}
rec = make([]nimdb.GetPostRow, len(rec_cur))
for id, val := range rec_cur {
rec[id] = nimdb.GetPostRow(val)
}
} else {
return c.SendStatus(fiber.StatusBadRequest)
}
if len(rec) == 0 {
return c.SendStatus(fiber.StatusNoContent)
}
var rec_with_media []Post = make([]Post, len(rec))
for i, val := range rec {
rec_with_media[i] = Post{ID: val.ID, Content: val.Content, Signing: val.Signing, PostAt: val.PostAt, Heart: val.Heart, IGid: val.Igid}
media, err := internal.NIMDB.GetPostMedia(ctx, pgtype.Int4{Int32: val.ID, Valid: true})
if err != nil {
log.Println(err)
return fiber.ErrInternalServerError
}
rec_with_media[i].Enclosure = media
}
return c.JSON(rec_with_media)
}
func Delete_post(c *fiber.Ctx) error {
post := c.Query("post")
ctx := c.Context()
post_id, err := strconv.Atoi(post)
if err != nil {
return c.Status(500).SendString("Failed to Parse Int")
}
tx, err := internal.POOL.Begin(ctx)
if err != nil {
return c.Status(500).SendString("Failed to Start Transaction")
}
defer tx.Rollback(ctx)
qtx := internal.NIMDB.WithTx(tx)
if err = qtx.DeletePost(ctx, int32(post_id)); err != nil {
return err
}
if err = qtx.InvalidateMedia(ctx, pgtype.Int4{Int32: int32(post_id)}); err != nil {
return err
}
if tx.Commit(ctx) != nil {
return c.Status(500).SendString("Failed to Commit")
}
return c.SendStatus(200)
}
func Fetch_comment(c *fiber.Ctx) error {
cursor := c.Query("cursor")
post, err := strconv.Atoi(c.Query("post"))
if err != nil {
return c.Status(500).SendString("Failed to parse Int")
}
ctx := context.Background()
conn, err := internal.POOL.Acquire(ctx)
if err != nil {
return c.Status(500).SendString("Failed to Acuire DB")
}
defer conn.Release()
db := nimdb.New(conn)
var rec []nimdb.GetCommentRow
if len(cursor) == 0 {
rec, err = db.GetComment(ctx, nimdb.GetCommentParams{PostID: int32(post), Limit: 10})
} else if cursor_num, err := strconv.Atoi(cursor); err == nil {
rec_cur, err := db.GetCommentWithCursor(ctx, nimdb.GetCommentWithCursorParams{ID: int32(cursor_num), PostID: int32(post), Limit: 10})
if err != nil {
return c.Status(500).SendString("Failed to Parse Query DB")
}
rec = make([]nimdb.GetCommentRow, len(rec_cur))
for id, val := range rec_cur {
rec[id] = nimdb.GetCommentRow(val)
}
}
return c.JSON(rec)
}
func Insert_Post(c *fiber.Ctx) error {
ctx := c.Context()
tx, err := internal.POOL.Begin(ctx)
if err != nil {
return c.Status(500).SendString("Failed to Start Transaction")
}
defer tx.Rollback(ctx)
qtx := internal.NIMDB.WithTx(tx)
if form, err := c.MultipartForm(); err == nil {
content := form.Value["content"]
signing := form.Value["signing"]
files := form.File["enclosure"]
if len(content) == 0 || len(content[0]) == 0 {
return fiber.ErrBadRequest
}
post_hash := sha256.New()
post_hash.Write([]byte(content[0]))
signing_pgt := new(pgtype.Text)
signing_pgt.Valid = false
if len(signing) > 0 && len(signing[0]) > 0 {
post_hash.Write([]byte(signing[0]))
signing_pgt.Valid = true
signing_pgt.String = signing[0]
}
now, err := time.Now().MarshalBinary()
if err != nil {
c.SendString("Failed to get time")
return c.SendStatus(500)
}
credential, err := qtx.InsertPost(ctx,
nimdb.InsertPostParams{
Content: content[0],
Signing: *signing_pgt,
Hash: base64.StdEncoding.EncodeToString(post_hash.Sum(now)),
},
)
if err != nil {
log.Println(err.Error())
return c.Status(500).SendString("Failed to insert post")
}
for _, file := range files {
filemime, _, err := mime.ParseMediaType(file.Header.Get("Content-Type"))
if err != nil {
return c.Status(fiber.ErrBadRequest.Code).SendString("Failed to parse content type")
}
if !find_supported(filemime) {
return c.Status(fiber.ErrBadRequest.Code).SendString("Unsupported Type")
}
filetype := strings.Split(filemime, "/")[0]
mid, err := uuid.NewRandom()
if err != nil {
c.SendString("Faild to generate UUIDv4")
return c.SendStatus(500)
}
fxnamepath := filetype + "/" + mid.String()
if err = c.SaveFile(file, path.Join("./static", fxnamepath)); err != nil {
return fiber.ErrInternalServerError
}
if err := qtx.InsertPostImage(ctx, nimdb.InsertPostImageParams{
Url: fxnamepath,
PostID: pgtype.Int4{Int32: credential.ID, Valid: true},
}); err != nil {
log.Println(err.Error())
return c.Status(500).SendString("Failed to insert image")
}
}
if err = tx.Commit(ctx); err != nil {
log.Println(err.Error())
c.SendString("Failed to send Commit")
return c.SendStatus(500)
}
return c.JSON(credential)
}
return fiber.ErrBadRequest
}