aides-repo-api/internal/router/router.go
Maxim Slipenko d02e0d122c chore: migrate to standard layout (#5)
See: https://github.com/golang-standards/project-layout
Reviewed-on: https://code.alt-gnome.ru/aides-infra/aides-repo-api/pulls/5
Co-authored-by: Maxim Slipenko <no-reply@maxim.slipenko.com>
Co-committed-by: Maxim Slipenko <no-reply@maxim.slipenko.com>
2024-12-12 07:47:17 +00:00

135 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package router
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
"strconv"
"code.alt-gnome.ru/aides-infra/aides-repo-api/internal/models"
"github.com/go-chi/render"
"github.com/go-chi/chi/v5" //Импорт пакета chi для маршрутизации
"github.com/go-chi/chi/v5/middleware" // Импорт middleware для логирования
)
func createSymlink(target, link string) error {
if _, err := os.Lstat(link); err == nil {
if err := os.Remove(link); err != nil {
return fmt.Errorf("failed to remove existing file or symlink: %w", err)
}
}
if err := os.Symlink(target, link); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
}
return nil
}
type Router struct {
Config models.Config
}
// Создает Роутер
func NewRouter(cfg models.Config) *Router {
return &Router{Config: cfg}
}
// Метод настройки маршрутов для Роутера
func (r *Router) SetupRoutes() *chi.Mux {
router := chi.NewRouter()
router.Use(middleware.Logger)
// Создаем директорию для загрузки
os.MkdirAll(path.Join(r.Config.UploadDir, "out"), os.ModePerm)
// Определяем маршрут для загрузки файлов("/upload/{executionID}" путь, по которому будет доступен данный маршрут.
//Путь включает переменную часть {executionID}, которая позволяет извлекать динамические параметры из URL.)
router.Post("/upload/{repo}/task/{taskID}", r.uploadHandler)
return router
}
// Метод отвечает за загрузку файлов
func (r *Router) uploadHandler(w http.ResponseWriter, req *http.Request) {
// Проверка авторизации
if req.Header.Get("Authorization") != "Bearer "+r.Config.Token {
render.JSON(w, req, models.ErrResponse{Message: "Не авторизованный", Code: http.StatusUnauthorized})
return
}
// Извлекаем параметр taskID из URL и проверяем его наличие
taskID := chi.URLParam(req, "taskID")
repo := chi.URLParam(req, "repo")
if taskID == "" || repo == "" {
render.JSON(w, req, models.ErrResponse{Message: "Требуются параметры [repo] и [taskID]", Code: http.StatusBadRequest})
return
}
// Чтение файлов из запроса
err := req.ParseMultipartForm(r.Config.MaxSizeUpload) // Лимит 100 MB
if err != nil {
render.JSON(w, req, models.ErrResponse{Message: "Парсинг не удался", Code: http.StatusBadRequest})
return
}
// При успешном парсинге извлекаем файлы
files := req.MultipartForm.File["files"] // Карта где ключами являются имена полей формы, а значениями — массивы заголовков файлов.
localPath := path.Join(repo, "task", taskID)
taskFolderPath := path.Join(r.Config.UploadDir, "extra", localPath)
os.MkdirAll(taskFolderPath, os.ModePerm)
for _, fileHeader := range files {
file, err := fileHeader.Open()
if err != nil {
render.JSON(w, req, models.ErrResponse{Message: "Не удается открыть файл", Code: http.StatusInternalServerError})
return
}
defer file.Close()
// Полный путь для файла
filePath := path.Join(taskFolderPath, fileHeader.Filename)
//Удаляем файл если такой уже существует
if _, err := os.Stat(filePath); err == nil {
err = os.Remove(filePath)
if err != nil {
render.JSON(w, req, models.ErrResponse{Message: "Не удалось удалить файл", Code: http.StatusInternalServerError})
return
}
}
// Сохранение файла на сервере
outFile, err := os.Create(filePath)
if err != nil {
render.JSON(w, req, models.ErrResponse{Message: "Не удается создать файл", Code: http.StatusInternalServerError})
return
}
defer outFile.Close()
_, err = io.Copy(outFile, file)
if err != nil {
render.JSON(w, req, models.ErrResponse{Message: "Не удалось сохранить файл", Code: http.StatusInternalServerError})
return
}
// Символическая ссылка
targetPath := path.Join("../extra/", localPath, fileHeader.Filename)
symLink := path.Join(r.Config.UploadDir, "out", fileHeader.Filename)
err = createSymlink(targetPath, symLink)
if err != nil {
render.JSON(w, req, models.ErrResponse{Message: "Не удается создать ссылку", Code: http.StatusInternalServerError})
return
}
}
//Ответы в формате JSON
resp := map[string]string{
"taskID": taskID,
"repository": repo,
"message": "Файлы успешно загружены",
"fileCount": strconv.Itoa(len(files)),
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(resp)
}