Added authorization

This commit is contained in:
KamilM1205 2025-09-25 09:01:00 +04:00
parent c3c3d65d32
commit b96dd39795
50 changed files with 685 additions and 410 deletions

View file

@ -7,4 +7,6 @@ type CreatePostCommand struct {
Title string
Description string
Content string
Category string
Tags []string
}

View file

@ -1,8 +0,0 @@
package commands
import "github.com/google/uuid"
type CreatePostsCommand struct {
PostId uuid.UUID
UserId uuid.UUID
}

View file

@ -1,7 +0,0 @@
package commands
import "github.com/google/uuid"
type DeletePostsCommand struct {
Id uuid.UUID
}

View file

@ -14,6 +14,8 @@ type PostResult struct {
Content string
CreatedAt time.Time
UpdatedAt time.Time
Category string
Tags []string
}
type PostResultList struct {

View file

@ -1,13 +0,0 @@
package common
import "github.com/google/uuid"
type PostsResult struct {
Id uuid.UUID
UserId uuid.UUID
PostId uuid.UUID
}
type PostsResultList struct {
Result []*PostsResult
}

View file

@ -0,0 +1,15 @@
package errors
type ReadFileError struct {
msg string
}
func NewReadFileError(msg string) *ReadFileError {
return &ReadFileError{
msg: msg,
}
}
func (e *ReadFileError) Error() string {
return "Read file error: " + e.msg
}

View file

@ -1,16 +0,0 @@
package interfaces
import (
"58team_blog/internal/application/commands"
"58team_blog/internal/application/common"
"58team_blog/internal/application/queries"
)
type PostsService interface {
Create(commands.CreatePostsCommand) (*common.PostsResult, error)
FindByUserId(queries.PostsFindByUserIdQuery) (*queries.PostsFindByUserIdResult, error)
FindByPostId(queries.PostsFindByPostIdQuery) (*queries.PostsFindByPostIdResult, error)
FindAllByUserId(queries.PostsFindByUserIdQuery) (*queries.PostsFindAllByUserIdResult, error)
GetAll() (queries.PostsGetAllResult, error)
Delete(commands.DeletePostsCommand) error
}

View file

@ -15,6 +15,8 @@ func CreatePostResultFromEntity(entity *entities.Post) *common.PostResult {
Content: entity.Content,
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
Category: entity.Category,
Tags: entity.Tags,
}
}

View file

@ -1,48 +0,0 @@
package mapper
import (
"58team_blog/internal/application/common"
"58team_blog/internal/application/queries"
"58team_blog/internal/domain/entities"
)
func CreatePostsResultFromEntity(entity *entities.Posts) *common.PostsResult {
return &common.PostsResult{
Id: entity.Id,
UserId: entity.UserId,
PostId: entity.PostId,
}
}
func CreatePostsResultListFromEntityList(entity_list []*entities.Posts) *common.PostsResultList {
var result common.PostsResultList
for _, e := range entity_list {
result.Result = append(result.Result, CreatePostsResultFromEntity(e))
}
return &result
}
func CreatePostsFindByUserIdResultFromEntity(entity *entities.Posts) *queries.PostsFindByUserIdResult {
return &queries.PostsFindByUserIdResult{
Result: CreatePostsResultFromEntity(entity),
}
}
func CreatePostsFindByPostIdResultFromEntity(entity *entities.Posts) *queries.PostsFindByPostIdResult {
return &queries.PostsFindByPostIdResult{
Result: CreatePostsResultFromEntity(entity),
}
}
func CreatePostsFindAllByUserIdResultFromEntity(entity_list []*entities.Posts) *queries.PostsFindAllByUserIdResult {
return &queries.PostsFindAllByUserIdResult{
Result: CreatePostsResultListFromEntityList(entity_list),
}
}
func CreatePostsGetAllResultFromEntity(entity_list []*entities.Posts) *queries.PostsGetAllResult {
return &queries.PostsGetAllResult{
Result: CreatePostsResultListFromEntityList(entity_list),
}
}

View file

@ -1,15 +0,0 @@
package queries
import (
"58team_blog/internal/application/common"
"github.com/google/uuid"
)
type PostsFindAllByUserIdQuery struct {
UserId uuid.UUID
}
type PostsFindAllByUserIdResult struct {
Result *common.PostsResultList
}

