feature/task-1 #3

Merged
walleri1 merged 4 commits from feature/task-1 into main 2025-03-21 23:56:47 +03:00
5 changed files with 454 additions and 32 deletions
Showing only changes of commit abd0575d71 - Show all commits

View File

@ -1,29 +1,22 @@
# syntax=docker/dockerfile:1.5
# Этап сборки
FROM golang:1.21-alpine AS builder
# Build stage
ARG GO_VERSION=1.24
FROM golang:${GO_VERSION} AS builder
# Set the working directory
# Устанавливаем рабочую директорию
WORKDIR /app
# Cache dependencies and build the application
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=bind,target=. \
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /task-manager ./cmd/main.go
# Копируем файлы проекта
COPY go.mod go.sum ./
RUN go mod download
# Final stage
COPY . ./
# Сборка бинарного файла
RUN go build -o /task_manager ./cmd/task_manager
# Финальный минимальный образ
FROM scratch AS final
# Set the working directory
WORKDIR /app
# Copy the built binary from the builder stage
COPY --from=builder /task-manager ./
# Expose the application port
COPY --from=builder /task_manager .
EXPOSE 8080
# Set the default command to run the application
CMD ["./task-manager"]
CMD ["./task_manager"]

View File

@ -16,4 +16,15 @@ services:
- 5432:5432
volumes:
- ./database/init:/docker-entrypoint-initdb.d
restart: unless-stopped
swagger-ui:
image: swaggerapi/swagger-ui:latest
container_name: swagger-ui
ports:
- "8081:8080"
volumes:
- ./config/swagger.yaml:/tmp/swagger.yaml:ro
environment:
- SWAGGER_JSON=/tmp/swagger.yaml
restart: unless-stopped

254
config/swagger.yaml Normal file
View File

@ -0,0 +1,254 @@
openapi: "3.0.0"
info:
title: "Task Manager API"
description: "API для управления задачами, проектами и пользователями"
version: "1.0.0"
servers:
- url: "http://localhost:8080/api/v1"
description: "Локальный сервер для тестирования API"
paths:
/user/login:
post:
summary: "Вход пользователя в систему"
description: "Авторизация пользователя по имени и паролю"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: "johndoe"
description: "Имя пользователя"
password:
type: string
example: "12345"
description: "Пароль пользователя"
responses:
"200":
description: "Авторизация успешна"
content:
application/json:
schema:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
"401":
description: "Недействительный логин или пароль"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/user/register:
post:
summary: "Регистрация нового пользователя"
description: "Создание нового аккаунта пользователя"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/User"
responses:
"201":
description: "Пользователь успешно зарегистрирован"
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: "Ошибка в запросе"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/user/tasks:
get:
summary: "Получить задачи пользователя"
description: "Возвращает список задач для указанного пользователя"
parameters:
- name: "id_user"
in: query
required: true
schema:
type: integer
example: 1
description: "ID пользователя"
responses:
"200":
description: "Список задач пользователя"
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Task"
"400":
description: "Отсутствует ID пользователя"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/user/projects:
get:
summary: "Получить проекты пользователя"
description: "Возвращает список проектов, связанных с пользователем"
parameters:
- name: "id_user"
in: query
required: true
schema:
type: integer
example: 1
description: "ID пользователя"
responses:
"200":
description: "Список проектов"
content:
application/json:
schema:
type: array
items:
type: string
"400":
description: "Отсутствует ID пользователя"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/projects:
post:
summary: "Создать проект"
description: "Создание нового проекта (в разработке)"
responses:
"501":
description: "Не реализовано"
content:
application/json:
schema:
$ref: "#/components/schemas/UnimplementedResponse"
get:
summary: "Получить проекты"
description: "Получение списка всех проектов (в разработке)"
responses:
"501":
description: "Не реализовано"
content:
application/json:
schema:
$ref: "#/components/schemas/UnimplementedResponse"
/projects/tasks:
post:
summary: "Создать задачу"
description: "Создание новой задачи для проекта"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
responses:
"201":
description: "Задача успешно создана"
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
"400":
description: "Ошибка в запросе"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/projects/tasks/{task_id}:
patch:
summary: "Обновить задачу"
description: "Изменение данных задачи по ID"
parameters:
- name: "task_id"
in: path
required: true
schema:
type: integer
example: 42
description: "ID задачи"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
responses:
"200":
description: "Задача успешно обновлена"
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
"400":
description: "Ошибка в запросе"
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/projects/sprints:
post:
summary: "Создать спринт"
description: "Создание спринта в проекте (в разработке)"
responses:
"501":
description: "Не реализовано"
content:
application/json:
schema:
$ref: "#/components/schemas/UnimplementedResponse"
components:
schemas:
ErrorResponse:
type: object
properties:
code:
type: integer
description: "HTTP-код ошибки"
message:
type: string
description: "Сообщение об ошибке"
error:
type: string
description: "Машинное описание ошибки"
UnimplementedResponse:
type: object
properties:
message:
type: string
example: "Not implemented"
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
password:
type: string
Task:
type: object
properties:
id:
type: integer
name:
type: string
description:
type: string

View File

