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:
Michilis
2026-03-09 18:57:51 +00:00
parent 75105b8b46
commit bd24545b7b
61 changed files with 6595 additions and 90 deletions

View File

@@ -0,0 +1,91 @@
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 TaskDependencyService struct {
queries *repository.Queries
}
func NewTaskDependencyService(queries *repository.Queries) *TaskDependencyService {
return &TaskDependencyService{queries: queries}
}
func (s *TaskDependencyService) Add(ctx context.Context, userID uuid.UUID, taskID uuid.UUID, blocksTaskID uuid.UUID) error {
if taskID == blocksTaskID {
return models.NewValidationError("task cannot depend on itself")
}
if _, err := s.queries.GetTaskByID(ctx, repository.GetTaskByIDParams{
ID: utils.ToPgUUID(taskID),
OwnerID: utils.ToPgUUID(userID),
}); err != nil {
if err == pgx.ErrNoRows {
return models.ErrNotFound
}
return models.ErrInternal
}
hasCircle, err := s.queries.CheckDirectCircularDependency(ctx, repository.CheckDirectCircularDependencyParams{
BlocksTaskID: utils.ToPgUUID(taskID),
TaskID: utils.ToPgUUID(blocksTaskID),
})
if err != nil {
return models.ErrInternal
}
if hasCircle {
return models.NewConflictError("circular dependency detected")
}
return s.queries.AddTaskDependency(ctx, repository.AddTaskDependencyParams{
TaskID: utils.ToPgUUID(taskID),
BlocksTaskID: utils.ToPgUUID(blocksTaskID),
})
}
func (s *TaskDependencyService) Remove(ctx context.Context, userID uuid.UUID, taskID uuid.UUID, blocksTaskID uuid.UUID) error {
if _, err := s.queries.GetTaskByID(ctx, repository.GetTaskByIDParams{
ID: utils.ToPgUUID(taskID),
OwnerID: utils.ToPgUUID(userID),
}); err != nil {
if err == pgx.ErrNoRows {
return models.ErrNotFound
}
return models.ErrInternal
}
return s.queries.RemoveTaskDependency(ctx, repository.RemoveTaskDependencyParams{
TaskID: utils.ToPgUUID(taskID),
BlocksTaskID: utils.ToPgUUID(blocksTaskID),
})
}
func (s *TaskDependencyService) ListBlockers(ctx context.Context, userID uuid.UUID, taskID uuid.UUID) ([]models.Task, error) {
if _, err := s.queries.GetTaskByID(ctx, repository.GetTaskByIDParams{
ID: utils.ToPgUUID(taskID),
OwnerID: utils.ToPgUUID(userID),
}); err != nil {
if err == pgx.ErrNoRows {
return nil, models.ErrNotFound
}
return nil, models.ErrInternal
}
rows, err := s.queries.ListTaskBlockers(ctx, utils.ToPgUUID(taskID))
if err != nil {
return nil, models.ErrInternal
}
tasks := make([]models.Task, 0, len(rows))
for _, r := range rows {
tasks = append(tasks, *taskFromDB(r))
}
return tasks, nil
}