️ feat: update global styles and color variables for improved theming

refactor: change import paths for DeviceContext and GravatarAvatar components

fix: adjust login form API call and update UI text for clarity

feat: add post API for listing posts with pagination and filtering options

feat: implement BlogCard component for displaying blog posts with enhanced UI

feat: create Badge component for consistent styling of labels and indicators

refactor: reintroduce DeviceContext with improved functionality for theme and language management

feat: define Label and Post models for better type safety and structure
This commit is contained in:
2025-07-24 13:12:59 +08:00
parent 21a1726f71
commit abe1099711
30 changed files with 935 additions and 288 deletions

View File

@ -1 +1,85 @@
package service
import (
"github.com/snowykami/neo-blog/internal/dto"
"github.com/snowykami/neo-blog/internal/model"
"github.com/snowykami/neo-blog/internal/repo"
"gorm.io/gorm"
)
type LabelService struct{}
func NewLabelService() *LabelService {
return &LabelService{}
}
func (l *LabelService) CreateLabel(req *dto.LabelDto) (uint, error) {
label := &model.Label{
Key: req.Key,
Value: req.Value,
Color: req.Color,
TailwindClassName: req.TailwindClassName,
}
return label.ID, repo.Label.CreateLabel(label)
}
func (l *LabelService) UpdateLabel(req *dto.LabelDto) (uint, error) {
label := &model.Label{
Model: gorm.Model{ID: req.ID},
Key: req.Key,
Value: req.Value,
Color: req.Color,
TailwindClassName: req.TailwindClassName,
}
return label.ID, repo.Label.UpdateLabel(label)
}
func (l *LabelService) DeleteLabel(id string) error {
return repo.Label.DeleteLabel(id)
}
func (l *LabelService) GetLabelByKey(key string) (*dto.LabelDto, error) {
label, err := repo.Label.GetLabelByKey(key)
if err != nil {
return nil, err
}
return &dto.LabelDto{
ID: label.ID,
Key: label.Key,
Value: label.Value,
Color: label.Color,
TailwindClassName: label.TailwindClassName,
}, nil
}
func (l *LabelService) GetLabelByID(id string) (*dto.LabelDto, error) {
label, err := repo.Label.GetLabelByID(id)
if err != nil {
return nil, err
}
return &dto.LabelDto{
ID: label.ID,
Key: label.Key,
Value: label.Value,
Color: label.Color,
TailwindClassName: label.TailwindClassName,
}, nil
}
func (l *LabelService) ListLabels() ([]dto.LabelDto, error) {
labels, err := repo.Label.ListLabels()
var labelDtos []dto.LabelDto
if err != nil {
return labelDtos, err
}
for _, label := range labels {
labelDtos = append(labelDtos, dto.LabelDto{
ID: label.ID,
Key: label.Key,
Value: label.Value,
Color: label.Color,
TailwindClassName: label.TailwindClassName,
})
}
return labelDtos, nil
}

View File

