package handlers import ( "bytes" "encoding/base64" "image/png" "log" "os" "time" "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v5" "github.com/jackc/pgx/v5/pgtype" "github.com/pquerna/otp/totp" "golang.org/x/crypto/scrypt" "nim.jasinco.work/app/internal" "nim.jasinco.work/app/nimdb" ) type NewAdmin struct { Name string `json:"name"` Password string `json:"password"` TOTP_Secret string `json:"totp_secret"` } type AdminAuth struct { Name string `json:"name"` Password string `json:"password"` TOTP_code string `json:"totp_code"` } func Admin_create(c *fiber.Ctx) error { cfg := new(NewAdmin) if err := c.BodyParser(cfg); err != nil { return fiber.ErrBadRequest } hashed, err := scrypt.Key([]byte(cfg.Password), []byte(internal.SALT), 32768, 8, 1, 32) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } err = internal.NIMDB.AdminCreateAccount(c.Context(), nimdb.AdminCreateAccountParams{Username: cfg.Name, Password: base64.StdEncoding.EncodeToString(hashed), Totp: cfg.TOTP_Secret}) if err != nil { log.Println(base64.StdEncoding.EncodeToString(hashed), err) return c.SendStatus(fiber.StatusBadRequest) } return c.SendStatus(fiber.StatusCreated) } func Admin_new_totp_code(c *fiber.Ctx) error { name := c.Query("name") if len(name) == 0 { return fiber.ErrBadRequest } key, err := totp.Generate(totp.GenerateOpts{Issuer: "TCIVS_NIMING", AccountName: name}) if err != nil { return fiber.ErrInternalServerError } var buf bytes.Buffer img, err := key.Image(200, 200) png.Encode(&buf, img) return c.JSON(fiber.Map{"key": key.Secret(), "img": base64.StdEncoding.EncodeToString(buf.Bytes())}) } func Admin_Login(c *fiber.Ctx) error { cred := new(AdminAuth) if err := c.BodyParser(cred); err != nil { return fiber.ErrBadRequest } hashed, err := scrypt.Key([]byte(cred.Password), []byte(internal.SALT), 32768, 8, 1, 32) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } rec, err := internal.NIMDB.AdminLoginGetTOTP(c.Context(), nimdb.AdminLoginGetTOTPParams{Username: cred.Name, Password: base64.StdEncoding.EncodeToString(hashed)}) if err != nil { log.Println(err) return c.SendStatus(fiber.StatusUnauthorized) } if !totp.Validate(cred.TOTP_code, rec.Totp) { log.Println(totp.GenerateCode(rec.Totp, time.Now())) return c.SendStatus(fiber.StatusUnauthorized) } claims := jwt.MapClaims{"name": cred.Name, "admin": rec.Super} token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims) t, err := token.SignedString([]byte(internal.JWT_SECRET)) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } cookie := new(fiber.Cookie) cookie.Name = "token" cookie.Value = t cookie.Expires = time.Now().Add(5 * time.Hour) c.Cookie(cookie) return c.JSON(fiber.Map{"token": t}) } func Admin_Fetch_Post(c *fiber.Ctx) error { user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) super := claims["admin"].(bool) ctx := c.Context() if !super { rec, err := internal.NIMDB.AdminGetPost(ctx) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } type recwithimg struct { Id int32 `json:"id"` Content string `json:"content"` Signing pgtype.Text `json:"signing"` Post_at time.Time `json:"post_at"` Enclosue []string `json:"enclosure"` } rec_with_img := make([]recwithimg, len(rec)) for id, k := range rec { rec_with_img[id] = recwithimg{Id: k.ID, Content: k.Content, Signing: k.Signing, Post_at: k.PostAt.Time} imgrec, err := internal.NIMDB.AdminGetMedia(ctx, pgtype.Int4{Int32: k.ID, Valid: true}) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } rec_with_img[id].Enclosue = imgrec } return c.JSON(rec_with_img) } else { rec, err := internal.NIMDB.SuperAdminGetPost(ctx) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } type recwithimg struct { Id int32 `json:"id"` Content string `json:"content"` Signing pgtype.Text `json:"signing"` Post_at time.Time `json:"post_at"` Enclosue []string `json:"enclosure"` Phase nimdb.PostPhase `json:"phase"` } rec_with_img := make([]recwithimg, len(rec)) for id, k := range rec { rec_with_img[id] = recwithimg{Id: k.ID, Content: k.Content, Signing: k.Signing, Post_at: k.PostAt.Time, Phase: k.Phase} imgrec, err := internal.NIMDB.AdminGetMedia(ctx, pgtype.Int4{Int32: k.ID, Valid: true}) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } rec_with_img[id].Enclosue = imgrec } return c.JSON(rec_with_img) } } type Verify struct { Id int32 `json:"post"` Check bool `json:"check"` } func AdminVerify(c *fiber.Ctx) error { user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) super := claims["admin"].(bool) ctx := c.Context() tx, err := internal.POOL.Begin(ctx) defer tx.Rollback(ctx) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } qtx := internal.NIMDB.WithTx(tx) post_verify := new(Verify) log.Println(string(c.Body())) if err = c.BodyParser(post_verify); err != nil { return c.SendStatus(fiber.StatusBadRequest) } if !super { if post_verify.Check { _, err = qtx.AdminVerify(ctx, nimdb.AdminVerifyParams{ID: post_verify.Id, Phase: nimdb.PostPhaseOk}) } else { _, err = qtx.AdminVerify(ctx, nimdb.AdminVerifyParams{ID: post_verify.Id, Phase: nimdb.PostPhaseRejected}) } if err != nil { log.Println(err.Error()) return c.SendStatus(fiber.StatusBadRequest) } } else { if post_verify.Check { _, err = qtx.SuperAdminVerify(ctx, nimdb.SuperAdminVerifyParams{ID: post_verify.Id, Phase: nimdb.PostPhaseOk}) } else { _, err = qtx.SuperAdminVerify(ctx, nimdb.SuperAdminVerifyParams{ID: post_verify.Id, Phase: nimdb.PostPhaseAdminRejected}) } if err != nil { log.Println(err.Error()) return c.SendStatus(fiber.StatusBadRequest) } } err = qtx.AdminUpdateImage(ctx, pgtype.Int4{Int32: post_verify.Id, Valid: true}) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } return tx.Commit(ctx) }