diff --git a/cmd/main.go b/cmd/main.go index 7905807..18553b2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,5 +1,18 @@ package main +import ( + "log" + "os" + + "github.com/Yandex-Practicum/go1fl-sprint6-final/internal/server" +) + func main() { + logger := log.New(os.Stdout, "SERVER:", log.LstdFlags) + srv := server.New(logger) + logger.Println("Запуск сервера...") + if err := srv.Start(); err != nil { + logger.Fatalf("ошибка запуска сервера: %v", err) + } } diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 5ac8282..8d46387 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -1 +1,112 @@ +// Пакет handlers package handlers + +import ( + "io" + "log" + "net/http" + "os" + "path/filepath" + "strings" + "time" + + "github.com/Yandex-Practicum/go1fl-sprint6-final/internal/service" +) + +func ReturnHTML(w http.ResponseWriter, r *http.Request) { + // Получаем текущую рабочую директорию + wd, err := os.Getwd() + if err != nil { + log.Printf("Ошибка получения рабочей директории: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Формируем абсолютный путь к index.html + indexPath := filepath.Join(wd, "index.html") + data, err := os.ReadFile(indexPath) + if err != nil { + log.Printf("Ошибка чтения %s: %v", indexPath, err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Write(data) +} + +func ConvertStr(w http.ResponseWriter, r *http.Request) { + const maxMemory = 32 << 20 // 32MB + + if r.Method != http.MethodPost { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + + // Парсинг формы с увеличенным лимитом памяти + if err := r.ParseMultipartForm(maxMemory); err != nil { + log.Printf("Ошибка парсинга формы: %v", err) + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + + file, header, err := r.FormFile("myFile") + if err != nil { + log.Printf("Файл не найден: %v", err) + http.Error(w, "Bad Request", http.StatusBadRequest) + return + } + defer file.Close() + + // Чтение содержимого файла + data, err := io.ReadAll(file) + if err != nil { + log.Printf("Ошибка чтения файла: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + content := strings.TrimSpace(string(data)) + if content == "" { + http.Error(w, "Empty File", http.StatusBadRequest) + return + } + + // Конвертация + converted, err := service.AutoConvert(content) + if err != nil { + log.Printf("Ошибка конвертации: %v", err) + http.Error(w, "Conversion Error", http.StatusInternalServerError) + return + } + + // Создание результата + timestamp := strings.ReplaceAll( + time.Now().UTC().Format("2006-01-02_15-04-05"), + ":", "-", + ) + ext := filepath.Ext(header.Filename) + if ext == "" { + ext = ".txt" + } + outputFilename := "result_" + timestamp + ext + + outputFile, err := os.Create(outputFilename) + if err != nil { + log.Printf("Ошибка создания файла: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + defer outputFile.Close() + + if _, err := outputFile.WriteString(converted); err != nil { + log.Printf("Ошибка записи файла: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Ответ клиенту + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(converted)) +} diff --git a/internal/server/server.go b/internal/server/server.go index abb4e43..feef934 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1 +1,40 @@ package server + +import ( + "log" + "net/http" + "time" + + "github.com/Yandex-Practicum/go1fl-sprint6-final/internal/handlers" +) + +type Server struct { + logger *log.Logger // Логгер + httpServer *http.Server // HTTP-сервер +} + +func New(logger *log.Logger) *Server { + router := http.NewServeMux() + + router.HandleFunc("/", handlers.ReturnHTML) + router.HandleFunc("/upload", handlers.ConvertStr) + + httpServer := &http.Server{ + Addr: ":8080", + Handler: router, + ErrorLog: logger, + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 15 * time.Second, + } + + return &Server{ + logger: logger, + httpServer: httpServer, + } +} + +func (s *Server) Start() error { + s.logger.Printf("Сервер запущен на %s", s.httpServer.Addr) + return s.httpServer.ListenAndServe() +} diff --git a/internal/service/service.go b/internal/service/service.go index 6d43c33..2a17d02 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -1 +1,38 @@ +// Пакет service package service + +import ( + "fmt" + "regexp" + "strings" + + "github.com/Yandex-Practicum/go1fl-sprint6-final/pkg/morse" +) + +func AutoConvert(input string) (string, error) { + input = strings.TrimSpace(input) + if input == "" { + return "", fmt.Errorf("empty input") + } + + // Проверка + if isMorse(input) { + text := morse.ToText(input) + if text == "" { + return "", fmt.Errorf("invalid morse code") + } + return text, nil + } + + // Конвертация + morseCode := morse.ToMorse(strings.ToUpper(input)) + if morseCode == "" { + return "", fmt.Errorf("invalid text") + } + + return morseCode, nil +} + +func isMorse(s string) bool { + return regexp.MustCompile(`^[\s.\-/]+$`).MatchString(s) +}