package api import ( "errors" "log" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" "booklib/internal/auth" ) func isUnique(e error) bool { var pgErr *pgconn.PgError return errors.As(e, &pgErr) && pgErr.Code == "23505" } func (a *api) listUsers(c *gin.Context) { users, e := a.st.ListUsers(c) if e != nil { log.Printf("db: %v", e) err(c, http.StatusInternalServerError, "internal", "db error") return } out := make([]gin.H, 0, len(users)) for _, u := range users { out = append(out, gin.H{"id": u.ID, "username": u.Username, "role": u.Role, "created_at": u.CreatedAt.Format(time.RFC3339)}) } c.JSON(http.StatusOK, out) } func (a *api) createUser(c *gin.Context) { var req struct{ Username, Password, Role string } if c.ShouldBindJSON(&req) != nil { err(c, http.StatusBadRequest, "bad_request", "json body required") return } if req.Role != "admin" && req.Role != "member" { err(c, http.StatusBadRequest, "bad_request", "role must be admin|member") return } if len(req.Password) < 8 { err(c, http.StatusBadRequest, "bad_request", "password too short (min 8)") return } h, e := auth.HashPassword(req.Password) if e != nil { err(c, http.StatusInternalServerError, "internal", "hash") return } id, e := a.st.CreateUser(c, req.Username, h, req.Role) if e != nil { if isUnique(e) { err(c, http.StatusConflict, "exists", "username taken") return } err(c, http.StatusBadRequest, "bad_request", "invalid input") return } c.JSON(http.StatusCreated, gin.H{"id": id, "username": req.Username, "role": req.Role}) } func (a *api) deleteUser(c *gin.Context) { id, e := strconv.ParseInt(c.Param("id"), 10, 64) if e != nil { err(c, http.StatusBadRequest, "bad_request", "bad id") return } if id == uid(c) { err(c, http.StatusBadRequest, "bad_request", "cannot delete yourself") return } target, e := a.st.GetUserByID(c, id) if e != nil { if errors.Is(e, pgx.ErrNoRows) { err(c, http.StatusNotFound, "not_found", "no such user") return } err(c, http.StatusInternalServerError, "internal", "db error") return } if target.Role == "admin" { n, _ := a.st.CountAdmins(c) // 防删光最后一个 admin if n <= 1 { err(c, http.StatusBadRequest, "bad_request", "cannot delete the last admin") return } } if e := a.st.DeleteUser(c, id); e != nil { err(c, http.StatusInternalServerError, "internal", "db error") return } c.Status(http.StatusNoContent) }