aides-uploader/main.go

213 lines
5.8 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 main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
)
type Configuration struct {
ServerURL string
Repo string
Token string
FilePatterns []string
Files []string
}
type TaskResponse struct {
TaskID int `json:"taskID"`
Status int `json:"status"`
}
func parseFlags() Configuration {
var cfg Configuration
flag.StringVar(&cfg.ServerURL, "url", "http://localhost:9999", "Базовый URL сервера загрузки")
flag.StringVar(&cfg.Repo, "repo", "", "Название репозитория (обязательно)")
flag.StringVar(&cfg.Token, "token", "", "Токен авторизации (обязательно)")
files := flag.String("files", "", "Список путей к файлам или паттернов (разделены запятой) для загрузки (обязательно)")
flag.Parse()
missing := false
if cfg.Repo == "" {
fmt.Println("Ошибка: -repo является обязательным")
missing = true
}
if cfg.Token == "" {
fmt.Println("Ошибка: -token является обязательным")
missing = true
}
if *files == "" {
fmt.Println("Ошибка: -files является обязательным")
missing = true
}
if missing {
flag.Usage()
os.Exit(1)
}
cfg.FilePatterns = splitAndTrim(*files, ",")
var allFiles []string
for _, pattern := range cfg.FilePatterns {
matches, err := filepath.Glob(pattern)
if err != nil {
fmt.Fprintf(os.Stderr, "Неверный паттерн glob '%s': %v\n", pattern, err)
continue
}
if len(matches) == 0 {
fmt.Fprintf(os.Stderr, "Предупреждение: Паттерн '%s' не совпадает ни с одним файлом\n", pattern)
continue
}
allFiles = append(allFiles, matches...)
}
cfg.Files = unique(allFiles)
if len(cfg.Files) == 0 {
fmt.Println("Ошибка: Не найдено файлов для загрузки после обработки паттернов")
os.Exit(1)
}
return cfg
}
func splitAndTrim(s, sep string) []string {
parts := strings.Split(s, sep)
var trimmed []string
for _, part := range parts {
part = strings.TrimSpace(part)
if part != "" {
trimmed = append(trimmed, part)
}
}
return trimmed
}
func unique(input []string) []string {
seen := make(map[string]struct{})
var result []string
for _, item := range input {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
}
return result
}
func createMultipartRequest(url string, files []string, token string, repo string) (*http.Request, error) {
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
for _, filePath := range files {
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("не удалось открыть файл %s: %w", filePath, err)
}
defer file.Close()
part, err := writer.CreateFormFile("files", filepath.Base(filePath))
if err != nil {
return nil, fmt.Errorf("не удалось создать поле формы для файла %s: %w", filePath, err)
}
_, err = io.Copy(part, file)
if err != nil {
return nil, fmt.Errorf("не удалось скопировать содержимое файла %s: %w", filePath, err)
}
}
err := writer.WriteField("repo", repo)
if err != nil {
return nil, fmt.Errorf("не удалось добавить поле repo: %w", err)
}
err = writer.Close()
if err != nil {
return nil, fmt.Errorf("не удалось закрыть multipart писатель: %w", err)
}
req, err := http.NewRequest("POST", url, &buf)
if err != nil {
return nil, fmt.Errorf("не удалось создать HTTP-запрос: %w", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Authorization", "Bearer "+token)
return req, nil
}
func createTask(cfg Configuration) (int, error) {
taskURL := fmt.Sprintf("%s/tasks", strings.TrimRight(cfg.ServerURL, "/"))
requestBody, err := json.Marshal(map[string]string{
"Repo": cfg.Repo,
})
if err != nil {
return 0, fmt.Errorf("не удалось создать тело запроса: %w", err)
}
req, err := http.NewRequest("POST", taskURL, bytes.NewReader(requestBody))
if err != nil {
return 0, fmt.Errorf("не удалось создать HTTP-запрос: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+cfg.Token)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return 0, fmt.Errorf("не удалось выполнить HTTP-запрос: %w", err)
}
defer resp.Body.Close()
var taskResp TaskResponse
err = json.NewDecoder(resp.Body).Decode(&taskResp)
if err != nil {
return 0, fmt.Errorf("не удалось декодировать ответ: %w", err)
}
return taskResp.TaskID, nil
}
func uploadFiles(cfg Configuration, taskID int) error {
uploadURL := fmt.Sprintf("%s/tasks/%d/upload", strings.TrimRight(cfg.ServerURL, "/"), taskID)
req, err := createMultipartRequest(uploadURL, cfg.Files, cfg.Token, cfg.Repo)
if err != nil {
return fmt.Errorf("не удалось создать multipart-запрос: %w", err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("не удалось выполнить HTTP-запрос: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("не удалось прочитать тело ответа: %w", err)
}
fmt.Println(string(respBody))
return nil
}
func main() {
cfg := parseFlags()
taskID, err := createTask(cfg)
if err != nil {
fmt.Fprintf(os.Stderr, "Ошибка: %v\n", err)
os.Exit(1)
}
uploadFiles(cfg, taskID)
}