Fix BASE_URL config loading, add tasks/projects; robust .env path resolution
- Config: try ENV_FILE, .env, ../.env for loading; trim trailing slash from BaseURL - Log BASE_URL at server startup for verification - .env.example: document BASE_URL - Tasks, projects, tags, migrations and related API/handlers Made-with: Cursor
This commit is contained in:
159
internal/service/tag.go
Normal file
159
internal/service/tag.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/calendarapi/internal/models"
|
||||
"github.com/calendarapi/internal/repository"
|
||||
"github.com/calendarapi/internal/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type TagService struct {
|
||||
queries *repository.Queries
|
||||
}
|
||||
|
||||
func NewTagService(queries *repository.Queries) *TagService {
|
||||
return &TagService{queries: queries}
|
||||
}
|
||||
|
||||
type CreateTagRequest struct {
|
||||
Name string
|
||||
Color *string
|
||||
}
|
||||
|
||||
type UpdateTagRequest struct {
|
||||
Name *string
|
||||
Color *string
|
||||
}
|
||||
|
||||
func (s *TagService) Create(ctx context.Context, userID uuid.UUID, req CreateTagRequest) (*models.Tag, error) {
|
||||
if len(req.Name) < 1 || len(req.Name) > 50 {
|
||||
return nil, models.NewValidationError("tag name must be 1-50 characters")
|
||||
}
|
||||
color := "#6B7280"
|
||||
if req.Color != nil {
|
||||
if err := utils.ValidateColor(*req.Color); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
color = *req.Color
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
t, err := s.queries.CreateTag(ctx, repository.CreateTagParams{
|
||||
ID: utils.ToPgUUID(id),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
Name: req.Name,
|
||||
Column4: color,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
|
||||
tag := tagFromDB(t)
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
func (s *TagService) Get(ctx context.Context, userID uuid.UUID, tagID uuid.UUID) (*models.Tag, error) {
|
||||
t, err := s.queries.GetTagByID(ctx, repository.GetTagByIDParams{
|
||||
ID: utils.ToPgUUID(tagID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
})
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, models.ErrNotFound
|
||||
}
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
tag := tagFromDB(t)
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
func (s *TagService) List(ctx context.Context, userID uuid.UUID) ([]models.Tag, error) {
|
||||
rows, err := s.queries.ListTagsByOwner(ctx, utils.ToPgUUID(userID))
|
||||
if err != nil {
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
|
||||
tags := make([]models.Tag, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
tags = append(tags, tagFromDB(r))
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (s *TagService) Update(ctx context.Context, userID uuid.UUID, tagID uuid.UUID, req UpdateTagRequest) (*models.Tag, error) {
|
||||
if req.Name != nil && (len(*req.Name) < 1 || len(*req.Name) > 50) {
|
||||
return nil, models.NewValidationError("tag name must be 1-50 characters")
|
||||
}
|
||||
if req.Color != nil {
|
||||
if err := utils.ValidateColor(*req.Color); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
updateParams := repository.UpdateTagParams{
|
||||
ID: utils.ToPgUUID(tagID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
}
|
||||
if req.Name != nil {
|
||||
updateParams.Name = utils.ToPgTextPtr(req.Name)
|
||||
}
|
||||
if req.Color != nil {
|
||||
updateParams.Color = utils.ToPgTextPtr(req.Color)
|
||||
}
|
||||
|
||||
t, err := s.queries.UpdateTag(ctx, updateParams)
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, models.ErrNotFound
|
||||
}
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
tag := tagFromDB(t)
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
func (s *TagService) Delete(ctx context.Context, userID uuid.UUID, tagID uuid.UUID) error {
|
||||
err := s.queries.DeleteTag(ctx, repository.DeleteTagParams{
|
||||
ID: utils.ToPgUUID(tagID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
})
|
||||
if err != nil {
|
||||
return models.ErrInternal
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TagService) AttachToTask(ctx context.Context, userID uuid.UUID, tagID uuid.UUID, taskID uuid.UUID) error {
|
||||
if _, err := s.queries.GetTagByID(ctx, repository.GetTagByIDParams{
|
||||
ID: utils.ToPgUUID(tagID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
}); err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return models.ErrNotFound
|
||||
}
|
||||
return models.ErrInternal
|
||||
}
|
||||
return s.queries.AttachTagToTask(ctx, repository.AttachTagToTaskParams{
|
||||
TaskID: utils.ToPgUUID(taskID),
|
||||
TagID: utils.ToPgUUID(tagID),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TagService) DetachFromTask(ctx context.Context, userID uuid.UUID, tagID uuid.UUID, taskID uuid.UUID) error {
|
||||
if _, err := s.queries.GetTagByID(ctx, repository.GetTagByIDParams{
|
||||
ID: utils.ToPgUUID(tagID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
}); err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return models.ErrNotFound
|
||||
}
|
||||
return models.ErrInternal
|
||||
}
|
||||
return s.queries.DetachTagFromTask(ctx, repository.DetachTagFromTaskParams{
|
||||
TaskID: utils.ToPgUUID(taskID),
|
||||
TagID: utils.ToPgUUID(tagID),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user