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 }