@ -7,6 +7,7 @@ import (
"github.com/snowykami/neo-blog/internal/model"
"github.com/snowykami/neo-blog/internal/repo"
"github.com/snowykami/neo-blog/pkg/errs"
"strconv"
)
type PostService struct{}
@ -15,10 +16,10 @@ func NewPostService() *PostService {
return &PostService{}
}
func (p *PostService) CreatePost(ctx context.Context, req *dto.CreateOrUpdatePostReq) error {
currentUser := ctxutils.GetCurrentUser(ctx)
if currentUser == nil {
return errs.ErrUnauthorized
func (p *PostService) CreatePost(ctx context.Context, req *dto.CreateOrUpdatePostReq) (uint, error) {
currentUser, ok := ctxutils.GetCurrentUser(ctx)
if !ok {
return 0, errs.ErrUnauthorized
}
post := &model.Post{
Title: req.Title,
@ -27,7 +28,7 @@ func (p *PostService) CreatePost(ctx context.Context, req *dto.CreateOrUpdatePos
Labels: func() []model.Label {
labelModels := make([]model.Label, 0)
for _, labelID := range req.Labels {
labelModel, err := repo.Label.GetLabelByID(labelID)
labelModel, err := repo.Label.GetLabelByID(strconv.Itoa(int(labelID)))
if err == nil {
labelModels = append(labelModels, *labelModel)
}
@ -37,14 +38,14 @@ func (p *PostService) CreatePost(ctx context.Context, req *dto.CreateOrUpdatePos
IsPrivate: req.IsPrivate,
}
if err := repo.Post.CreatePost(post); err != nil {
return err
return 0, err
}
return nil
return post.ID, nil
}
func (p *PostService) DeletePost(ctx context.Context, id string) error {
currentUser := ctxutils.GetCurrentUser(ctx)
if currentUser == nil {
currentUser, ok := ctxutils.GetCurrentUser(ctx)
if !ok {
return errs.ErrUnauthorized
}
if id == "" {
@ -64,8 +65,8 @@ func (p *PostService) DeletePost(ctx context.Context, id string) error {
}
func (p *PostService) GetPost(ctx context.Context, id string) (*dto.PostDto, error) {
currentUser := ctxutils.GetCurrentUser(ctx)
if currentUser == nil {
currentUser, ok := ctxutils.GetCurrentUser(ctx)
if !ok {
return nil, errs.ErrUnauthorized
}
if id == "" {
@ -92,20 +93,20 @@ func (p *PostService) GetPost(ctx context.Context, id string) (*dto.PostDto, err
}, nil
}
func (p *PostService) UpdatePost(ctx context.Context, id string, req *dto.CreateOrUpdatePostReq) error {
currentUser := ctxutils.GetCurrentUser(ctx)
if currentUser == nil {
return errs.ErrUnauthorized
func (p *PostService) UpdatePost(ctx context.Context, id string, req *dto.CreateOrUpdatePostReq) (uint, error) {
currentUser, ok := ctxutils.GetCurrentUser(ctx)
if !ok {
return 0, errs.ErrUnauthorized
}
if id == "" {
return errs.ErrBadRequest
return 0, errs.ErrBadRequest
}
post, err := repo.Post.GetPostByID(id)
if err != nil {
return errs.New(errs.ErrNotFound.Code, "post not found", err)
return 0, errs.New(errs.ErrNotFound.Code, "post not found", err)
}
if post.UserID != currentUser.ID {
return errs.ErrForbidden
return 0, errs.ErrForbidden
}
post.Title = req.Title
post.Content = req.Content
@ -113,7 +114,7 @@ func (p *PostService) UpdatePost(ctx context.Context, id string, req *dto.Create
post.Labels = func() []model.Label {
labelModels := make([]model.Label, len(req.Labels))
for _, labelID := range req.Labels {
labelModel, err := repo.Label.GetLabelByID(labelID)
labelModel, err := repo.Label.GetLabelByID(strconv.Itoa(int(labelID)))
if err == nil {
labelModels = append(labelModels, *labelModel)
}
@ -121,14 +122,14 @@ func (p *PostService) UpdatePost(ctx context.Context, id string, req *dto.Create
return labelModels
}()
if err := repo.Post.UpdatePost(post); err != nil {
return errs.ErrInternalServer
return 0, errs.ErrInternalServer
}
return nil
return post.ID, nil
}
func (p *PostService) ListPosts(ctx context.Context, req *dto.ListPostReq) (*dto.ListPostResp, error) {
func (p *PostService) ListPosts(ctx context.Context, req *dto.ListPostReq) ([]dto.PostDto, error) {
postDtos := make([]dto.PostDto, 0)
currentUserID := ctxutils.GetCurrentUserID(ctx)
currentUserID, _ := ctxutils.GetCurrentUserID(ctx)
posts, err := repo.Post.ListPosts(currentUserID, req.Keywords, req.Page, req.Size, req.OrderedBy, req.Reverse)
if err != nil {
return nil, errs.New(errs.ErrInternalServer.Code, "failed to list posts", err)
@ -136,10 +137,5 @@ func (p *PostService) ListPosts(ctx context.Context, req *dto.ListPostReq) (*dto
for _, post := range posts {
postDtos = append(postDtos, post.ToDto())
}
return &dto.ListPostResp{
Posts: postDtos,
Total: uint64(len(posts)),
OrderedBy: req.OrderedBy,
Reverse: req.Reverse,
}, nil
return postDtos, nil
}

View File

@ -140,7 +140,7 @@ func (s *UserService) RequestVerifyEmail(req *dto.VerifyEmailReq) (*dto.VerifyEm
return &dto.VerifyEmailResp{Success: true}, nil
}
func (s *UserService) ListOidcConfigs() (*dto.ListOidcConfigResp, error) {
func (s *UserService) ListOidcConfigs() ([]dto.UserOidcConfigDto, error) {
enabledOidcConfigs, err := repo.Oidc.ListOidcConfigs(true)
if err != nil {
return nil, errs.ErrInternalServer
@ -187,9 +187,7 @@ func (s *UserService) ListOidcConfigs() (*dto.ListOidcConfigResp, error) {
LoginUrl: loginUrl,
})
}
return &dto.ListOidcConfigResp{
OidcConfigs: oidcConfigsDtos,
}, nil
return oidcConfigsDtos, nil
}
func (s *UserService) OidcLogin(req *dto.OidcLoginReq) (*dto.OidcLoginResp, error) {