feature/task-1 #3
35
Dockerfile
35
Dockerfile
@ -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"]
|
11
compose.yaml
11
compose.yaml
@ -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
254
config/swagger.yaml
Normal 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
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user