Feat: finish putImageAliases
This commit is contained in:
9
Makefile
9
Makefile
@@ -1,6 +1,7 @@
|
|||||||
.PHONY: all swagger docker install test bench
|
.PHONY: all swagger docker install test
|
||||||
|
|
||||||
SWAG ?= go run github.com/swaggo/swag/cmd/swag@v1.16.4
|
SWAG ?= go run github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||||
|
DOCKER ?= docker
|
||||||
|
|
||||||
GO_ENV += CGO_ENABLED=0
|
GO_ENV += CGO_ENABLED=0
|
||||||
SOURCE := $(shell find . -type f -name '*.go')
|
SOURCE := $(shell find . -type f -name '*.go')
|
||||||
@@ -9,7 +10,7 @@ TARGET := backend
|
|||||||
all: swagger docker
|
all: swagger docker
|
||||||
|
|
||||||
docker: $(TARGET)
|
docker: $(TARGET)
|
||||||
docker compose up -d --force-recreate --build backend
|
$(DOCKER) compose up -d --force-recreate --build backend
|
||||||
|
|
||||||
install:
|
install:
|
||||||
$(GO_ENV) go install
|
$(GO_ENV) go install
|
||||||
@@ -17,6 +18,10 @@ install:
|
|||||||
test:
|
test:
|
||||||
go test -v ./tests -count=1
|
go test -v ./tests -count=1
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
$(DOCKER) compose exec postgres psql \
|
||||||
|
postgres://go2025:go2025@postgres:5432/go2025?sslmode=disable
|
||||||
|
|
||||||
swagger:
|
swagger:
|
||||||
$(SWAG) fmt
|
$(SWAG) fmt
|
||||||
$(SWAG) init -o docs -g cmds/serve.go -pdl 1
|
$(SWAG) init -o docs -g cmds/serve.go -pdl 1
|
||||||
|
|||||||
@@ -268,10 +268,10 @@ const docTemplate = `{
|
|||||||
"api.getImagesOutputImage": {
|
"api.getImagesOutputImage": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"aliases": {
|
"aliasesIds": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
|
|||||||
@@ -260,10 +260,10 @@
|
|||||||
"api.getImagesOutputImage": {
|
"api.getImagesOutputImage": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"aliases": {
|
"aliasesIds": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
api.getImagesOutputImage:
|
api.getImagesOutputImage:
|
||||||
properties:
|
properties:
|
||||||
aliases:
|
aliasesIds:
|
||||||
items:
|
items:
|
||||||
type: string
|
type: integer
|
||||||
type: array
|
type: array
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ import (
|
|||||||
func (self *Handlers) DeleteAlias(
|
func (self *Handlers) DeleteAlias(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
|
// TODO
|
||||||
return utils.Success(w)
|
return utils.Success(w)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ import (
|
|||||||
func (self *Handlers) DeleteImage(
|
func (self *Handlers) DeleteImage(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
|
// TODO
|
||||||
return utils.Success(w)
|
return utils.Success(w)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ import (
|
|||||||
func (self *Handlers) GetAliasUpdate(
|
func (self *Handlers) GetAliasUpdate(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
|
// TODO
|
||||||
return utils.Success(w)
|
return utils.Success(w)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ type getAliasesOutputAlias struct {
|
|||||||
func (self *Handlers) GetAliases(
|
func (self *Handlers) GetAliases(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
// mock output
|
|
||||||
ctx := req.Context()
|
ctx := req.Context()
|
||||||
|
|
||||||
aliases, err := self.db.GetAliases(ctx)
|
aliases, err := self.db.GetAliases(ctx)
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.konchin.com/go2025/backend/middlewares"
|
"gitea.konchin.com/go2025/backend/middlewares"
|
||||||
"gitea.konchin.com/go2025/backend/utils"
|
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ type getImagesOutputImage struct {
|
|||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Uploader string `json:"uploadedUserId"`
|
Uploader string `json:"uploadedUserId"`
|
||||||
UploadTS int64 `json:"uploadedAt"`
|
UploadTS int64 `json:"uploadedAt"`
|
||||||
Aliases []string `json:"aliases"`
|
AliasesIds []int64 `json:"aliasesIds"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetImages
|
// GetImages
|
||||||
@@ -27,18 +27,63 @@ type getImagesOutputImage struct {
|
|||||||
func (self *Handlers) GetImages(
|
func (self *Handlers) GetImages(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
// ctx := req.Context()
|
ctx := req.Context()
|
||||||
|
|
||||||
images := strings.Split(req.Param("images"), ",")
|
rawReqImages := strings.Split(req.Param("images"), ",")
|
||||||
aliases := strings.Split(req.Param("aliases"), ",")
|
rawReqAliases := strings.Split(req.Param("aliases"), ",")
|
||||||
|
|
||||||
if (len(images) == 0 && len(aliases) == 0) ||
|
if (len(rawReqImages) == 0 && len(rawReqAliases) == 0) ||
|
||||||
(len(images) > 0 && len(aliases) > 0) {
|
(len(rawReqImages) > 0 && len(rawReqAliases) > 0) {
|
||||||
return middlewares.HTTPError{
|
return middlewares.HTTPError{
|
||||||
StatusCode: http.StatusBadRequest,
|
StatusCode: http.StatusBadRequest,
|
||||||
Message: "images and aliases should exist exactly one",
|
Message: "images and aliases should exist exactly one",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils.Success(w)
|
var reqImages, reqAliases []int64
|
||||||
|
for _, img := range rawReqImages {
|
||||||
|
imgId, err := strconv.ParseInt(img, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "failed to parse image ids",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reqImages = append(reqImages, imgId)
|
||||||
|
}
|
||||||
|
for _, ali := range rawReqAliases {
|
||||||
|
aliId, err := strconv.ParseInt(ali, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "failed to parse alias ids",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reqAliases = append(reqAliases, aliId)
|
||||||
|
}
|
||||||
|
|
||||||
|
images, err := self.db.GetImages(ctx, reqImages, reqAliases)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
Message: "failed to get images",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var output []getImagesOutputImage
|
||||||
|
for _, img := range images {
|
||||||
|
var aliases []int64
|
||||||
|
for _, alias := range img.Aliases {
|
||||||
|
aliases = append(aliases, alias.Id)
|
||||||
|
}
|
||||||
|
output = append(output, getImagesOutputImage{
|
||||||
|
Id: img.Id,
|
||||||
|
Uploader: img.Uploader,
|
||||||
|
UploadTS: img.UploadTS.Unix(),
|
||||||
|
AliasesIds: aliases,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return bunrouter.JSON(w, output)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/middlewares"
|
||||||
"gitea.konchin.com/go2025/backend/utils"
|
"gitea.konchin.com/go2025/backend/utils"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
@@ -19,5 +21,14 @@ import (
|
|||||||
func (self *Handlers) PostImage(
|
func (self *Handlers) PostImage(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
|
typeHeader := strings.Split(req.Header.Get("Content-Type"), "/")
|
||||||
|
if len(typeHeader) != 2 || typeHeader[0] != "image" {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "incorrect 'Content-Type' header",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
return utils.Success(w)
|
return utils.Success(w)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/middlewares"
|
||||||
"gitea.konchin.com/go2025/backend/utils"
|
"gitea.konchin.com/go2025/backend/utils"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
type putImageAliasesInputAlias = string
|
type putImageAliasesInput struct {
|
||||||
|
Aliases []string `json:"aliases"`
|
||||||
|
}
|
||||||
|
|
||||||
// PutImageAliases
|
// PutImageAliases
|
||||||
//
|
//
|
||||||
// @param id path int64 true "Image Id"
|
// @param id path int64 true "Image Id"
|
||||||
// @param payload body []putImageAliasesInputAlias true "Payload"
|
// @param payload body putImageAliasesInput true "Payload"
|
||||||
// @success 200
|
// @success 200
|
||||||
// @failure 401
|
// @failure 401
|
||||||
// @failure 404
|
// @failure 404
|
||||||
@@ -20,5 +25,43 @@ type putImageAliasesInputAlias = string
|
|||||||
func (self *Handlers) PutImageAliases(
|
func (self *Handlers) PutImageAliases(
|
||||||
w http.ResponseWriter, req bunrouter.Request,
|
w http.ResponseWriter, req bunrouter.Request,
|
||||||
) error {
|
) error {
|
||||||
|
ctx := req.Context()
|
||||||
|
|
||||||
|
imgId, err := req.Params().Int64("id")
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "falied to parse image id in path",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "failed to read payload",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var input putImageAliasesInput
|
||||||
|
if err := json.Unmarshal(b, &input); err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "failed to unmarshal json",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = self.db.UpdateAliases(ctx, imgId, input.Aliases)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
Message: "failed to update aliases",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return utils.Success(w)
|
return utils.Success(w)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,19 +42,21 @@ func (self *BunDatabase) UpdateRefreshToken(
|
|||||||
ret := models.Session{
|
ret := models.Session{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := self.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||||
err := self.db.NewSelect().
|
err := self.db.NewSelect().
|
||||||
Model(&ret).
|
Model(&ret).
|
||||||
WherePK().
|
WherePK().
|
||||||
Scan(ctx)
|
Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Session{}, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ret.RotateRefreshToken(); err != nil {
|
if err := ret.RotateRefreshToken(); err != nil {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.Ctx(ctx).
|
||||||
Error("failed to rotate refresh token",
|
Error("failed to rotate refresh token",
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
return models.Session{}, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = self.db.NewUpdate().
|
_, err = self.db.NewUpdate().
|
||||||
@@ -62,6 +64,12 @@ func (self *BunDatabase) UpdateRefreshToken(
|
|||||||
Set("refresh_token = ?", ret.RefreshToken).
|
Set("refresh_token = ?", ret.RefreshToken).
|
||||||
Where("user_id = ?", userId).
|
Where("user_id = ?", userId).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Session{}, err
|
return models.Session{}, err
|
||||||
}
|
}
|
||||||
@@ -104,3 +112,75 @@ func (self *BunDatabase) GetAliases(
|
|||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *BunDatabase) GetImages(
|
||||||
|
ctx context.Context,
|
||||||
|
imageIds []int64,
|
||||||
|
aliasIds []int64,
|
||||||
|
) ([]models.Image, error) {
|
||||||
|
if len(aliasIds) > 0 {
|
||||||
|
var rels []models.AliasImage
|
||||||
|
err := self.db.NewSelect().
|
||||||
|
Model(&rels).
|
||||||
|
Where("alias_id IN (?)", bun.In(aliasIds)).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return []models.Image{}, err
|
||||||
|
}
|
||||||
|
for _, rel := range rels {
|
||||||
|
imageIds = append(imageIds, rel.ImageId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret []models.Image
|
||||||
|
if len(imageIds) > 0 {
|
||||||
|
err := self.db.NewSelect().
|
||||||
|
Model(&ret).
|
||||||
|
Where("image_id IN (?)", bun.In(imageIds)).
|
||||||
|
Relation("Aliases").
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return []models.Image{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BunDatabase) UpdateAliases(
|
||||||
|
ctx context.Context,
|
||||||
|
imageId int64,
|
||||||
|
aliasNames []string,
|
||||||
|
) error {
|
||||||
|
return self.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
var aliases []models.Alias
|
||||||
|
for _, ali := range aliasNames {
|
||||||
|
aliases = append(aliases, models.Alias{
|
||||||
|
Name: ali,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err := self.db.NewInsert().
|
||||||
|
Model(&aliases).
|
||||||
|
On("CONFLICT (id) DO NOTHING").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rels []models.AliasImage
|
||||||
|
for _, alias := range aliases {
|
||||||
|
rels = append(rels, models.AliasImage{
|
||||||
|
AliasId: alias.Id,
|
||||||
|
ImageId: imageId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err = self.db.NewInsert().
|
||||||
|
Model(&rels).
|
||||||
|
On("CONFLICT (alias_id, image_id) DO NOTHING").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,4 +25,16 @@ type Database interface {
|
|||||||
GetAliases(
|
GetAliases(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) ([]models.Alias, error)
|
) ([]models.Alias, error)
|
||||||
|
|
||||||
|
GetImages(
|
||||||
|
ctx context.Context,
|
||||||
|
imageIds []int64,
|
||||||
|
aliasIds []int64,
|
||||||
|
) ([]models.Image, error)
|
||||||
|
|
||||||
|
UpdateAliases(
|
||||||
|
ctx context.Context,
|
||||||
|
imageId int64,
|
||||||
|
aliasNames []string,
|
||||||
|
) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ type Image struct {
|
|||||||
Id int64 `bun:"id,pk,autoincrement"`
|
Id int64 `bun:"id,pk,autoincrement"`
|
||||||
Uploader string `bun:"uploader"`
|
Uploader string `bun:"uploader"`
|
||||||
UploadTS time.Time `bun:"upload_timestamp"`
|
UploadTS time.Time `bun:"upload_timestamp"`
|
||||||
|
|
||||||
|
Aliases []Alias `bun:"m2m:alias_image,join:Image=Alias"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func InitDB(ctx context.Context, db *bun.DB) error {
|
func InitDB(ctx context.Context, db *bun.DB) error {
|
||||||
db.RegisterModel((*models.AliasImage)(nil))
|
|
||||||
return db.ResetModel(ctx,
|
return db.ResetModel(ctx,
|
||||||
|
(*models.AliasImage)(nil),
|
||||||
(*models.Alias)(nil),
|
(*models.Alias)(nil),
|
||||||
(*models.Image)(nil),
|
(*models.Image)(nil),
|
||||||
(*models.Session)(nil),
|
(*models.Session)(nil),
|
||||||
|
|||||||
Reference in New Issue
Block a user