2024-12-04 11:46:42 +00:00
|
|
|
|
package router
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"path"
|
|
|
|
|
"rest_api/models" // Импорт пакета с моделями, где определены структуры конфигурации
|
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
|
|
"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) {
|
2024-12-06 15:13:29 +00:00
|
|
|
|
// Проверка авторизации
|
2024-12-04 11:46:42 +00:00
|
|
|
|
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)
|
|
|
|
|
}
|