Added completed user. Some fixes and more more more...

This commit is contained in:
KamilM1205 2026-01-02 22:56:25 +04:00
parent b96dd39795
commit ea8ab7c0ed
33 changed files with 576 additions and 212 deletions

View file

@ -23,6 +23,7 @@ import (
"58team_blog/internal/utils" "58team_blog/internal/utils"
"log" "log"
"github.com/gin-contrib/cors"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie" "github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -37,12 +38,29 @@ const secret = "58secret"
func main() { func main() {
router := gin.Default() router := gin.Default()
// Setup CORS
// IMPORTANT: Must be setup before all another middlewares.
//cors_config := cors.DefaultConfig()
//cors_config.AllowOrigins = []string{"http://localhost:8080", "http://localhost:5173"}
//cors_config.AllowAllOrigins = true
//cors_config.AllowHeaders = []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With"}
//router.Use(cors.New(cors_config))
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:5173"},
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"*"}, // Пробуем разрешить все заголовки
ExposeHeaders: []string{},
AllowCredentials: true,
MaxAge: 12 * 60 * 60,
}))
// Setup cookie container // Setup cookie container
router.Use(sessions.Sessions("session", cookie.NewStore([]byte(secret)))) router.Use(sessions.Sessions("session", cookie.NewStore([]byte(secret))))
// Register custom validators // Register custom validators
if v, ok := binding.Validator.Engine().(*validator.Validate); ok { if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("password", utils.PasswordValidator) v.RegisterValidation("password", utils.PasswordValidator)
v.RegisterValidation("inlineList", utils.InlineListValidator)
} }
// Swagger setup // Swagger setup
@ -78,6 +96,7 @@ func main() {
imagesService := services.CreateImagesService(&imagesRepository) imagesService := services.CreateImagesService(&imagesRepository)
interfaces.BindPostAdmin(&postService, &userService, g) interfaces.BindPostAdmin(&postService, &userService, g)
interfaces.BindPost(&postService, &userService, g)
interfaces.BindUser(config.AdminName, config.AdminPassword, &userService, g) interfaces.BindUser(config.AdminName, config.AdminPassword, &userService, g)
interfaces.BindImages(config.ImagesPath, &imagesService, g) interfaces.BindImages(config.ImagesPath, &imagesService, g)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -29,16 +29,57 @@ definitions:
type: object type: object
requests.CreateUserRequest: requests.CreateUserRequest:
properties: properties:
avatar:
type: string
description:
maxLength: 500
minLength: 10
type: string
joinDate:
type: string
motto:
maxLength: 200
minLength: 2
type: string
name:
maxLength: 100
minLength: 2
type: string
password: password:
maxLength: 32 maxLength: 32
minLength: 6 minLength: 6
type: string type: string
projects:
items:
type: string
minItems: 1
type: array
role:
type: string
skills:
items:
type: string
minItems: 1
type: array
speciality:
maxLength: 100
minLength: 2
type: string
username: username:
maxLength: 32 maxLength: 32
minLength: 3 minLength: 3
type: string type: string
required: required:
- avatar
- description
- joinDate
- motto
- name
- password - password
- projects
- role
- skills
- speciality
- username - username
type: object type: object
requests.LoginUserRequest: requests.LoginUserRequest:
@ -66,16 +107,54 @@ definitions:
type: object type: object
requests.PutUserRequest: requests.PutUserRequest:
properties: properties:
avatar:
type: string
description:
maxLength: 500
minLength: 10
type: string
motto:
maxLength: 200
minLength: 2
type: string
name:
maxLength: 100
minLength: 2
type: string
password: password:
maxLength: 32 maxLength: 32
minLength: 6 minLength: 6
type: string type: string
projects:
items:
type: string
minItems: 1
type: array
role:
type: string
skills:
items:
type: string
minItems: 1
type: array
speciality:
maxLength: 100
minLength: 2
type: string
username: username:
maxLength: 32 maxLength: 32
minLength: 3 minLength: 3
type: string type: string
required: required:
- avatar
- description
- motto
- name
- password - password
- projects
- role
- skills
- speciality
- username - username
type: object type: object
responses.ErrorResponse: responses.ErrorResponse:
@ -85,6 +164,8 @@ definitions:
message: message:
type: string type: string
type: object type: object
responses.GetAllImagesList:
type: object
responses.GetListPostResponseItem: responses.GetListPostResponseItem:
properties: properties:
category: category:
@ -140,8 +221,30 @@ definitions:
type: object type: object
responses.UserResponse: responses.UserResponse:
properties: properties:
avatar:
type: string
description:
type: string
id: id:
type: string type: string
joinDate:
type: string
motto:
type: string
name:
type: string
projects:
items:
type: string
type: array
role:
type: string
skills:
items:
type: string
type: array
speciality:
type: string
username: username:
type: string type: string
type: object type: object
@ -159,6 +262,38 @@ info:
title: 58team blog backend title: 58team blog backend
version: "1.0" version: "1.0"
paths: paths:
/images:
get:
description: Delete image from server by given path
parameters:
- description: Path to image
in: path
name: filename
required: true
type: string
produces:
- image/png
- image/jpeg
responses:
"200":
description: OK
schema:
$ref: '#/definitions/responses.GetAllImagesList'
"400":
description: Bad Request
schema:
$ref: '#/definitions/responses.ErrorResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/responses.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/responses.ErrorResponse'
summary: Delete image by path
tags:
- images
/images/: /images/:
post: post:
description: Upload new image and returns uploaded image json object description: Upload new image and returns uploaded image json object
@ -179,6 +314,8 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Upload new image summary: Upload new image
tags: tags:
- images - images
@ -209,6 +346,8 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Delete image by path summary: Delete image by path
tags: tags:
- images - images
@ -291,9 +430,34 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Create new user summary: Create new user
tags: tags:
- user - user
/members/:
get:
description: Return all registered users
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/responses.UserResponse'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/responses.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/responses.ErrorResponse'
summary: Get all users
tags:
- user
/post: /post:
get: get:
description: Return first 5 posts description: Return first 5 posts
@ -339,6 +503,8 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Create new post summary: Create new post
tags: tags:
- post - post
@ -368,6 +534,8 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Delete post summary: Delete post
tags: tags:
- post - post
@ -436,6 +604,8 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Update post content summary: Update post content
tags: tags:
- post - post
@ -468,29 +638,7 @@ paths:
summary: Get posts after offset summary: Get posts after offset
tags: tags:
- post - post
/user/: /team/:
get:
description: Return all registered users
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/responses.UserResponse'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/responses.ErrorResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/responses.ErrorResponse'
summary: Get all users
tags:
- user
post: post:
consumes: consumes:
- application/json - application/json
@ -521,10 +669,12 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Create new user summary: Create new user
tags: tags:
- user - user
/user/{id}: /team/{id}:
delete: delete:
description: Delete user description: Delete user
parameters: parameters:
@ -550,6 +700,8 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Delete user summary: Delete user
tags: tags:
- user - user
@ -620,10 +772,12 @@ paths:
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/responses.ErrorResponse' $ref: '#/definitions/responses.ErrorResponse'
security:
- BasicAuth: []
summary: Change user summary: Change user
tags: tags:
- user - user
/user/name/{name}: /team/name/{name}:
get: get:
consumes: consumes:
- application/json - application/json

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 60 KiB

1
go.mod
View file

@ -14,6 +14,7 @@ require (
github.com/creasty/defaults v1.8.0 // indirect github.com/creasty/defaults v1.8.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect
github.com/gin-contrib/cors v1.7.6 // indirect
github.com/gin-contrib/sessions v1.0.4 // indirect github.com/gin-contrib/sessions v1.0.4 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/gin-gonic/gin v1.10.1 // indirect github.com/gin-gonic/gin v1.10.1 // indirect

2
go.sum
View file

@ -26,6 +26,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U= github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs= github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=

View file

@ -1,6 +1,17 @@
package commands package commands
import "time"
type CreateUserCommand struct { type CreateUserCommand struct {
Username string Username string
Password string Password string
Name string
Role string
Speciality string
Description string
Skills string
Avatar string
JoinDate time.Time
Projects string
Motto string
} }

View file

@ -6,4 +6,12 @@ type UpdateUserCommand struct {
Id uuid.UUID Id uuid.UUID
Username string Username string
Password string Password string
Name string
Role string
Speciality string
Description string
Skills string
Avatar string
Projects string
Motto string
} }

View file

@ -1,11 +1,24 @@
package common package common
import "github.com/google/uuid" import (
"time"
"github.com/google/uuid"
)
type UserResult struct { type UserResult struct {
Id uuid.UUID Id uuid.UUID
UserName string UserName string
Password string Password string
Name string
Role string
Speciality string
Description string
Skills string
Avatar string
JoinDate time.Time
Projects string
Motto string
} }
type UserResultList struct { type UserResultList struct {

View file

@ -11,6 +11,15 @@ func CreateUserResultFromEntity(entity *entities.User) *common.UserResult {
Id: entity.Id, Id: entity.Id,
UserName: entity.UserName, UserName: entity.UserName,
Password: entity.Password, Password: entity.Password,
Name: entity.Name,
Role: entity.Role,
Speciality: entity.Speciality,
Description: entity.Description,
Skills: entity.Skills,
Avatar: entity.Avatar,
JoinDate: entity.JoinDate,
Projects: entity.Projects,
Motto: entity.Motto,
} }
} }