@ -1,7 +1,13 @@
package app
import (
"encoding/json"
"errors"
"net/http"
"strconv"
"task_manager/internal/domain/tasks"
"task_manager/internal/domain/users"
"task_manager/internal/persistance"
gorilla "github.com/gorilla/mux"
)
@ -35,16 +41,174 @@ func (rm *TaskManager) RegisterRoutes(router *gorilla.Router) {
}
func (rm *TaskManager) handleLogin(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleRegistrateUser(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleUserTasks(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleUserProjects(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleAssignUserToProject(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleLogin(w http.ResponseWriter, r *http.Request) {
var input struct {
Name string `json:"name"`
Password string `json:"password"`
}
func (rm *TaskManager) handleCreateProject(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleCreateTask(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleCreateSprint(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleGetProjectUsers(w http.ResponseWriter, r *http.Request) {}
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload", err)
return
}
func (rm *TaskManager) handleUpdateTask(w http.ResponseWriter, r *http.Request) {}
func (rm *TaskManager) handleGetProjects(w http.ResponseWriter, r *http.Request) {}
user, err := rm.repo.GetUser(input.Name)
if err != nil || user.Password != persistance.GetMD5Hash(input.Password) {
respondWithError(w, http.StatusUnauthorized, "Invalid username or password", errors.New("authentication failed"))
return
}
respondWithJSON(w, http.StatusOK, user)
}
func (rm *TaskManager) handleRegistrateUser(w http.ResponseWriter, r *http.Request) {
var user users.User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload", err)
return
}
err = rm.repo.AddUser(&user)
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Failed to create user", err)
return
}
respondWithJSON(w, http.StatusCreated, user)
}
func (rm *TaskManager) handleUserTasks(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Query().Get("id_user")
if userID == "" {
respondWithError(w, http.StatusBadRequest, "Missing user ID", errors.New("id_user parameter required"))
return
}
id, err := strconv.Atoi(userID)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid user ID", err)
return
}
tasks, err := rm.repo.GetUserTasks(id)
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Failed to fetch user tasks", err)
return
}
respondWithJSON(w, http.StatusOK, tasks)
}
func (rm *TaskManager) handleUserProjects(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Query().Get("id_user")
if userID == "" {
respondWithError(w, http.StatusBadRequest, "Missing user ID", errors.New("id_user parameter required"))
return
}
_, err := strconv.Atoi(userID)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid user ID", err)
return
}
// Заглушка: здесь предполагается метод получения проектов пользователя
// Для реализации необходимо будет добавить соответствующий метод в репозиторий
projects := []string{"Project 1", "Project 2"} // Пример данных
respondWithJSON(w, http.StatusOK, projects)
}
func (rm *TaskManager) handleAssignUserToProject(w http.ResponseWriter, r *http.Request) {
var input struct {
IdUser int `json:"id_user"`
IdProject int `json:"id_project"`
}
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload", err)
return
}
// Заглушка: реализация привязки пользователя к проекту
// Для добавления необходимо доработать метод в репозитории
respondWithJSON(w, http.StatusOK, map[string]string{
"message": "User assigned to project successfully",
})
}
func (rm *TaskManager) handleCreateProject(w http.ResponseWriter, r *http.Request) {
respondWithJSON(w, http.StatusNotImplemented, map[string]string{
"message": "Create project not implemented yet",
})
}
func (rm *TaskManager) handleCreateTask(w http.ResponseWriter, r *http.Request) {
var task tasks.Task
err := json.NewDecoder(r.Body).Decode(&task)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload", err)
return
}
err = rm.repo.AddTask(task)
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Failed to create task", err)
return
}
respondWithJSON(w, http.StatusCreated, task)
}
func (rm *TaskManager) handleCreateSprint(w http.ResponseWriter, r *http.Request) {
taskID := gorilla.Vars(r)["task_id"]
id, err := strconv.Atoi(taskID)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid task ID", err)
return
}
var task tasks.Task
err = json.NewDecoder(r.Body).Decode(&task)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid request payload", err)
return
}
task.Id = id
err = rm.repo.UpdateTask(task)
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Failed to update task", err)
return
}
respondWithJSON(w, http.StatusOK, task)
}
func (rm *TaskManager) handleGetProjectUsers(w http.ResponseWriter, r *http.Request) {
respondWithJSON(w, http.StatusNotImplemented, map[string]string{
"message": "Get projects not implemented yet",
})
}
func (rm *TaskManager) handleUpdateTask(w http.ResponseWriter, r *http.Request) {
respondWithJSON(w, http.StatusNotImplemented, map[string]string{
"message": "Create sprint not implemented yet",
})
}
func (rm *TaskManager) handleGetProjects(w http.ResponseWriter, r *http.Request) {
respondWithJSON(w, http.StatusNotImplemented, map[string]string{
"message": "Get project users not implemented yet",
})
}
func respondWithError(w http.ResponseWriter, code int, message string, err error) {
w.WriteHeader(code)
json.NewEncoder(w).Encode(ErrorResponse{
Code: code,
Message: message,
Error: err.Error(),
})
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
w.WriteHeader(code)
json.NewEncoder(w).Encode(payload)
}