View file

@ -1,15 +0,0 @@
package queries
import (
"58team_blog/internal/application/common"
"github.com/google/uuid"
)
type PostsFindByPostIdQuery struct {
PostId uuid.UUID
}
type PostsFindByPostIdResult struct {
Result *common.PostsResult
}

View file

@ -1,15 +0,0 @@
package queries
import (
"58team_blog/internal/application/common"
"github.com/google/uuid"
)
type PostsFindByUserIdQuery struct {
UserId uuid.UUID
}
type PostsFindByUserIdResult struct {
Result *common.PostsResult
}

View file

@ -1,7 +0,0 @@
package queries
import "58team_blog/internal/application/common"
type PostsGetAllResult struct {
Result *common.PostsResultList
}

View file

@ -14,7 +14,7 @@ type ImagesService struct {
repo repository.ImagesRepository
}
func NewImagesService(repo repository.ImagesRepository) ImagesService {
func CreateImagesService(repo repository.ImagesRepository) ImagesService {
return ImagesService{
repo: repo,
}

View file

@ -22,7 +22,7 @@ func CreatePostService(repo repository.PostRepository) PostService {
}
func (s *PostService) Create(cmd commands.CreatePostCommand) (*common.PostResult, error) {
entity, err := entities.CreatePost(cmd.UserId, cmd.Title, cmd.Description, cmd.Content)
entity, err := entities.CreatePost(cmd.UserId, cmd.Title, cmd.Description, cmd.Content, cmd.Category, cmd.Tags)
if err != nil {
return nil, errors.NewValidationError("Invalid input data " + err.Error())
}

View file

@ -1,141 +0,0 @@
package services
import (
"58team_blog/internal/application/commands"
"58team_blog/internal/application/common"
"58team_blog/internal/application/mapper"
"58team_blog/internal/application/queries"
"58team_blog/internal/domain/entities"
"58team_blog/internal/domain/repository"
"errors"
"fmt"
)
type PostsService struct {
repo repository.PostsRepository
}
func CreatePostsService(repo repository.PostsRepository) PostsService {
return PostsService{
repo: repo,
}
}
func (s *PostsService) Create(cmd commands.CreatePostsCommand) (*common.PostsResult, error) {
if user, err := s.repo.FindByPostId(cmd.PostId); user != nil {
if err != nil {
return nil, err
}
return nil, errors.New("Posts already exists")
}
entity, err := entities.CreatePosts(cmd.UserId, cmd.PostId)
if err != nil {
return nil, err
}
if err := entity.Validate(); err != nil {
return nil, err
}
result := mapper.CreatePostsResultFromEntity(&entity)
return result, nil
}
func (s *PostsService) FindByUserId(query queries.PostsFindByUserIdQuery) (*queries.PostsFindByUserIdResult, error) {
entity, err := s.repo.FindByUserId(query.UserId)
if err != nil {
return nil, err
}
if entity == nil {
return nil, errors.New("Posts not found")
}
if err := entity.Validate(); err != nil {
return nil, err
}
result := mapper.CreatePostsFindByUserIdResultFromEntity(entity)
return result, nil
}
func (s *PostsService) FindByPostId(query queries.PostsFindByPostIdQuery) (*queries.PostsFindByPostIdResult, error) {
entity, err := s.repo.FindByPostId(query.PostId)
if err != nil {
return nil, err
}
if entity == nil {
return nil, errors.New("Posts not found")
}
if err := entity.Validate(); err != nil {
return nil, err
}
result := mapper.CreatePostsFindByPostIdResultFromEntity(entity)
return result, nil
}
func (s *PostsService) FindAllByUserId(query queries.PostsFindByUserIdQuery) (*queries.PostsFindAllByUserIdResult, error) {
entities, err := s.repo.FindAllByUserId(query.UserId)
if err != nil {
return nil, err
}
if entities == nil {
return nil, fmt.Errorf("No posts owned by user: %s", query.UserId.String())
}
for _, e := range entities {
if err := e.Validate(); err != nil {
return nil, err
}
}
result := mapper.CreatePostsFindAllByUserIdResultFromEntity(entities)
return result, nil
}
func (s *PostsService) GetAll() (*queries.PostsGetAllResult, error) {
entities, err := s.repo.GetAll()
if err != nil {
return nil, err
}
for _, e := range entities {
if err := e.Validate(); err != nil {
return nil, err
}
}
result := mapper.CreatePostsGetAllResultFromEntity(entities)
return result, nil
}
func (s *PostsService) Delete(cmd commands.DeletePostsCommand) error {
entity, err := s.repo.FindById(cmd.Id)
if err != nil {
return err
}
if entity == nil {
return fmt.Errorf("Posts row not found: %s", cmd.Id)
}
if err := entity.Validate(); err != nil {
return err
}
if err := s.repo.Delete(cmd.Id); err != nil {
return err
}
return nil
}

View file

@ -5,21 +5,24 @@ import (
"time"
"github.com/google/uuid"
"github.com/lib/pq"
)
const PostTable = "post"
type Post struct {
Id uuid.UUID `db:"id"`
UserId uuid.UUID `db:"userid"`
Title string `db:"title"`
Description string `db:"description"`
Content string `db:"content"`
CreatedAt time.Time `db:"createdat"`
UpdatedAt time.Time `db:"updatedat"`
Id uuid.UUID `db:"id"`
UserId uuid.UUID `db:"userid"`
Title string `db:"title"`
Description string `db:"description"`
Content string `db:"content"`
CreatedAt time.Time `db:"createdat"`
UpdatedAt time.Time `db:"updatedat"`
Category string `db:"category"`
Tags pq.StringArray `db:"tags"` // TODO: rewrite it to many2many
}
func CreatePost(userId uuid.UUID, title string, description string, content string) (post Post, err error) {
func CreatePost(userId uuid.UUID, title string, description string, content string, category string, tags []string) (post Post, err error) {
post = Post{
Id: uuid.New(),
UserId: userId,
@ -28,6 +31,8 @@ func CreatePost(userId uuid.UUID, title string, description string, content stri
Content: content,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Category: category,
Tags: tags,
}
err = post.Validate()

View file

@ -1,43 +0,0 @@
package entities
import (
"errors"
"github.com/google/uuid"
)
const PostsTable = "posts"
type Posts struct {
Id uuid.UUID `db:"id"`
UserId uuid.UUID `db:"user_id"`
PostId uuid.UUID `db:"post_id"`
}
func CreatePosts(userId uuid.UUID, postId uuid.UUID) (posts Posts, err error) {
posts = Posts{
Id: uuid.New(),
UserId: userId,
PostId: postId,
}
err = posts.Validate()
return
}
func (p *Posts) Validate() error {
if err := uuid.Validate(p.Id.String()); err != nil {
return errors.New("Invalid posts.id")
}
if err := uuid.Validate(p.UserId.String()); err != nil {
return errors.New("Invalid posts.userId")
}
if err := uuid.Validate(p.PostId.String()); err != nil {
return errors.New("Invalid posts.postId")
}
return nil
}

View file

@ -1,17 +0,0 @@
package repository
import (
"58team_blog/internal/domain/entities"
"github.com/google/uuid"
)
type PostsRepository interface {
Create(*entities.Posts) (*entities.Posts, error)
FindById(uuid.UUID) (*entities.Posts, error)
FindByPostId(uuid.UUID) (*entities.Posts, error)
FindByUserId(uuid.UUID) (*entities.Posts, error)
FindAllByUserId(uuid.UUID) ([]*entities.Posts, error)
GetAll() ([]*entities.Posts, error)
Delete(uuid.UUID) error
}

View file

@ -0,0 +1,19 @@
package infrastructure
import (
"net/http"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
func AuthRequired(c *gin.Context) {
session := sessions.Default(c)
if user := session.Get("user"); user == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
c.Next()
}

View file

@ -21,8 +21,8 @@ func CreatePostRepository(conn *db.Database) PostRepository {
}
func (r *PostRepository) Create(entity *entities.Post) (*entities.Post, error) {
query := "INSERT INTO " + entities.PostTable + " (id, userid, title, description, content, createdat, updatedat)" +
"VALUES (:id, :userid, :title, :description, :content, :createdat, :updatedat)"
query := "INSERT INTO " + entities.PostTable + " (id, userid, title, description, content, createdat, updatedat, category, tags)" +
"VALUES (:id, :userid, :title, :description, :content, :createdat, :updatedat, :category, :tags)"
_, err := r.conn.Conn.NamedExec(query, entity)
return entity, err
@ -49,9 +49,23 @@ func (r *PostRepository) FindById(id uuid.UUID) (*entities.Post, error) {
func (r *PostRepository) FindAllByUserName(userName string) ([]*entities.Post, error) {
var entity_list []*entities.Post
var id string
user_query := "SELECT id FROM " + entities.UserTable + " WHERE username=?"
user_query, args, err := sqlx.In(user_query, userName)
if err != nil {
return nil, err
}
user_query = r.conn.Conn.Rebind(user_query)
err = r.conn.Conn.Select(&id, user_query, args...)
if err != nil {
return nil, err
}
query := "SELECT * FROM " + entities.PostTable + " WHERE userid=?"
query, args, err := sqlx.In(query, userName)
query, args, err = sqlx.In(query, id)
if err != nil {
return nil, err
}

View file

@ -31,7 +31,7 @@ func (r *UserRepository) Create(entity *entities.User) (*entities.User, error) {
}
func (r *UserRepository) FindById(id uuid.UUID) (*entities.User, error) {
var entity *entities.User
var entity entities.User
query := "SELECT * FROM " + entities.UserTable + " WHERE id=?"
query, arg, err := sqlx.In(query, id)
@ -40,9 +40,9 @@ func (r *UserRepository) FindById(id uuid.UUID) (*entities.User, error) {
}
query = r.conn.Conn.Rebind(query)
err = r.conn.Conn.Select(entity, query, arg...)
err = r.conn.Conn.Get(&entity, query, arg...)
return entity, err
return &entity, err
}
func (r *UserRepository) FindByName(username string) (*entities.User, error) {

View file

@ -1,43 +1,155 @@
package controllers
import (
"58team_blog/internal/application/commands"
"58team_blog/internal/application/errors"
"58team_blog/internal/application/services"
"58team_blog/internal/interfaces/api/mapper"
"58team_blog/internal/interfaces/api/responses"
"58team_blog/internal/utils"
"log"
"net/http"
"os"
"path/filepath"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type ImagesController struct {
service *services.ImagesService
images_path string
service *services.ImagesService
}
func CreateImagesController(service *services.ImagesService) ImagesController {
func CreateImagesController(images_path string, service *services.ImagesService) ImagesController {
return ImagesController{
service: service,
images_path: images_path,
service: service,
}
}
// get /images/{path}
// post /images
// delete /images/{id}
// @Summary Upload new image
// @Description Upload new image and returns uploaded image json object
// @Tags images
// @Produce json
// @Param file formData file true "image file"
// @Success 200 {object} responses.ImageResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /images/ [post]
func (r *ImagesController) PostImage(c *gin.Context) {
file, err := c.FormFile("file")
if err != nil {
resp := utils.HandleError(errors.NewNotFoundError("File not found"))
c.JSON(resp.ErrorCode, resp)
return
}
uploadedFile, err := file.Open()
if err != nil {
resp := utils.HandleError(errors.NewReadFileError(err.Error()))
c.JSON(resp.ErrorCode, resp)
return
}
// Read first 512 bytes for detect MIME-type
buffer := make([]byte, 512)
_, err = uploadedFile.Read(buffer)
if err != nil {
resp := utils.HandleError(errors.NewReadFileError(err.Error()))
c.JSON(resp.ErrorCode, resp)
return
}
uploadedFile.Close()
mimeType := http.DetectContentType(buffer)
if !utils.IsImageMime(mimeType) {
resp := utils.HandleError(errors.NewValidationError("Unexpected file type. Expected: jpeg, png, gif, webp, bmp."))
c.JSON(resp.ErrorCode, resp)
return
}
cmd := commands.CreateImageCommand{
Path: uuid.NewString(),
}
image, err := r.service.Create(cmd)
if err != nil {
resp := utils.HandleError(errors.NewValidationError(err.Error()))
c.JSON(resp.ErrorCode, resp)
return
}
c.SaveUploadedFile(file, r.images_path+"/"+image.Path)
resp := mapper.ResponseFromImageResult(image)
c.JSON(http.StatusOK, resp)
}
// @Summary Get an image by path
// @Description get image by path
// @Param path query string true "Path to image"
// @Tags images
// @Param path path string true "Path to image"
// @Produce octet-stream
// @Produce json
// @Success 200 {file} blob
// @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /images/{path} [get]
func (r *ImagesController) GetImage(c *gin.Context) {
filename := c.Param("path")
filePath := filepath.Join(r.images_path, filename)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusNotFound, "Image not found")
c.JSON(resp.ErrorCode, resp)
return
}
file, err := os.Open(filePath)
if err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Cannot load image file from server")
c.JSON(resp.ErrorCode, resp)
return
}
mimeData := make([]byte, 512)
if _, err := file.Read(mimeData); err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Cannot load image from server")
c.JSON(resp.ErrorCode, resp)
return
}
mimeType, err := utils.GetImageMimeType(mimeData)
if err != nil {
log.Println(err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, err.Error())
c.JSON(resp.ErrorCode, resp)
return
}
c.Header("Content-Type", mimeType)
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
// @Router /images/{path} [get]
func (r *ImagesController) GetImage(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}
func (r *ImagesController) PostImage(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}
// @Failure 400 {object} responses.ErrorResponse
// @Failure 404 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /images/{path} [delete]
func (r *ImagesController) DeleteImage(c *gin.Context) {
// TODO: return image
panic("Not implemented")
}

View file

@ -17,12 +17,14 @@ import (
)
type PostController struct {
service *services.PostService
service *services.PostService
userService *services.UserService
}
func CreatePostController(service *services.PostService) PostController {
func CreatePostController(service *services.PostService, userService *services.UserService) PostController {
return PostController{
service: service,
service: service,
userService: userService,
}
}
@ -61,6 +63,8 @@ func (r *PostController) Post(c *gin.Context) {
Title: request.Title,
Description: request.Description,
Content: request.Content,
Category: request.Category,
Tags: request.Tags,
}
res, err := r.service.Create(cmd)
@ -70,7 +74,16 @@ func (r *PostController) Post(c *gin.Context) {
return
}
// Get username by userid
user, err := r.userService.FindById(queries.UserFindByIdQuery{Id: userId})
if err != nil {
resp := utils.HandleError(err)
c.JSON(resp.ErrorCode, resp)
return
}
response := mapper.ResponseFromPostResult(res)
response.Username = user.Result.UserName
c.JSON(http.StatusCreated, response)
}
@ -94,6 +107,17 @@ func (r *PostController) GetAll(c *gin.Context) {
res := mapper.ResponseFromPostGetAllResult(result)
for e, i := range res {
// Get username by userid
user, err := r.userService.FindById(queries.UserFindByIdQuery{Id: uuid.MustParse(i.UserId)})
if err != nil {
resp := utils.HandleError(err)
c.JSON(resp.ErrorCode, resp)
return
}
res[e].Username = user.Result.UserName
}
c.JSON(http.StatusOK, res)
}
@ -127,6 +151,17 @@ func (r *PostController) GetAllWithOffset(c *gin.Context) {
res := mapper.ResponseFromPostGetAllResult(result)
for e, i := range res {
// Get username by userid
user, err := r.userService.FindById(queries.UserFindByIdQuery{Id: uuid.MustParse(i.UserId)})
if err != nil {
resp := utils.HandleError(err)
c.JSON(resp.ErrorCode, resp)
return
}
res[e].Username = user.Result.UserName
}
c.JSON(http.StatusOK, res)
}
@ -166,6 +201,14 @@ func (r *PostController) GetById(c *gin.Context) {
result := mapper.ResponseFormPostFindByIdResult(posts)
user, err := r.userService.FindById(queries.UserFindByIdQuery{Id: uuid.MustParse(result.UserId)})
if err != nil {
resp := utils.HandleError(err)
c.JSON(resp.ErrorCode, resp)
return
}
result.Username = user.Result.UserName
c.JSON(http.StatusOK, result)
}

View file

@ -11,20 +11,117 @@ import (
"log"
"net/http"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type UserController struct {
service *services.UserService
adminName string
adminPass string
service *services.UserService
}
func CreateUserController(service *services.UserService) UserController {
func CreateUserController(service *services.UserService, adminName string, adminPass string) UserController {
return UserController{
service: service,
service: service,
adminName: adminName,
adminPass: adminPass,
}
}
// @Summary Login
// @Description Login user into system
// @Tags user
// @Accept json
// @Produce json
// @Param request body requests.LoginUserRequest true "User login data"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse
// @Failure 401 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /login [post]
func (r *UserController) Login(c *gin.Context) {
session := sessions.Default(c)
var request requests.LoginUserRequest
if err := c.BindJSON(&request); err != nil {
log.Println("User invalid request: ", err)
resp := responses.CreateErrorResponse(http.StatusBadRequest, err.Error())
c.JSON(resp.ErrorCode, resp)
return
}
// Check admin login
if request.Username == r.adminName && request.Password == r.adminPass {
session.Set("user", uuid.NewString())
if err := session.Save(); err != nil {
log.Println("User save session error: ", err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Internal server error")
c.JSON(resp.ErrorCode, resp)
return
}
c.Status(http.StatusOK)
return
}
user, err := r.service.FindByName(queries.UserFindByNameQuery{Name: request.Username})
if err != nil {
resp := utils.HandleError(err)
c.JSON(resp.ErrorCode, resp)
return
}
pass, err := utils.EncryptPassword(request.Password)
if err != nil {
log.Println("User encrypt password error: ", err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Internal server error")
c.JSON(resp.ErrorCode, resp)
return
}
if utils.CheckPassword(user.Result.Password, pass) {
log.Println("Pass ", user.Result.Password, " != ", pass)
resp := responses.CreateErrorResponse(http.StatusUnauthorized, "Authentication error")
c.JSON(resp.ErrorCode, resp)
return
}
session.Set("user", user.Result.Id.String())
if err := session.Save(); err != nil {
log.Println("User save session error: ", err)
resp := responses.CreateErrorResponse(http.StatusInternalServerError, "Internal server error")
c.JSON(resp.ErrorCode, resp)
return
}
c.Status(http.StatusOK)
}
// @Summary Create new user
// @Description Creates new user in system
// @Tags user
// @Produce json
// @Success 200
// @Failure 400 {object} responses.ErrorResponse
// @Failure 500 {object} responses.ErrorResponse
// @Router /logout [get]
func (r *UserController) Logout(c *gin.Context) {
session := sessions.Default(c)
user := session.Get("user")
if user == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid session token"})
return
}
session.Delete("user")
if err := session.Save(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save session"})
return
}
c.Status(http.StatusOK)
}
// @Summary Create new user
// @Description Creates new user in system
// @Tags user

View file

@ -0,0 +1,13 @@
package mapper
import (
"58team_blog/internal/application/common"
"58team_blog/internal/interfaces/api/responses"
)
func ResponseFromImageResult(result *common.ImageResult) responses.ImageResponse {
return responses.ImageResponse{
Id: result.Id.String(),
Path: result.Path,
}
}

View file

@ -15,5 +15,7 @@ func ResponseFormPostFindByIdResult(result *queries.PostFindByIdResult) response
Content: res.Content,
CreatedAt: res.CreatedAt,
UpdatedAt: res.UpdatedAt,
Tags: res.Tags,
Category: res.Category,
}
}

View file

@ -9,8 +9,12 @@ import (
func itemFromResult(item *common.PostResult) responses.GetListPostResponseItem {
return responses.GetListPostResponseItem{
Id: item.Id.String(),
UserId: item.UserId.String(),
Title: item.Title,
Description: item.Description,
UpdatedAt: item.UpdatedAt.String(),
Tags: item.Tags,
Category: item.Category,
}
}

View file

@ -14,5 +14,7 @@ func ResponseFromPostResult(result *common.PostResult) responses.PostResponse {
Content: result.Content,
CreatedAt: result.CreatedAt,
UpdatedAt: result.UpdatedAt,
Category: result.Category,
Tags: result.Tags,
}
}

View file

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

View file

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

View file

@ -1,9 +1,14 @@
package responses
type GetListPostResponseItem struct {
Id string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Id string `json:"id"`
UserId string `json:"userId"`
Title string `json:"title"`
Description string `json:"description"`
UpdatedAt string `json:"updatedAt"`
Tags []string `json:"tags"`
Category string `json:"category"`
Username string `json:"username"`
}
type GetListPostResponse []GetListPostResponseItem

View file

@ -0,0 +1,6 @@
package responses
type ImageResponse struct {
Id string `json:"id"`
Path string `json:"path"`
}

View file

@ -10,6 +10,9 @@ type PostResponse struct {
Content string `json:"content"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Tags []string `json:"tags"`
Category string `json:"category"`
Username string `json:"username"`
}
type PostResponseList []*PostResponse

View file

@ -2,27 +2,41 @@ package interfaces
import (
"58team_blog/internal/application/services"
"58team_blog/internal/infrastructure"
"58team_blog/internal/interfaces/api/controllers"
"github.com/gin-gonic/gin"
)
func BindPostAdmin(service *services.PostService, group *gin.RouterGroup) {
post := controllers.CreatePostController(service)
func BindPostAdmin(service *services.PostService, userService *services.UserService, group *gin.RouterGroup) {
post := controllers.CreatePostController(service, userService)
g := group.Group("/post")
g.GET("/", post.GetAll)
g.GET("/offset/:offset", post.GetAllWithOffset)
g.GET("/:id", post.GetById)
g.Use(infrastructure.AuthRequired)
g.POST("/", post.Post)
g.PUT("/:id", post.Put)
g.DELETE("/:id", post.Delete)
}
func BindUser(service *services.UserService, group *gin.RouterGroup) {
user := controllers.CreateUserController(service)
func BindPost(service *services.PostService, userService *services.UserService, group *gin.RouterGroup) {
post := controllers.CreatePostController(service, userService)
g := group.Group("/post")
g.GET("/", post.GetAll)
g.GET("/offset/:offset", post.GetAllWithOffset)
g.GET("/:id", post.GetById)
}
func BindUser(adminName string, adminPass string, service *services.UserService, group *gin.RouterGroup) {
user := controllers.CreateUserController(service, adminName, adminPass)
group.POST("/login", user.Login)
group.GET("/logout", user.Logout)
g := group.Group("/user/")
g.Use(infrastructure.AuthRequired)
g.POST("/", user.Post)
g.GET("/", user.GetAll)
g.GET("/:id", user.FindById)
@ -30,3 +44,12 @@ func BindUser(service *services.UserService, group *gin.RouterGroup) {
g.PUT("/:id", user.Put)
g.DELETE("/:id", user.Delete)
}
func BindImages(images_path string, service *services.ImagesService, group *gin.RouterGroup) {
images := controllers.CreateImagesController(images_path, service)
g := group.Group("/images/")
g.POST("/", images.PostImage)
g.GET("/:path", images.GetImage)
g.DELETE("/:path", images.DeleteImage)
}

View file

@ -16,6 +16,8 @@ func HandleError(err error) responses.ErrorResponse {
if errors.Is(&ie.ValidationError{}, err) {
errorCode = http.StatusBadRequest
} else if errors.Is(&ie.ReadFileError{}, err) {
errorCode = http.StatusInternalServerError
} else if errors.Is(&ie.NotFoundError{}, err) {
errorCode = http.StatusNotFound
} else if errors.Is(&ie.AlreadyExistsError{}, err) {

View file

@ -0,0 +1,29 @@
package utils
import (
"errors"
"net/http"
)
var allowedTypes = map[string]bool{
"image/jpeg": true,
"image/jpg": true,
"image/png": true,
"image/gif": true,
"image/webp": true,
"image/bmp": true,
}
func IsImageMime(data string) bool {
return allowedTypes[data]
}
func GetImageMimeType(data []byte) (string, error) {
content_type := http.DetectContentType(data)
if !IsImageMime(content_type) {
return "", errors.New("Unexpected image format.")
}
return content_type, nil
}

View file

@ -6,9 +6,10 @@ import (
"golang.org/x/crypto/bcrypt"
)
const salt = "58_team:%s:1205secret"
func EncryptPassword(pass string) (string, error) {
var salted string
salt := "58_team:%s:1205secret"
salted = fmt.Sprintf(salt, pass)
@ -19,3 +20,14 @@ func EncryptPassword(pass string) (string, error) {
return string(hashed), nil
}
func CheckPassword(pass_hashed string, pass string) bool {
salted := fmt.Sprintf(salt, pass)
err := bcrypt.CompareHashAndPassword([]byte(pass_hashed), []byte(salted))
if err != nil {
return false
}
return true
}