View file

@ -8,6 +8,7 @@ import (
"58team_blog/internal/application/queries" "58team_blog/internal/application/queries"
"58team_blog/internal/domain/entities" "58team_blog/internal/domain/entities"
"58team_blog/internal/domain/repository" "58team_blog/internal/domain/repository"
"fmt"
) )
type UserService struct { type UserService struct {
@ -29,12 +30,15 @@ func (s *UserService) Create(cmd commands.CreateUserCommand) (*common.UserResult
} }
if user != nil { if user != nil {
return nil, errors.NewAlreadyExistsError("user: " + cmd.Username) fmt.Println(user)
return nil, errors.NewAlreadyExistsError("user: " + user.UserName)
} }
} }
// Create new user // Create new user
user, err := entities.CreateUser(cmd.Username, cmd.Password) user, err := entities.CreateUser(cmd.Username, cmd.Password,
cmd.Name, cmd.Role, cmd.Speciality, cmd.Description,
cmd.Skills, cmd.Avatar, cmd.JoinDate, cmd.Projects, cmd.Motto)
if err != nil { if err != nil {
return nil, errors.NewValidationError(err.Error()) return nil, errors.NewValidationError(err.Error())
} }
@ -70,6 +74,10 @@ func (s *UserService) FindByName(query queries.UserFindByNameQuery) (*queries.Us
return nil, errors.NewDBError(err.Error()) return nil, errors.NewDBError(err.Error())
} }
if entity == nil {
return nil, errors.NewNotFoundError("User not found.")
}
if err := entity.Validate(); err != nil { if err := entity.Validate(); err != nil {
return nil, errors.NewValidationError(err.Error()) return nil, errors.NewValidationError(err.Error())
} }
@ -109,6 +117,14 @@ func (s *UserService) Update(cmd commands.UpdateUserCommand) (*common.UserResult
} }
entity.Password = cmd.Password entity.Password = cmd.Password
entity.Name = cmd.Name
entity.Role = cmd.Role
entity.Speciality = cmd.Speciality
entity.Description = cmd.Description
entity.Skills = cmd.Skills
entity.Avatar = cmd.Avatar
entity.Projects = cmd.Projects
entity.Motto = cmd.Motto
if err := entity.Validate(); err != nil { if err := entity.Validate(); err != nil {
return nil, errors.NewValidationError(err.Error()) return nil, errors.NewValidationError(err.Error())

View file

@ -2,6 +2,7 @@ package entities
import ( import (
"errors" "errors"
"time"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -12,13 +13,44 @@ type User struct {
Id uuid.UUID `db:"id"` Id uuid.UUID `db:"id"`
UserName string `db:"username"` UserName string `db:"username"`
Password string `db:"password"` Password string `db:"password"`
Name string `db:"name"`
Role string `db:"role"`
Speciality string `db:"speciality"`
Description string `db:"description"`
Skills string `db:"skills"`
Avatar string `db:"avatar"`
JoinDate time.Time `db:"joindate"`
Projects string `db:"projects"`
Motto string `db:"motto"`
} }
func CreateUser(userName string, password string) (user User, err error) { func CreateUser(
userName string,
password string,
name string,
role string,
speciality string,
description string,
skills string,
avatar string,
joinDate time.Time,
projects string,
motto string,
) (user User, err error) {
user = User{ user = User{
Id: uuid.New(), Id: uuid.New(),
UserName: userName, UserName: userName,
Password: password, Password: password,
Name: name,
Role: role,
Speciality: speciality,
Description: description,
Skills: skills,
Avatar: avatar,
JoinDate: joinDate,
Projects: projects,
Motto: motto,
} }
err = user.Validate() err = user.Validate()
@ -28,14 +60,51 @@ func CreateUser(userName string, password string) (user User, err error) {
func (u *User) Validate() error { func (u *User) Validate() error {
if err := uuid.Validate(u.Id.String()); err != nil { if err := uuid.Validate(u.Id.String()); err != nil {
return errors.New("Invalid user.id") return errors.New("invalid user.id")
} }
if u.UserName == "" { if u.UserName == "" {
return errors.New("Empty user.name") return errors.New("empty user.username")
} }
if u.Password == "" { if u.Password == "" {
return errors.New("Empty user.password") return errors.New("empty user.password")
}
if u.Role == "" {
return errors.New("empty user.role")
}
if u.Name == "" {
return errors.New("empty user.name")
}
if u.Speciality == "" {
return errors.New("empty user.speciality")
}
if u.Description == "" {
return errors.New("empty user.description")
}
if u.Skills == "" {
return errors.New("empty user.skills")
}
if u.Avatar == "" {
return errors.New("empty user.avatar")
}
if u.Projects == "" {
return errors.New("empty user.projects")
}
if u.Motto == "" {
return errors.New("empty user.motto")
}
if u.JoinDate.IsZero() {
return errors.New("empty user.joinDate")
} }
return nil return nil

View file

@ -20,7 +20,9 @@ func CreateUserRepository(conn *db.Database) UserRepository {
} }
func (r *UserRepository) Create(entity *entities.User) (*entities.User, error) { func (r *UserRepository) Create(entity *entities.User) (*entities.User, error) {
query := "INSERT INTO " + entities.UserTable + "(id, username, password) VALUES (:id, :username, :password)" query := "INSERT INTO " + entities.UserTable +
"(id, username, password, name, role, speciality, description, skills, avatar, joindate, projects, motto) " +
"VALUES (:id, :username, :password, :name, :role, :speciality, :description, :skills, :avatar, :joindate, :projects, :motto)"
_, err := r.conn.Conn.NamedExec(query, entity) _, err := r.conn.Conn.NamedExec(query, entity)
if err != nil { if err != nil {

View file

@ -36,6 +36,7 @@ func CreateImagesController(images_path string, service *services.ImagesService)
// @Success 200 {object} responses.ImageResponse // @Success 200 {object} responses.ImageResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /images/ [post] // @Router /images/ [post]
// @Security BasicAuth
func (r *ImagesController) PostImage(c *gin.Context) { func (r *ImagesController) PostImage(c *gin.Context) {
file, err := c.FormFile("file") file, err := c.FormFile("file")
if err != nil { if err != nil {
@ -139,6 +140,21 @@ func (r *ImagesController) GetImage(c *gin.Context) {
c.File(filePath) c.File(filePath)
} }
// @Summary Delete image by path
// @Description Delete image from server by given path
// @Tags images
// @Param filename path string true "Path to image"
// @Produce image/png
// @Produce image/jpeg
// @Success 200 {object} responses.GetAllImagesList
// @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /images [get]
func (r *ImagesController) GetAllImage(c *gin.Context) {
panic("Not implemented")
}
// @Summary Delete image by path // @Summary Delete image by path
// @Description Delete image from server by given path // @Description Delete image from server by given path
// @Tags images // @Tags images
@ -150,6 +166,7 @@ func (r *ImagesController) GetImage(c *gin.Context) {
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /images/{path} [delete] // @Router /images/{path} [delete]
// @Security BasicAuth
func (r *ImagesController) DeleteImage(c *gin.Context) { func (r *ImagesController) DeleteImage(c *gin.Context) {
panic("Not implemented") panic("Not implemented")
} }

View file

@ -40,6 +40,8 @@ func CreatePostController(service *services.PostService, userService *services.U
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /post [post] // @Router /post [post]
//
// @Security BasicAuth
func (r *PostController) Post(c *gin.Context) { func (r *PostController) Post(c *gin.Context) {
var request requests.CreatePostRequest var request requests.CreatePostRequest
@ -227,6 +229,8 @@ func (r *PostController) GetById(c *gin.Context) {
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /post/{id} [put] // @Router /post/{id} [put]
//
// @Security BasicAuth
func (r *PostController) Put(c *gin.Context) { func (r *PostController) Put(c *gin.Context) {
var request requests.PutPostRequest var request requests.PutPostRequest
@ -276,6 +280,8 @@ func (r *PostController) Put(c *gin.Context) {
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /post/{id} [delete] // @Router /post/{id} [delete]
//
// @Security BasicAuth
func (r *PostController) Delete(c *gin.Context) { func (r *PostController) Delete(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
id_valid, err := uuid.Parse(id) id_valid, err := uuid.Parse(id)

View file

@ -10,6 +10,7 @@ import (
"58team_blog/internal/utils" "58team_blog/internal/utils"
"log" "log"
"net/http" "net/http"
"strings"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -107,6 +108,7 @@ func (r *UserController) Login(c *gin.Context) {
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /logout [get] // @Router /logout [get]
// @Security BasicAuth
func (r *UserController) Logout(c *gin.Context) { func (r *UserController) Logout(c *gin.Context) {
session := sessions.Default(c) session := sessions.Default(c)
user := session.Get("user") user := session.Get("user")
@ -132,7 +134,8 @@ func (r *UserController) Logout(c *gin.Context) {
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 409 {object} responses.ErrorResponse // @Failure 409 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /user/ [post] // @Router /team/ [post]
// @Security BasicAuth
func (r *UserController) Post(c *gin.Context) { func (r *UserController) Post(c *gin.Context) {
var request requests.CreateUserRequest var request requests.CreateUserRequest
if err := c.BindJSON(&request); err != nil { if err := c.BindJSON(&request); err != nil {
@ -153,6 +156,15 @@ func (r *UserController) Post(c *gin.Context) {
cmd := commands.CreateUserCommand{ cmd := commands.CreateUserCommand{
Username: request.Username, Username: request.Username,
Password: encrypted_password, Password: encrypted_password,
Role: request.Role,
Name: request.Name,
Speciality: request.Speciality,
Description: request.Description,
Skills: strings.Join(request.Skills, ";"),
Avatar: request.Avatar,
JoinDate: request.JoinDate,
Projects: strings.Join(request.Projects, ";"),
Motto: request.Motto,
} }
user, err := r.service.Create(cmd) user, err := r.service.Create(cmd)
@ -177,7 +189,7 @@ func (r *UserController) Post(c *gin.Context) {
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /user/{id} [get] // @Router /team/{id} [get]
func (r *UserController) FindById(c *gin.Context) { func (r *UserController) FindById(c *gin.Context) {
id_path := c.Param("id") id_path := c.Param("id")
@ -214,7 +226,7 @@ func (r *UserController) FindById(c *gin.Context) {
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /user/name/{name} [get] // @Router /team/name/{name} [get]
func (r *UserController) FindByName(c *gin.Context) { func (r *UserController) FindByName(c *gin.Context) {
name := c.Param("name") name := c.Param("name")
@ -230,6 +242,7 @@ func (r *UserController) FindByName(c *gin.Context) {
} }
response := mapper.ResponseFromUserFindByNameResult(user) response := mapper.ResponseFromUserFindByNameResult(user)
c.JSON(http.StatusOK, response) c.JSON(http.StatusOK, response)
} }
@ -240,7 +253,7 @@ func (r *UserController) FindByName(c *gin.Context) {
// @Success 200 {object} responses.UserResponseList // @Success 200 {object} responses.UserResponseList
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /user/ [get] // @Router /members/ [get]
func (r *UserController) GetAll(c *gin.Context) { func (r *UserController) GetAll(c *gin.Context) {
users, err := r.service.GetAll() users, err := r.service.GetAll()
if err != nil { if err != nil {
@ -249,8 +262,9 @@ func (r *UserController) GetAll(c *gin.Context) {
return return
} }
responses := mapper.ResponseFromUserGetAllResult(users) resp := mapper.ResponseFromUserGetAllResult(users)
c.JSON(http.StatusOK, responses)
c.JSON(http.StatusOK, resp)
} }
// @Summary Change user // @Summary Change user
@ -264,7 +278,8 @@ func (r *UserController) GetAll(c *gin.Context) {
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /user/{id} [put] // @Router /team/{id} [put]
// @Security BasicAuth
func (r *UserController) Put(c *gin.Context) { func (r *UserController) Put(c *gin.Context) {
var request requests.PutUserRequest var request requests.PutUserRequest
id_path := c.Param("id") id_path := c.Param("id")
@ -292,10 +307,21 @@ func (r *UserController) Put(c *gin.Context) {
return return
} }
skills := strings.Join(request.Skills, ";")
projects := strings.Join(request.Projects, ";")
cmd := commands.UpdateUserCommand{ cmd := commands.UpdateUserCommand{
Id: id, Id: id,
Username: request.Username, Username: request.Username,
Name: request.Name,
Password: password, Password: password,
Role: request.Role,
Speciality: request.Speciality,
Description: request.Description,
Skills: skills,
Avatar: request.Avatar,
Projects: projects,
Motto: request.Motto,
} }
user, err := r.service.Update(cmd) user, err := r.service.Update(cmd)
@ -318,7 +344,8 @@ func (r *UserController) Put(c *gin.Context) {
// @Failure 400 {object} responses.ErrorResponse // @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse // @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse // @Failure 500 {object} responses.ErrorResponse
// @Router /user/{id} [delete] // @Router /team/{id} [delete]
// @Security BasicAuth
func (r *UserController) Delete(c *gin.Context) { func (r *UserController) Delete(c *gin.Context) {
id_path := c.Param("id") id_path := c.Param("id")

View file

@ -12,14 +12,14 @@ func itemFromResult(item *common.PostResult) responses.GetListPostResponseItem {
UserId: item.UserId.String(), UserId: item.UserId.String(),
Title: item.Title, Title: item.Title,
Description: item.Description, Description: item.Description,
UpdatedAt: item.UpdatedAt.String(), UpdatedAt: item.UpdatedAt,
Tags: item.Tags, Tags: item.Tags,
Category: item.Category, Category: item.Category,
} }
} }
func ResponseFromPostGetAllResult(result *queries.PostGetAllResult) responses.GetListPostResponse { func ResponseFromPostGetAllResult(result *queries.PostGetAllResult) responses.GetListPostResponse {
var resp []responses.GetListPostResponseItem resp := responses.GetListPostResponse{}
for _, r := range result.Result.Result { for _, r := range result.Result.Result {
resp = append(resp, itemFromResult(r)) resp = append(resp, itemFromResult(r))

View file

@ -3,11 +3,25 @@ package mapper
import ( import (
"58team_blog/internal/application/queries" "58team_blog/internal/application/queries"
"58team_blog/internal/interfaces/api/responses" "58team_blog/internal/interfaces/api/responses"
"strings"
) )
func ResponseFromUserFindByIdResult(user *queries.UserFindByIdResult) responses.UserResponse { func ResponseFromUserFindByIdResult(user *queries.UserFindByIdResult) responses.UserResponse {
res := user.Result
skills := strings.Split(res.Skills, ";")
projects := strings.Split(res.Projects, ";")
return responses.UserResponse{ return responses.UserResponse{
Id: user.Result.Id.String(), Id: res.Id.String(),
UserName: user.Result.UserName, UserName: res.UserName,
Name: res.Name,
Role: res.Role,
Speciality: res.Speciality,
Description: res.Description,
Skills: skills,
Avatar: res.Avatar,
JoinDate: res.JoinDate,
Projects: projects,
Motto: res.Motto,
} }
} }

View file

@ -3,11 +3,25 @@ package mapper
import ( import (
"58team_blog/internal/application/queries" "58team_blog/internal/application/queries"
"58team_blog/internal/interfaces/api/responses" "58team_blog/internal/interfaces/api/responses"
"strings"
) )
func ResponseFromUserFindByNameResult(result *queries.UserFindByNameResult) responses.UserResponse { func ResponseFromUserFindByNameResult(result *queries.UserFindByNameResult) responses.UserResponse {
res := result.Result
skills := strings.Split(res.Skills, ";")
projects := strings.Split(res.Projects, ";")
return responses.UserResponse{ return responses.UserResponse{
Id: result.Result.Id.String(), Id: res.Id.String(),
UserName: result.Result.UserName, UserName: res.UserName,
Name: res.Name,
Role: res.Role,
Speciality: res.Speciality,
Description: res.Description,
Skills: skills,
Avatar: res.Avatar,
JoinDate: res.JoinDate,
Projects: projects,
Motto: res.Motto,
} }
} }

View file

@ -6,7 +6,7 @@ import (
) )
func ResponseFromUserGetAllResult(result *queries.UserGetAllResult) responses.UserResponseList { func ResponseFromUserGetAllResult(result *queries.UserGetAllResult) responses.UserResponseList {
var list responses.UserResponseList list := responses.UserResponseList{}
for _, i := range result.Result.Result { for _, i := range result.Result.Result {
list = append(list, ResponseFromUserResult(i)) list = append(list, ResponseFromUserResult(i))

View file

@ -3,11 +3,24 @@ package mapper
import ( import (
"58team_blog/internal/application/common" "58team_blog/internal/application/common"
"58team_blog/internal/interfaces/api/responses" "58team_blog/internal/interfaces/api/responses"
"strings"
) )
func ResponseFromUserResult(result *common.UserResult) responses.UserResponse { func ResponseFromUserResult(result *common.UserResult) responses.UserResponse {
skills := strings.Split(result.Skills, ";")
projects := strings.Split(result.Projects, ";")
return responses.UserResponse{ return responses.UserResponse{
Id: result.Id.String(), Id: result.Id.String(),
UserName: result.UserName, UserName: result.UserName,
Name: result.Name,
Role: result.Role,
Speciality: result.Speciality,
Description: result.Description,
Skills: skills,
Avatar: result.Avatar,
JoinDate: result.JoinDate,
Projects: projects,
Motto: result.Motto,
} }
} }

View file

@ -1,10 +1,10 @@
package requests package requests
type CreatePostRequest struct { type CreatePostRequest struct {
Title string `json:"title" validate:"required,min=8,max=255"` Title string `json:"title" binding:"required,min=8,max=255"`
Description string `json:"description" validate:"required,min=8,max=255"` Description string `json:"description" binding:"required,min=8,max=255"`
Content string `json:"content" validate:"required,min=36"` Content string `json:"content" binding:"required,min=36"`
UserId string `json:"userId" validate:"required,uuid5"` UserId string `json:"userId" binding:"required,uuid5"`
Category string `json:"category"` Category string `json:"category"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }

View file

@ -1,6 +1,17 @@
package requests package requests
import "time"
type CreateUserRequest struct { type CreateUserRequest struct {
Username string `json:"username" validate:"required,min=3,max=32"` Username string `json:"username" binding:"required,min=3,max=32"`
Password string `json:"password" validate:"required,min=6,max=32,password"` Password string `json:"password" binding:"required,min=6,max=32,password"`
Name string `json:"name" binding:"required,min=2,max=100"`
Role string `json:"role" binding:"required"`
Speciality string `json:"speciality" binding:"required,min=2,max=100"`
Description string `json:"description" binding:"required,min=10,max=500"`
Skills []string `json:"skills" binding:"required,min=1,inlineList"`
Avatar string `json:"avatar" binding:"required"`
JoinDate time.Time `json:"joinDate" binding:"required"`
Projects []string `json:"projects" binding:"required,min=1,inlineList"`
Motto string `json:"motto" binding:"required,min=2,max=200"`
} }

View file

@ -1,6 +1,6 @@
package requests package requests
type LoginUserRequest struct { type LoginUserRequest struct {
Username string `json:"username" validate:"required,min=3,max=32"` Username string `json:"username" binding:"required,min=3,max=32"`
Password string `json:"password" validate:"required,min=6,max=32,password"` Password string `json:"password" binding:"required,min=6,max=32,password"`
} }

View file

@ -1,6 +1,14 @@
package requests package requests
type PutUserRequest struct { type PutUserRequest struct {
Username string `json:"username" validate:"required,min=3,max=32"` Username string `json:"username" binding:"required,min=3,max=32"`
Password string `json:"password" validate:"required,min=6,max=32,password"` Password string `json:"password" binding:"required,min=6,max=32,password"`
Name string `json:"name" binding:"required,min=2,max=100"`
Role string `json:"role" binding:"required"`
Speciality string `json:"speciality" binding:"required,min=2,max=100"`
Description string `json:"description" binding:"required,min=10,max=500"`
Skills []string `json:"skills" binding:"required,min=1,inlineList"`
Avatar string `json:"avatar" binding:"required"`
Projects []string `json:"projects" binding:"required,min=1,inlineList"`
Motto string `json:"motto" binding:"required,min=2,max=200"`
} }

View file

@ -1,11 +1,13 @@
package responses package responses
import "time"
type GetListPostResponseItem struct { type GetListPostResponseItem struct {
Id string `json:"id"` Id string `json:"id"`
UserId string `json:"userId"` UserId string `json:"userId"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
UpdatedAt string `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
Category string `json:"category"` Category string `json:"category"`
Username string `json:"username"` Username string `json:"username"`

View file

@ -0,0 +1,4 @@
package responses
type GetAllImagesList struct {
}

View file

@ -1,8 +1,18 @@
package responses package responses
import "time"
type UserResponse struct { type UserResponse struct {
Id string `json:"id"` Id string `json:"id"`
UserName string `json:"username"` UserName string `json:"username"`
Name string `json:"name"`
Role string `json:"role"`
Speciality string `json:"speciality"`
Description string `json:"description"`
Skills []string `json:"skills"`
Avatar string `json:"avatar"`
JoinDate time.Time `json:"joinDate"`
Projects []string `json:"projects"`
Motto string `json:"motto"`
} }
type UserResponseList []UserResponse type UserResponseList []UserResponse

View file

@ -34,13 +34,15 @@ func BindUser(adminName string, adminPass string, service *services.UserService,
group.POST("/login", user.Login) group.POST("/login", user.Login)
group.GET("/logout", user.Logout) group.GET("/logout", user.Logout)
g := group.Group("/user/") g_all := group.Group("/members")
g_all.GET("/", user.GetAll)
g_all.GET("/:id", user.FindById)
g_all.GET("/name/:name", user.FindByName)
g := group.Group("/team/")
g.Use(infrastructure.AuthRequired) g.Use(infrastructure.AuthRequired)
g.POST("/", user.Post) g.POST("/", user.Post)
g.GET("/", user.GetAll)
g.GET("/:id", user.FindById)
g.GET("/name/:name", user.FindByName)
g.PUT("/:id", user.Put) g.PUT("/:id", user.Put)
g.DELETE("/:id", user.Delete) g.DELETE("/:id", user.Delete)
} }

View file

@ -14,17 +14,24 @@ func HandleError(err error) responses.ErrorResponse {
log.Println(err) log.Println(err)
if errors.Is(&ie.ValidationError{}, err) { var validationErr *ie.ValidationError
var readFileErr *ie.ReadFileError
var notFoundErr *ie.NotFoundError
var alreadyExistsErr *ie.AlreadyExistsError
var dbErr *ie.DBError
switch {
case errors.As(err, &validationErr):
errorCode = http.StatusBadRequest errorCode = http.StatusBadRequest
} else if errors.Is(&ie.ReadFileError{}, err) { case errors.As(err, &readFileErr):
errorCode = http.StatusInternalServerError errorCode = http.StatusInternalServerError
} else if errors.Is(&ie.NotFoundError{}, err) { case errors.As(err, &notFoundErr):
errorCode = http.StatusNotFound errorCode = http.StatusNotFound
} else if errors.Is(&ie.AlreadyExistsError{}, err) { case errors.As(err, &alreadyExistsErr):
errorCode = http.StatusConflict errorCode = http.StatusConflict
} else if errors.Is(&ie.DBError{}, err) { case errors.As(err, &dbErr):
errorCode = http.StatusInternalServerError errorCode = http.StatusInternalServerError
} else { default:
errorCode = http.StatusInternalServerError errorCode = http.StatusInternalServerError
} }

View file

@ -0,0 +1,21 @@
package utils
import (
"strings"
"github.com/go-playground/validator/v10"
)
func InlineListValidator(fl validator.FieldLevel) bool {
str, ok := fl.Field().Interface().(string)
if ok {
for _, char := range str {
if strings.ContainsRune(";", char) {
return false
}
}
}
return true
}

View file

@ -0,0 +1,10 @@
ALTER TABLE users
ADD COLUMN name TEXT NOT NULL DEFAULT '',
ADD COLUMN role TEXT NOT NULL DEFAULT '',
ADD COLUMN speciality TEXT NOT NULL DEFAULT '',
ADD COLUMN description TEXT NOT NULL DEFAULT '',
ADD COLUMN skills TEXT NOT NULL DEFAULT '',
ADD COLUMN avatar TEXT NOT NULL DEFAULT '',
ADD COLUMN joinDate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN projects TEXT NOT NULL DEFAULT '',
ADD COLUMN motto TEXT NOT NULL DEFAULT '';