From 673ce48fac5c17f4aab157344cc8fce86e45e124 Mon Sep 17 00:00:00 2001 From: Iron_Felix Date: Tue, 25 Nov 2025 03:55:23 +0300 Subject: [PATCH 1/2] fix: bad types from sql --- modules/backend/handlers/common.go | 30 ++++++++++- modules/backend/handlers/titles.go | 85 ++++++++++++++++-------------- modules/backend/queries.sql | 8 +-- sql/queries.sql.go | 18 +++---- sql/sqlc.yaml | 7 ++- 5 files changed, 91 insertions(+), 57 deletions(-) diff --git a/modules/backend/handlers/common.go b/modules/backend/handlers/common.go index e233f98..73efc42 100644 --- a/modules/backend/handlers/common.go +++ b/modules/backend/handlers/common.go @@ -17,6 +17,22 @@ func NewServer(db *sqlc.Queries) Server { return Server{db: db} } +func sql2StorageType(s *sqlc.StorageTypeT) (*oapi.ImageStorageType, error) { + if s == nil { + return nil, nil + } + var t oapi.ImageStorageType + switch *s { + case sqlc.StorageTypeTLocal: + t = oapi.Local + case sqlc.StorageTypeTS3: + t = oapi.S3 + default: + return nil, fmt.Errorf("unexpected storage type: %s", *s) + } + return &t, nil +} + func (s Server) mapTitle(ctx context.Context, title sqlc.GetTitleByIDRow) (oapi.Title, error) { oapi_title := oapi.Title{ @@ -70,7 +86,13 @@ func (s Server) mapTitle(ctx context.Context, title sqlc.GetTitleByIDRow) (oapi. oapi_studio.Poster = &oapi.Image{} oapi_studio.Poster.Id = title.StudioIllustID oapi_studio.Poster.ImagePath = title.StudioImagePath - oapi_studio.Poster.StorageType = &title.StudioStorageType + + s, err := sql2StorageType(title.StudioStorageType) + if err != nil { + return oapi.Title{}, fmt.Errorf("mapTitle, studio storage type: %v", err) + } + oapi_studio.Poster.StorageType = s + } } oapi_title.Studio = &oapi_studio @@ -80,7 +102,11 @@ func (s Server) mapTitle(ctx context.Context, title sqlc.GetTitleByIDRow) (oapi. if title.PosterID != nil { oapi_image.Id = title.PosterID oapi_image.ImagePath = title.TitleImagePath - oapi_image.StorageType = &title.TitleStorageType + s, err := sql2StorageType(title.TitleStorageType) + if err != nil { + return oapi.Title{}, fmt.Errorf("mapTitle, title starage type: %v", err) + } + oapi_image.StorageType = s } oapi_title.Poster = &oapi_image diff --git a/modules/backend/handlers/titles.go b/modules/backend/handlers/titles.go index ec9426c..c67177f 100644 --- a/modules/backend/handlers/titles.go +++ b/modules/backend/handlers/titles.go @@ -81,56 +81,56 @@ func (s Server) GetTagsByTitleId(ctx context.Context, id int64) (oapi.Tags, erro return oapi_tag_names, nil } -func (s Server) GetImage(ctx context.Context, id int64) (*oapi.Image, error) { +// func (s Server) GetImage(ctx context.Context, id int64) (*oapi.Image, error) { - var oapi_image oapi.Image +// var oapi_image oapi.Image - sqlc_image, err := s.db.GetImageByID(ctx, id) - if err != nil { - if err == pgx.ErrNoRows { - return nil, nil //todo: error reference in db - } - return &oapi_image, fmt.Errorf("query GetImageByID: %v", err) - } +// sqlc_image, err := s.db.GetImageByID(ctx, id) +// if err != nil { +// if err == pgx.ErrNoRows { +// return nil, nil //todo: error reference in db +// } +// return &oapi_image, fmt.Errorf("query GetImageByID: %v", err) +// } - //can cast and dont use brain cause all this fields required in image table - oapi_image.Id = &sqlc_image.ID - oapi_image.ImagePath = &sqlc_image.ImagePath - storageTypeStr := string(sqlc_image.StorageType) - oapi_image.StorageType = &storageTypeStr +// //can cast and dont use brain cause all this fields required in image table +// oapi_image.Id = &sqlc_image.ID +// oapi_image.ImagePath = &sqlc_image.ImagePath +// storageTypeStr := string(sqlc_image.StorageType) +// oapi_image.StorageType = string(storageTypeStr) - return &oapi_image, nil -} +// return &oapi_image, nil +// } -func (s Server) GetStudio(ctx context.Context, id int64) (*oapi.Studio, error) { +// func (s Server) GetStudio(ctx context.Context, id int64) (*oapi.Studio, error) { - var oapi_studio oapi.Studio +// var oapi_studio oapi.Studio - sqlc_studio, err := s.db.GetStudioByID(ctx, id) - if err != nil { - if err == pgx.ErrNoRows { - return nil, nil - } - return &oapi_studio, fmt.Errorf("query GetStudioByID: %v", err) - } +// sqlc_studio, err := s.db.GetStudioByID(ctx, id) +// if err != nil { +// if err == pgx.ErrNoRows { +// return nil, nil +// } +// return &oapi_studio, fmt.Errorf("query GetStudioByID: %v", err) +// } - oapi_studio.Id = sqlc_studio.ID - oapi_studio.Name = sqlc_studio.StudioName - oapi_studio.Description = sqlc_studio.StudioDesc +// oapi_studio.Id = sqlc_studio.ID +// oapi_studio.Name = sqlc_studio.StudioName +// oapi_studio.Description = sqlc_studio.StudioDesc - if sqlc_studio.IllustID == nil { - return &oapi_studio, nil - } - oapi_illust, err := s.GetImage(ctx, *sqlc_studio.IllustID) - if err != nil { - return &oapi_studio, fmt.Errorf("GetImage: %v", err) - } - if oapi_illust != nil { - oapi_studio.Poster = oapi_illust - } +// if sqlc_studio.IllustID == nil { +// return &oapi_studio, nil +// } +// oapi_illust, err := s.GetImage(ctx, *sqlc_studio.IllustID) +// if err != nil { +// return &oapi_studio, fmt.Errorf("GetImage: %v", err) +// } +// if oapi_illust != nil { +// oapi_studio.Poster = oapi_illust +// } - return &oapi_studio, nil -} +// return &oapi_studio, nil +// } func (s Server) GetTitlesTitleId(ctx context.Context, request oapi.GetTitlesTitleIdRequestObject) (oapi.GetTitlesTitleIdResponseObject, error) { var oapi_title oapi.Title @@ -233,6 +233,11 @@ func (s Server) GetTitles(ctx context.Context, request oapi.GetTitlesRequestObje // StudioImagePath: title.StudioImagePath, } + // if title.TitleStorageType != nil { + // s := *title.TitleStorageType + // _title.TitleStorageType = string(s) + // } + t, err := s.mapTitle(ctx, _title) if err != nil { log.Errorf("%v", err) diff --git a/modules/backend/queries.sql b/modules/backend/queries.sql index b9e05c1..87d7755 100644 --- a/modules/backend/queries.sql +++ b/modules/backend/queries.sql @@ -76,7 +76,7 @@ RETURNING id, avatar_id, nickname, disp_name, user_desc, creation_date, mail; -- sqlc.struct: TitlesFull SELECT t.*, - i.storage_type::text as title_storage_type, + i.storage_type as title_storage_type, i.image_path as title_image_path, COALESCE( jsonb_agg(g.tag_names) FILTER (WHERE g.tag_names IS NOT NULL), @@ -85,7 +85,7 @@ SELECT s.studio_name as studio_name, s.illust_id as studio_illust_id, s.studio_desc as studio_desc, - si.storage_type::text as studio_storage_type, + si.storage_type as studio_storage_type, si.image_path as studio_image_path FROM titles as t @@ -112,7 +112,7 @@ SELECT t.season as season, t.episodes_aired as episodes_aired, t.episodes_all as episodes_all, - i.storage_type::text as title_storage_type, + i.storage_type as title_storage_type, i.image_path as title_image_path, COALESCE( jsonb_agg(g.tag_names) FILTER (WHERE g.tag_names IS NOT NULL), @@ -243,7 +243,7 @@ SELECT u.rate as user_rate, u.review_id as review_id, u.ctime as user_ctime, - i.storage_type::text as title_storage_type, + i.storage_type as title_storage_type, i.image_path as title_image_path, COALESCE( jsonb_agg(g.tag_names) FILTER (WHERE g.tag_names IS NOT NULL), diff --git a/sql/queries.sql.go b/sql/queries.sql.go index fa44808..5236f1f 100644 --- a/sql/queries.sql.go +++ b/sql/queries.sql.go @@ -114,7 +114,7 @@ const getTitleByID = `-- name: GetTitleByID :one SELECT t.id, t.title_names, t.studio_id, t.poster_id, t.title_status, t.rating, t.rating_count, t.release_year, t.release_season, t.season, t.episodes_aired, t.episodes_all, t.episodes_len, - i.storage_type::text as title_storage_type, + i.storage_type as title_storage_type, i.image_path as title_image_path, COALESCE( jsonb_agg(g.tag_names) FILTER (WHERE g.tag_names IS NOT NULL), @@ -123,7 +123,7 @@ SELECT s.studio_name as studio_name, s.illust_id as studio_illust_id, s.studio_desc as studio_desc, - si.storage_type::text as studio_storage_type, + si.storage_type as studio_storage_type, si.image_path as studio_image_path FROM titles as t @@ -152,13 +152,13 @@ type GetTitleByIDRow struct { EpisodesAired *int32 `json:"episodes_aired"` EpisodesAll *int32 `json:"episodes_all"` EpisodesLen []byte `json:"episodes_len"` - TitleStorageType string `json:"title_storage_type"` + TitleStorageType *StorageTypeT `json:"title_storage_type"` TitleImagePath *string `json:"title_image_path"` TagNames json.RawMessage `json:"tag_names"` StudioName *string `json:"studio_name"` StudioIllustID *int64 `json:"studio_illust_id"` StudioDesc *string `json:"studio_desc"` - StudioStorageType string `json:"studio_storage_type"` + StudioStorageType *StorageTypeT `json:"studio_storage_type"` StudioImagePath *string `json:"studio_image_path"` } @@ -415,7 +415,7 @@ SELECT t.season as season, t.episodes_aired as episodes_aired, t.episodes_all as episodes_all, - i.storage_type::text as title_storage_type, + i.storage_type as title_storage_type, i.image_path as title_image_path, COALESCE( jsonb_agg(g.tag_names) FILTER (WHERE g.tag_names IS NOT NULL), @@ -555,7 +555,7 @@ type SearchTitlesRow struct { Season *int32 `json:"season"` EpisodesAired *int32 `json:"episodes_aired"` EpisodesAll *int32 `json:"episodes_all"` - TitleStorageType string `json:"title_storage_type"` + TitleStorageType *StorageTypeT `json:"title_storage_type"` TitleImagePath *string `json:"title_image_path"` TagNames json.RawMessage `json:"tag_names"` StudioName *string `json:"studio_name"` @@ -628,7 +628,7 @@ SELECT u.rate as user_rate, u.review_id as review_id, u.ctime as user_ctime, - i.storage_type::text as title_storage_type, + i.storage_type as title_storage_type, i.image_path as title_image_path, COALESCE( jsonb_agg(g.tag_names) FILTER (WHERE g.tag_names IS NOT NULL), @@ -719,7 +719,7 @@ WHERE $8::usertitle_status_t[] IS NULL OR array_length($8::usertitle_status_t[], 1) IS NULL OR array_length($8::usertitle_status_t[], 1) = 0 - OR t.title_status = ANY($8::usertitle_status_t[]) + OR u.status = ANY($8::usertitle_status_t[]) ) AND ($9::int IS NULL OR u.rate >= $9::int) AND ($10::float IS NULL OR t.rating >= $10::float) @@ -785,7 +785,7 @@ type SearchUserTitlesRow struct { UserRate *int32 `json:"user_rate"` ReviewID *int64 `json:"review_id"` UserCtime time.Time `json:"user_ctime"` - TitleStorageType string `json:"title_storage_type"` + TitleStorageType *StorageTypeT `json:"title_storage_type"` TitleImagePath *string `json:"title_image_path"` TagNames json.RawMessage `json:"tag_names"` StudioName *string `json:"studio_name"` diff --git a/sql/sqlc.yaml b/sql/sqlc.yaml index f26cf92..de67bcf 100644 --- a/sql/sqlc.yaml +++ b/sql/sqlc.yaml @@ -14,8 +14,11 @@ sql: emit_pointers_for_null_types: true emit_empty_slices: true #slices returned by :many queries will be empty instead of nil overrides: - - column: "titles.title_storage_type" - go_type: "*string" + - db_type: "storage_type_t" + nullable: true + go_type: + type: "StorageTypeT" + pointer: true - db_type: "jsonb" go_type: "encoding/json.RawMessage" - db_type: "uuid" From 3aafab36c266b22272981de050d37b58e80ec240 Mon Sep 17 00:00:00 2001 From: Iron_Felix Date: Tue, 25 Nov 2025 04:15:46 +0300 Subject: [PATCH 2/2] feat: now GetUser returnes all the image info --- api/_build/openapi.yaml | 7 ++----- api/api.gen.go | 6 ++---- api/schemas/User.yaml | 7 ++----- modules/backend/handlers/users.go | 26 +++++++++++++++++------ modules/backend/queries.sql | 16 ++++++++++++--- sql/queries.sql.go | 34 ++++++++++++++++++++++--------- 6 files changed, 63 insertions(+), 33 deletions(-) diff --git a/api/_build/openapi.yaml b/api/_build/openapi.yaml index d2f231d..10cb7c7 100644 --- a/api/_build/openapi.yaml +++ b/api/_build/openapi.yaml @@ -552,11 +552,8 @@ components: format: int64 description: Unique user ID (primary key) example: 1 - avatar_id: - type: integer - format: int64 - description: ID of the user avatar (references images table) - example: null + image: + $ref: '#/components/schemas/Image' mail: type: string format: email diff --git a/api/api.gen.go b/api/api.gen.go index 5e0ddb5..02d389e 100644 --- a/api/api.gen.go +++ b/api/api.gen.go @@ -124,9 +124,6 @@ type TitleStatus string // User defines model for User. type User struct { - // AvatarId ID of the user avatar (references images table) - AvatarId *int64 `json:"avatar_id,omitempty"` - // CreationDate Timestamp when the user was created CreationDate *time.Time `json:"creation_date,omitempty"` @@ -134,7 +131,8 @@ type User struct { DispName *string `json:"disp_name,omitempty"` // Id Unique user ID (primary key) - Id *int64 `json:"id,omitempty"` + Id *int64 `json:"id,omitempty"` + Image *Image `json:"image,omitempty"` // Mail User email Mail *openapi_types.Email `json:"mail,omitempty"` diff --git a/api/schemas/User.yaml b/api/schemas/User.yaml index 8b4d88d..4e53534 100644 --- a/api/schemas/User.yaml +++ b/api/schemas/User.yaml @@ -5,11 +5,8 @@ properties: format: int64 description: Unique user ID (primary key) example: 1 - avatar_id: - type: integer - format: int64 - description: ID of the user avatar (references images table) - example: null + image: + $ref: '../schemas/Image.yaml' mail: type: string format: email diff --git a/modules/backend/handlers/users.go b/modules/backend/handlers/users.go index 9204eb9..96e7251 100644 --- a/modules/backend/handlers/users.go +++ b/modules/backend/handlers/users.go @@ -27,16 +27,25 @@ import ( // return int32(i), err // } -func mapUser(u sqlc.GetUserByIDRow) oapi.User { +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{ - AvatarId: u.AvatarID, + 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) GetUsersUserId(ctx context.Context, req oapi.GetUsersUserIdRequestObject) (oapi.GetUsersUserIdResponseObject, error) { @@ -44,14 +53,19 @@ func (s Server) GetUsersUserId(ctx context.Context, req oapi.GetUsersUserIdReque if err != nil { return oapi.GetUsersUserId404Response{}, nil } - user, err := s.db.GetUserByID(context.TODO(), int64(userID)) + _user, err := s.db.GetUserByID(context.TODO(), int64(userID)) if err != nil { if err == pgx.ErrNoRows { return oapi.GetUsersUserId404Response{}, nil } return nil, err } - return oapi.GetUsersUserId200JSONResponse(mapUser(user)), nil + user, err := mapUser(_user) + if err != nil { + log.Errorf("%v", err) + return oapi.GetUsersUserId500Response{}, err + } + return oapi.GetUsersUserId200JSONResponse(user), nil } func sqlDate2oapi(p_date pgtype.Timestamptz) *time.Time { @@ -327,7 +341,7 @@ func (s Server) UpdateUser(ctx context.Context, request oapi.UpdateUserRequestOb } oapi_user := oapi.User{ // maybe its possible to make one sqlc type and use one map func iinstead of this shit - AvatarId: user.AvatarID, + // AvatarId: user.AvatarID, CreationDate: &user.CreationDate, DispName: user.DispName, Id: &user.ID, diff --git a/modules/backend/queries.sql b/modules/backend/queries.sql index 87d7755..90484db 100644 --- a/modules/backend/queries.sql +++ b/modules/backend/queries.sql @@ -9,9 +9,19 @@ VALUES ($1, $2) RETURNING id, storage_type, image_path; -- name: GetUserByID :one -SELECT id, avatar_id, mail, nickname, disp_name, user_desc, creation_date -FROM users -WHERE id = $1; +SELECT + t.id as id, + t.avatar_id as avatar_id, + t.mail as mail, + t.nickname as nickname, + t.disp_name as disp_name, + t.user_desc as user_desc, + t.creation_date as creation_date, + i.storage_type as storage_type, + i.image_path as image_path +FROM users as t +LEFT JOIN images as i ON (t.avatar_id = i.id) +WHERE id = sqlc.arg('id')::bigint; -- name: GetStudioByID :one diff --git a/sql/queries.sql.go b/sql/queries.sql.go index 5236f1f..d88a041 100644 --- a/sql/queries.sql.go +++ b/sql/queries.sql.go @@ -224,19 +224,31 @@ func (q *Queries) GetTitleTags(ctx context.Context, titleID int64) ([]json.RawMe } const getUserByID = `-- name: GetUserByID :one -SELECT id, avatar_id, mail, nickname, disp_name, user_desc, creation_date -FROM users -WHERE id = $1 +SELECT + t.id as id, + t.avatar_id as avatar_id, + t.mail as mail, + t.nickname as nickname, + t.disp_name as disp_name, + t.user_desc as user_desc, + t.creation_date as creation_date, + i.storage_type as storage_type, + i.image_path as image_path +FROM users as t +LEFT JOIN images as i ON (t.avatar_id = i.id) +WHERE id = $1::bigint ` type GetUserByIDRow struct { - ID int64 `json:"id"` - AvatarID *int64 `json:"avatar_id"` - Mail *string `json:"mail"` - Nickname string `json:"nickname"` - DispName *string `json:"disp_name"` - UserDesc *string `json:"user_desc"` - CreationDate time.Time `json:"creation_date"` + ID int64 `json:"id"` + AvatarID *int64 `json:"avatar_id"` + Mail *string `json:"mail"` + Nickname string `json:"nickname"` + DispName *string `json:"disp_name"` + UserDesc *string `json:"user_desc"` + CreationDate time.Time `json:"creation_date"` + StorageType *StorageTypeT `json:"storage_type"` + ImagePath *string `json:"image_path"` } func (q *Queries) GetUserByID(ctx context.Context, id int64) (GetUserByIDRow, error) { @@ -250,6 +262,8 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (GetUserByIDRow, er &i.DispName, &i.UserDesc, &i.CreationDate, + &i.StorageType, + &i.ImagePath, ) return i, err }