nyanimedb/modules/backend/handlers/users.go

487 lines
13 KiB
Go

package handlers
import (
"context"
"errors"
"fmt"
oapi "nyanimedb/api"
sqlc "nyanimedb/sql"
"strconv"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgtype"
"github.com/oapi-codegen/runtime/types"
log "github.com/sirupsen/logrus"
)
const (
pgErrDuplicateKey = "23505"
)
func mapUser(u sqlc.GetUserByIDRow) (oapi.User, error) {
i := oapi.Image{
Id: u.AvatarID,
ImagePath: u.ImagePath,
}
s, err := sql2StorageType(u.StorageType)
if err != nil {
return oapi.User{}, fmt.Errorf("mapUser, storage type: %v", err)
}
i.StorageType = s
return oapi.User{
Image: &i,
CreationDate: &u.CreationDate,
DispName: u.DispName,
Id: &u.ID,
Mail: StringToEmail(u.Mail),
Nickname: u.Nickname,
UserDesc: u.UserDesc,
}, nil
}
func (s Server) GetUsersId(ctx context.Context, req oapi.GetUsersIdRequestObject) (oapi.GetUsersIdResponseObject, error) {
userID, err := parseInt64(req.UserId)
if err != nil {
return oapi.GetUsersId404Response{}, nil
}
_user, err := s.db.GetUserByID(context.TODO(), userID)
if err != nil {
if err == pgx.ErrNoRows {
return oapi.GetUsersId404Response{}, nil
}
return nil, err
}
user, err := mapUser(_user)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUsersId500Response{}, err
}
return oapi.GetUsersId200JSONResponse(user), nil
}
func sqlDate2oapi(p_date pgtype.Timestamptz) *time.Time {
if p_date.Valid {
t := p_date.Time
return &t
}
return nil
}
// func UserTitleStatus2Sqlc(s *[]oapi.UserTitleStatus) (*SqlcUserStatus, error) {
// var sqlc_status SqlcUserStatus
// if s == nil {
// return &sqlc_status, nil
// }
// for _, t := range *s {
// switch t {
// case oapi.UserTitleStatusFinished:
// sqlc_status.finished = "finished"
// case oapi.UserTitleStatusDropped:
// sqlc_status.dropped = "dropped"
// case oapi.UserTitleStatusPlanned:
// sqlc_status.planned = "planned"
// case oapi.UserTitleStatusInProgress:
// sqlc_status.in_progress = "in-progress"
// default:
// return nil, fmt.Errorf("unexpected tittle status: %s", t)
// }
// }
// return &sqlc_status, nil
// }
func sql2usertitlestatus(s sqlc.UsertitleStatusT) (oapi.UserTitleStatus, error) {
var status oapi.UserTitleStatus
switch s {
case sqlc.UsertitleStatusTFinished:
status = oapi.UserTitleStatusFinished
case sqlc.UsertitleStatusTDropped:
status = oapi.UserTitleStatusDropped
case sqlc.UsertitleStatusTPlanned:
status = oapi.UserTitleStatusPlanned
case sqlc.UsertitleStatusTInProgress:
status = oapi.UserTitleStatusInProgress
default:
return status, fmt.Errorf("unexpected tittle status: %s", s)
}
return status, nil
}
func UserTitleStatus2Sqlc(s *[]oapi.UserTitleStatus) ([]sqlc.UsertitleStatusT, error) {
var sqlc_status []sqlc.UsertitleStatusT
if s == nil {
return nil, nil
}
for _, t := range *s {
switch t {
case oapi.UserTitleStatusFinished:
sqlc_status = append(sqlc_status, sqlc.UsertitleStatusTFinished)
case oapi.UserTitleStatusInProgress:
sqlc_status = append(sqlc_status, sqlc.UsertitleStatusTInProgress)
case oapi.UserTitleStatusDropped:
sqlc_status = append(sqlc_status, sqlc.UsertitleStatusTDropped)
case oapi.UserTitleStatusPlanned:
sqlc_status = append(sqlc_status, sqlc.UsertitleStatusTPlanned)
default:
return nil, fmt.Errorf("unexpected tittle status: %s", t)
}
}
return sqlc_status, nil
}
func UserTitleStatus2Sqlc1(s *oapi.UserTitleStatus) (*sqlc.UsertitleStatusT, error) {
var sqlc_status sqlc.UsertitleStatusT = sqlc.UsertitleStatusTFinished
if s == nil {
return &sqlc_status, nil
}
switch *s {
case oapi.UserTitleStatusFinished:
sqlc_status = sqlc.UsertitleStatusTFinished
case oapi.UserTitleStatusInProgress:
sqlc_status = sqlc.UsertitleStatusTInProgress
case oapi.UserTitleStatusDropped:
sqlc_status = sqlc.UsertitleStatusTDropped
case oapi.UserTitleStatusPlanned:
sqlc_status = sqlc.UsertitleStatusTPlanned
default:
return nil, fmt.Errorf("unexpected tittle status: %s", *s)
}
return &sqlc_status, nil
}
func (s Server) mapUsertitle(ctx context.Context, t sqlc.SearchUserTitlesRow) (oapi.UserTitle, error) {
oapi_usertitle := oapi.UserTitle{
Ctime: &t.UserCtime,
Rate: t.UserRate,
ReviewId: t.ReviewID,
// Status: ,
// Title: ,
UserId: t.UserID,
}
status, err := sql2usertitlestatus(t.UsertitleStatus)
if err != nil {
return oapi_usertitle, fmt.Errorf("mapUsertitle: %v", err)
}
oapi_usertitle.Status = status
_title := sqlc.GetTitleByIDRow{
ID: t.ID,
// StudioID: title.StudioID,
PosterID: t.PosterID,
TitleStatus: t.TitleStatus,
Rating: t.Rating,
RatingCount: t.RatingCount,
ReleaseYear: t.ReleaseYear,
ReleaseSeason: t.ReleaseSeason,
Season: t.Season,
EpisodesAired: t.EpisodesAired,
EpisodesAll: t.EpisodesAll,
// EpisodesLen: title.EpisodesLen,
TitleStorageType: t.TitleStorageType,
TitleImagePath: t.TitleImagePath,
StudioName: t.StudioName,
TitleNames: t.TitleNames,
TagNames: t.TagNames,
// StudioIllustID: title.StudioIllustID,
// StudioDesc: title.StudioDesc,
// StudioStorageType: title.StudioStorageType,
// StudioImagePath: title.StudioImagePath,
}
oapi_title, err := s.mapTitle(_title)
if err != nil {
return oapi_usertitle, fmt.Errorf("mapUsertitle: %v", err)
}
oapi_usertitle.Title = &oapi_title
return oapi_usertitle, nil
}
func (s Server) GetUserTitles(ctx context.Context, request oapi.GetUserTitlesRequestObject) (oapi.GetUserTitlesResponseObject, error) {
oapi_usertitles := make([]oapi.UserTitle, 0)
word := Word2Sqlc(request.Params.Word)
season, err := ReleaseSeason2sqlc(request.Params.ReleaseSeason)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitles400Response{}, err
}
// var statuses_sort []string
// if request.Params.Status != nil {
// for _, s := range *request.Params.Status {
// ss := string(s) // s type is alias for string
// statuses_sort = append(statuses_sort, ss)
// }
// }
watch_status, err := UserTitleStatus2Sqlc(request.Params.WatchStatus)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitles400Response{}, err
}
title_statuses, err := TitleStatus2Sqlc(request.Params.Status)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitles400Response{}, err
}
userID, err := parseInt64(request.UserId)
if err != nil {
log.Errorf("get user titles: %v", err)
return oapi.GetUserTitles404Response{}, err
}
params := sqlc.SearchUserTitlesParams{
UserID: userID,
Word: word,
TitleStatuses: title_statuses,
UsertitleStatuses: watch_status,
Rating: request.Params.Rating,
Rate: request.Params.MyRate,
ReleaseYear: request.Params.ReleaseYear,
ReleaseSeason: season,
Forward: true, // default
SortBy: "id", // default
Limit: request.Params.Limit,
}
if request.Params.SortForward != nil {
params.Forward = *request.Params.SortForward
}
if request.Params.Sort != nil {
params.SortBy = string(*request.Params.Sort)
if request.Params.Cursor != nil {
// here we set CursorYear CursorID CursorRating fields
err := ParseCursorInto(string(*request.Params.Sort), string(*request.Params.Cursor), &params)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitles400Response{}, nil
}
}
}
// param = nil means it will not be used
titles, err := s.db.SearchUserTitles(ctx, params)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitles500Response{}, nil
}
if len(titles) == 0 {
return oapi.GetUserTitles204Response{}, nil
}
var new_cursor oapi.CursorObj
for _, title := range titles {
t, err := s.mapUsertitle(ctx, title)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitles500Response{}, nil
}
oapi_usertitles = append(oapi_usertitles, t)
new_cursor.Id = t.Title.Id
if request.Params.Sort != nil {
switch string(*request.Params.Sort) {
case "year":
tmp := fmt.Sprint(*t.Title.ReleaseYear)
new_cursor.Param = &tmp
case "rating":
tmp := strconv.FormatFloat(*t.Title.Rating, 'f', -1, 64) // падает
new_cursor.Param = &tmp
}
}
}
return oapi.GetUserTitles200JSONResponse{Cursor: new_cursor, Data: oapi_usertitles}, nil
}
func EmailToStringPtr(e *types.Email) *string {
if e == nil {
return nil
}
s := string(*e)
return &s
}
func StringToEmail(e *string) *types.Email {
if e == nil {
return nil
}
s := types.Email(*e)
return &s
}
// UpdateUser implements oapi.StrictServerInterface.
func (s Server) UpdateUser(ctx context.Context, request oapi.UpdateUserRequestObject) (oapi.UpdateUserResponseObject, error) {
params := sqlc.UpdateUserParams{
AvatarID: request.Body.AvatarId,
DispName: request.Body.DispName,
UserDesc: request.Body.UserDesc,
Mail: EmailToStringPtr(request.Body.Mail),
UserID: request.UserId,
}
user, err := s.db.UpdateUser(ctx, params)
if err != nil {
log.Errorf("%v", err)
return oapi.UpdateUser500Response{}, nil
}
oapi_user := oapi.User{ // maybe its possible to make one sqlc type and use one map func iinstead of this shit
// AvatarId: user.AvatarID,
CreationDate: &user.CreationDate,
DispName: user.DispName,
Id: &user.ID,
Mail: StringToEmail(user.Mail),
Nickname: user.Nickname,
UserDesc: user.UserDesc,
}
return oapi.UpdateUser200JSONResponse(oapi_user), nil
}
func (s Server) AddUserTitle(ctx context.Context, request oapi.AddUserTitleRequestObject) (oapi.AddUserTitleResponseObject, error) {
//TODO: add review if exists
status, err := UserTitleStatus2Sqlc1(&request.Body.Status)
if err != nil {
log.Errorf("%v", err)
return oapi.AddUserTitle400Response{}, nil
}
params := sqlc.InsertUserTitleParams{
UserID: request.UserId,
TitleID: request.Body.TitleId,
Status: *status,
Rate: request.Body.Rate,
}
user_title, err := s.db.InsertUserTitle(ctx, params)
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
// fmt.Println(pgErr.Message) // => syntax error at end of input
// fmt.Println(pgErr.Code) // => 42601
if pgErr.Code == pgErrDuplicateKey { //duplicate key value
return oapi.AddUserTitle409Response{}, nil
}
} else {
log.Errorf("%v", err)
return oapi.AddUserTitle500Response{}, nil
}
}
oapi_status, err := sql2usertitlestatus(user_title.Status)
if err != nil {
log.Errorf("%v", err)
return oapi.AddUserTitle500Response{}, nil
}
oapi_usertitle := oapi.UserTitleMini{
Ctime: &user_title.Ctime,
Rate: user_title.Rate,
ReviewId: user_title.ReviewID,
Status: oapi_status,
TitleId: user_title.TitleID,
UserId: user_title.UserID,
}
return oapi.AddUserTitle200JSONResponse(oapi_usertitle), nil
}
// DeleteUserTitle implements oapi.StrictServerInterface.
func (s Server) DeleteUserTitle(ctx context.Context, request oapi.DeleteUserTitleRequestObject) (oapi.DeleteUserTitleResponseObject, error) {
params := sqlc.DeleteUserTitleParams{
UserID: request.UserId,
TitleID: request.TitleId,
}
_, err := s.db.DeleteUserTitle(ctx, params)
if err != nil {
if err == pgx.ErrNoRows {
return oapi.DeleteUserTitle404Response{}, nil
}
log.Errorf("%v", err)
return oapi.DeleteUserTitle500Response{}, nil
}
return oapi.DeleteUserTitle200Response{}, nil
}
// UpdateUserTitle implements oapi.StrictServerInterface.
func (s Server) UpdateUserTitle(ctx context.Context, request oapi.UpdateUserTitleRequestObject) (oapi.UpdateUserTitleResponseObject, error) {
status, err := UserTitleStatus2Sqlc1(request.Body.Status)
if err != nil {
log.Errorf("%v", err)
return oapi.UpdateUserTitle400Response{}, nil
}
params := sqlc.UpdateUserTitleParams{
Status: status,
Rate: request.Body.Rate,
UserID: request.UserId,
TitleID: request.TitleId,
}
user_title, err := s.db.UpdateUserTitle(ctx, params)
if err != nil {
if err == pgx.ErrNoRows {
return oapi.UpdateUserTitle404Response{}, nil
}
log.Errorf("%v", err)
return oapi.UpdateUserTitle500Response{}, nil
}
oapi_status, err := sql2usertitlestatus(user_title.Status)
if err != nil {
log.Errorf("%v", err)
return oapi.UpdateUserTitle500Response{}, nil
}
oapi_usertitle := oapi.UserTitleMini{
Ctime: &user_title.Ctime,
Rate: user_title.Rate,
ReviewId: user_title.ReviewID,
Status: oapi_status,
TitleId: user_title.TitleID,
UserId: user_title.UserID,
}
return oapi.UpdateUserTitle200JSONResponse(oapi_usertitle), nil
}
func (s Server) GetUserTitle(ctx context.Context, request oapi.GetUserTitleRequestObject) (oapi.GetUserTitleResponseObject, error) {
user_title, err := s.db.GetUserTitleByID(ctx, sqlc.GetUserTitleByIDParams{
TitleID: request.TitleId,
UserID: request.UserId,
})
if err != nil {
if err == pgx.ErrNoRows {
return oapi.GetUserTitle404Response{}, nil
} else {
log.Errorf("%v", err)
return oapi.GetUserTitle500Response{}, nil
}
}
oapi_status, err := sql2usertitlestatus(user_title.Status)
if err != nil {
log.Errorf("%v", err)
return oapi.GetUserTitle500Response{}, nil
}
oapi_usertitle := oapi.UserTitleMini{
Ctime: &user_title.Ctime,
Rate: user_title.Rate,
ReviewId: user_title.ReviewID,
Status: oapi_status,
TitleId: user_title.TitleID,
UserId: user_title.UserID,
}
return oapi.GetUserTitle200JSONResponse(oapi_usertitle), nil
}