package cmd import ( "encoding/json" "fmt" "html/template" "net/http" "os" "path/filepath" "strconv" "strings" "time" "github.com/direct-dev-ru/linux-command-gpt/config" "github.com/direct-dev-ru/linux-command-gpt/gpt" "github.com/russross/blackfriday/v2" ) // StartResultServer запускает HTTP сервер для просмотра сохраненных результатов func StartResultServer(host, port string) error { http.HandleFunc("/", handleResultsPage) http.HandleFunc("/file/", handleFileView) http.HandleFunc("/delete/", handleDeleteFile) http.HandleFunc("/history", handleHistoryPage) http.HandleFunc("/history/delete/", handleDeleteHistoryEntry) http.HandleFunc("/history/clear", handleClearHistory) http.HandleFunc("/prompts", handlePromptsPage) http.HandleFunc("/prompts/add", handleAddPrompt) http.HandleFunc("/prompts/edit/", handleEditPrompt) http.HandleFunc("/prompts/delete/", handleDeletePrompt) http.HandleFunc("/prompts/restore/", handleRestorePrompt) http.HandleFunc("/prompts/restore-verbose/", handleRestoreVerbosePrompt) http.HandleFunc("/prompts/save-lang", handleSaveLang) addr := fmt.Sprintf("%s:%s", host, port) fmt.Printf("Сервер запущен на http://%s\n", addr) fmt.Println("Нажмите Ctrl+C для остановки") return http.ListenAndServe(addr, nil) } // handleResultsPage обрабатывает главную страницу со списком файлов func handleResultsPage(w http.ResponseWriter, r *http.Request) { files, err := getResultFiles() if err != nil { http.Error(w, fmt.Sprintf("Ошибка чтения папки: %v", err), http.StatusInternalServerError) return } tmpl := ` LCG Results - Linux Command GPT

🚀 LCG Results

Просмотр сохраненных результатов Linux Command GPT

📝 История ⚙️ Промпты
{{.TotalFiles}}
Всего файлов
{{.RecentFiles}}
За последние 7 дней
{{if .Files}}
{{range .Files}}
{{.Name}}
📅 {{.ModTime}} | 📏 {{.Size}}
{{.Preview}}
{{end}}
{{else}}

📁 Папка пуста

Здесь будут отображаться сохраненные результаты после использования команды lcg

{{end}}
` t, err := template.New("results").Parse(tmpl) if err != nil { http.Error(w, "Ошибка шаблона", http.StatusInternalServerError) return } // Подсчитываем статистику recentCount := 0 weekAgo := time.Now().AddDate(0, 0, -7) for _, file := range files { // Парсим время из строки для сравнения if modTime, err := time.Parse("02.01.2006 15:04", file.ModTime); err == nil { if modTime.After(weekAgo) { recentCount++ } } } data := struct { Files []FileInfo TotalFiles int RecentFiles int }{ Files: files, TotalFiles: len(files), RecentFiles: recentCount, } w.Header().Set("Content-Type", "text/html; charset=utf-8") t.Execute(w, data) } // FileInfo содержит информацию о файле type FileInfo struct { Name string Size string ModTime string Preview string } // getResultFiles возвращает список файлов из папки результатов func getResultFiles() ([]FileInfo, error) { entries, err := os.ReadDir(config.AppConfig.ResultFolder) if err != nil { return nil, err } var files []FileInfo for _, entry := range entries { if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".md") { continue } info, err := entry.Info() if err != nil { continue } // Читаем превью файла (первые 200 символов) и конвертируем Markdown preview := "" if content, err := os.ReadFile(filepath.Join(config.AppConfig.ResultFolder, entry.Name())); err == nil { // Конвертируем Markdown в HTML для превью htmlContent := blackfriday.Run(content) preview = strings.TrimSpace(string(htmlContent)) // Удаляем HTML теги для превью preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "

", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "
", "")
			preview = strings.ReplaceAll(preview, "
", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "", "") preview = strings.ReplaceAll(preview, "
  • ", "• ") preview = strings.ReplaceAll(preview, "
  • ", "") preview = strings.ReplaceAll(preview, "
      ", "") preview = strings.ReplaceAll(preview, "
    ", "") preview = strings.ReplaceAll(preview, "
    ", "") preview = strings.ReplaceAll(preview, "
    ", "") preview = strings.ReplaceAll(preview, "
    ", "") preview = strings.ReplaceAll(preview, "
    ", "") preview = strings.ReplaceAll(preview, "
    ", "") // Очищаем от лишних пробелов и переносов preview = strings.ReplaceAll(preview, "\n", " ") preview = strings.ReplaceAll(preview, "\r", "") preview = strings.ReplaceAll(preview, " ", " ") preview = strings.TrimSpace(preview) if len(preview) > 200 { preview = preview[:200] + "..." } } files = append(files, FileInfo{ Name: entry.Name(), Size: formatFileSize(info.Size()), ModTime: info.ModTime().Format("02.01.2006 15:04"), Preview: preview, }) } // Сортируем по времени изменения (новые сверху) for i := 0; i < len(files)-1; i++ { for j := i + 1; j < len(files); j++ { if files[i].ModTime < files[j].ModTime { files[i], files[j] = files[j], files[i] } } } return files, nil } // formatFileSize форматирует размер файла в читаемый вид func formatFileSize(size int64) string { const unit = 1024 if size < unit { return fmt.Sprintf("%d B", size) } div, exp := int64(unit), 0 for n := size / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f %cB", float64(size)/float64(div), "KMGTPE"[exp]) } // handleFileView обрабатывает просмотр конкретного файла func handleFileView(w http.ResponseWriter, r *http.Request) { filename := strings.TrimPrefix(r.URL.Path, "/file/") if filename == "" { http.NotFound(w, r) return } // Проверяем, что файл существует и находится в папке результатов filePath := filepath.Join(config.AppConfig.ResultFolder, filename) if !strings.HasPrefix(filePath, config.AppConfig.ResultFolder) { http.NotFound(w, r) return } content, err := os.ReadFile(filePath) if err != nil { http.NotFound(w, r) return } // Конвертируем Markdown в HTML htmlContent := blackfriday.Run(content) // Создаем HTML страницу с красивым отображением htmlPage := fmt.Sprintf(` %s - LCG Results

    📄 %s

    ← Назад к списку
    %s
    `, filename, filename, string(htmlContent)) // Устанавливаем заголовки для отображения HTML w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(htmlPage)) } // handleDeleteFile обрабатывает удаление файла func handleDeleteFile(w http.ResponseWriter, r *http.Request) { // Проверяем метод запроса if r.Method != "DELETE" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } filename := strings.TrimPrefix(r.URL.Path, "/delete/") if filename == "" { http.NotFound(w, r) return } // Проверяем, что файл существует и находится в папке результатов filePath := filepath.Join(config.AppConfig.ResultFolder, filename) if !strings.HasPrefix(filePath, config.AppConfig.ResultFolder) { http.NotFound(w, r) return } // Проверяем, что файл существует if _, err := os.Stat(filePath); os.IsNotExist(err) { http.NotFound(w, r) return } // Удаляем файл err := os.Remove(filePath) if err != nil { http.Error(w, fmt.Sprintf("Ошибка удаления файла: %v", err), http.StatusInternalServerError) return } // Возвращаем успешный ответ w.WriteHeader(http.StatusOK) w.Write([]byte("Файл успешно удален")) } // handleHistoryPage обрабатывает страницу истории запросов func handleHistoryPage(w http.ResponseWriter, r *http.Request) { historyEntries, err := readHistoryEntries() if err != nil { http.Error(w, fmt.Sprintf("Ошибка чтения истории: %v", err), http.StatusInternalServerError) return } tmpl := ` История запросов - LCG Results

    📝 История запросов

    Управление историей запросов Linux Command GPT

    {{if .Entries}} {{range .Entries}}
    #{{.Index}} {{.Timestamp}}
    {{.Command}}
    {{.Response}}
    {{end}} {{else}}

    📝 История пуста

    Здесь будут отображаться запросы после использования команды lcg

    {{end}}
    ` t, err := template.New("history").Parse(tmpl) if err != nil { http.Error(w, "Ошибка шаблона", http.StatusInternalServerError) return } data := struct { Entries []HistoryEntryInfo }{ Entries: historyEntries, } w.Header().Set("Content-Type", "text/html; charset=utf-8") t.Execute(w, data) } // HistoryEntryInfo содержит информацию о записи истории для отображения type HistoryEntryInfo struct { Index int Command string Response string Timestamp string } // readHistoryEntries читает записи истории func readHistoryEntries() ([]HistoryEntryInfo, error) { entries, err := read(config.AppConfig.ResultHistory) if err != nil { return nil, err } var result []HistoryEntryInfo for _, entry := range entries { result = append(result, HistoryEntryInfo{ Index: entry.Index, Command: entry.Command, Response: entry.Response, Timestamp: entry.Timestamp.Format("02.01.2006 15:04:05"), }) } return result, nil } // handleDeleteHistoryEntry обрабатывает удаление записи истории func handleDeleteHistoryEntry(w http.ResponseWriter, r *http.Request) { if r.Method != "DELETE" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } indexStr := strings.TrimPrefix(r.URL.Path, "/history/delete/") index, err := strconv.Atoi(indexStr) if err != nil { http.Error(w, "Invalid index", http.StatusBadRequest) return } err = DeleteHistoryEntry(config.AppConfig.ResultHistory, index) if err != nil { http.Error(w, fmt.Sprintf("Ошибка удаления: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Запись успешно удалена")) } // handleClearHistory обрабатывает очистку всей истории func handleClearHistory(w http.ResponseWriter, r *http.Request) { if r.Method != "DELETE" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } err := os.WriteFile(config.AppConfig.ResultHistory, []byte("[]"), 0644) if err != nil { http.Error(w, fmt.Sprintf("Ошибка очистки: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("История успешно очищена")) } // handlePromptsPage обрабатывает страницу управления промптами func handlePromptsPage(w http.ResponseWriter, r *http.Request) { // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов (использует конфигурацию из config.AppConfig.PromptFolder) pm := gpt.NewPromptManager(homeDir) // Получаем язык из параметра запроса, если не указан - берем из файла lang := r.URL.Query().Get("lang") if lang == "" { lang = pm.GetCurrentLanguage() } tmpl := ` Системные промпты - LCG Results

    ⚙️ Системные промпты

    Управление системными промптами Linux Command GPT

    {{if .Prompts}} {{range .Prompts}}
    #{{.ID}} {{.Name}} {{if .IsDefault}}Встроенный{{end}}
    {{.Description}}
    {{.Content}}
    {{end}} {{else}}

    ⚙️ Промпты не найдены

    Добавьте пользовательские промпты для настройки поведения системы

    {{end}}
    {{if .VerbosePrompts}} {{range .VerbosePrompts}}
    #{{.Mode}} {{.Name}} {{if .IsDefault}}Встроенный{{end}}
    {{.Description}}
    {{.Content}}
    {{end}} {{else}}

    📝 Промпты подробности

    Промпты для режимов v, vv, vvv

    {{end}}
    ` t, err := template.New("prompts").Parse(tmpl) if err != nil { http.Error(w, "Ошибка шаблона", http.StatusInternalServerError) return } // Создаем структуру с дополнительным полем IsDefault type PromptWithDefault struct { gpt.SystemPrompt IsDefault bool } // Получаем текущий язык из файла currentLang := pm.GetCurrentLanguage() // Если язык не указан в URL, используем язык из файла if lang == "" { lang = currentLang } // Получаем системные промпты с учетом языка systemPrompts := getSystemPromptsWithLang(pm.Prompts, lang) var promptsWithDefault []PromptWithDefault for _, prompt := range systemPrompts { // Показываем только системные промпты (ID 1-5) на первой вкладке if prompt.ID >= 1 && prompt.ID <= 5 { // Проверяем, является ли промпт встроенным и неизмененным isDefault := gpt.IsBuiltinPrompt(prompt) promptsWithDefault = append(promptsWithDefault, PromptWithDefault{ SystemPrompt: prompt, IsDefault: isDefault, }) } } // Получаем промпты подробности из файла sys_prompts verbosePrompts := getVerbosePromptsFromFile(pm.Prompts, lang) data := struct { Prompts []PromptWithDefault VerbosePrompts []VerbosePrompt Lang string }{ Prompts: promptsWithDefault, VerbosePrompts: verbosePrompts, Lang: lang, } w.Header().Set("Content-Type", "text/html; charset=utf-8") t.Execute(w, data) } // handleAddPrompt обрабатывает добавление нового промпта func handleAddPrompt(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов (использует конфигурацию из config.AppConfig.PromptFolder) pm := gpt.NewPromptManager(homeDir) // Парсим JSON данные var promptData struct { Name string `json:"name"` Description string `json:"description"` Content string `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&promptData); err != nil { http.Error(w, "Ошибка парсинга JSON", http.StatusBadRequest) return } // Добавляем промпт if err := pm.AddPrompt(promptData.Name, promptData.Description, promptData.Content); err != nil { http.Error(w, fmt.Sprintf("Ошибка добавления промпта: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Промпт успешно добавлен")) } // handleEditPrompt обрабатывает редактирование промпта func handleEditPrompt(w http.ResponseWriter, r *http.Request) { if r.Method != "PUT" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Получаем ID из URL idStr := strings.TrimPrefix(r.URL.Path, "/prompts/edit/") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Неверный ID промпта", http.StatusBadRequest) return } // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов (использует конфигурацию из config.AppConfig.PromptFolder) pm := gpt.NewPromptManager(homeDir) // Парсим JSON данные var promptData struct { Name string `json:"name"` Description string `json:"description"` Content string `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&promptData); err != nil { http.Error(w, "Ошибка парсинга JSON", http.StatusBadRequest) return } // Обновляем промпт if err := pm.UpdatePrompt(id, promptData.Name, promptData.Description, promptData.Content); err != nil { http.Error(w, fmt.Sprintf("Ошибка обновления промпта: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Промпт успешно обновлен")) } // handleDeletePrompt обрабатывает удаление промпта func handleDeletePrompt(w http.ResponseWriter, r *http.Request) { if r.Method != "DELETE" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Получаем ID из URL idStr := strings.TrimPrefix(r.URL.Path, "/prompts/delete/") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Неверный ID промпта", http.StatusBadRequest) return } // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов (использует конфигурацию из config.AppConfig.PromptFolder) pm := gpt.NewPromptManager(homeDir) // Удаляем промпт if err := pm.DeletePrompt(id); err != nil { http.Error(w, fmt.Sprintf("Ошибка удаления промпта: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Промпт успешно удален")) } // VerbosePrompt структура для промптов подробности type VerbosePrompt struct { Mode string Name string Description string Content string IsDefault bool } // getVerbosePromptsFromFile возвращает промпты подробности из файла sys_prompts func getVerbosePromptsFromFile(prompts []gpt.SystemPrompt, lang string) []VerbosePrompt { var verbosePrompts []VerbosePrompt // Ищем промпты подробности в загруженных промптах (ID 6, 7, 8) for _, prompt := range prompts { if prompt.ID >= 6 && prompt.ID <= 8 { // Определяем режим по ID var mode string switch prompt.ID { case 6: mode = "v" case 7: mode = "vv" case 8: mode = "vvv" } // Переводим на нужный язык если необходимо translatedPrompt := translateVerbosePrompt(prompt, lang) verbosePrompts = append(verbosePrompts, VerbosePrompt{ Mode: mode, Name: translatedPrompt.Name, Description: translatedPrompt.Description, Content: translatedPrompt.Content, IsDefault: gpt.IsBuiltinPrompt(translatedPrompt), // Проверяем, является ли промпт встроенным }) } } // Если промпты подробности не найдены в файле, используем встроенные if len(verbosePrompts) == 0 { return getVerbosePrompts(lang) } return verbosePrompts } // translateVerbosePrompt переводит промпт подробности на указанный язык func translateVerbosePrompt(prompt gpt.SystemPrompt, lang string) gpt.SystemPrompt { // Получаем встроенный промпт для указанного языка из YAML if builtinPrompt := gpt.GetBuiltinPromptByIDAndLanguage(prompt.ID, lang); builtinPrompt != nil { return *builtinPrompt } // Если перевод не найден, возвращаем оригинал return prompt } // getVerbosePrompts возвращает промпты для режимов v/vv/vvv (fallback) func getVerbosePrompts(lang string) []VerbosePrompt { // Английские версии (по умолчанию) enPrompts := []VerbosePrompt{ { Mode: "v", Name: "Verbose Mode", Description: "Detailed explanation of the command", Content: "Provide a brief explanation of what this Linux command does, including what each flag and option means, and give examples of usage.", IsDefault: true, }, { Mode: "vv", Name: "Very Verbose Mode", Description: "Comprehensive explanation with alternatives", Content: "Provide a comprehensive explanation of this Linux command, including detailed descriptions of all flags and options, alternative approaches, common use cases, and potential pitfalls to avoid.", IsDefault: true, }, { Mode: "vvv", Name: "Maximum Verbose Mode", Description: "Complete guide with examples and best practices", Content: "Provide a complete guide for this Linux command, including detailed explanations of all options, multiple examples with different scenarios, alternative commands that achieve similar results, best practices, troubleshooting tips, and related commands that work well together.", IsDefault: true, }, } // Русские версии ruPrompts := []VerbosePrompt{ { Mode: "v", Name: "Подробный режим", Description: "Подробное объяснение команды", Content: "Предоставь краткое объяснение того, что делает эта Linux команда, включая значение каждого флага и опции, и приведи примеры использования.", IsDefault: true, }, { Mode: "vv", Name: "Очень подробный режим", Description: "Исчерпывающее объяснение с альтернативами", Content: "Предоставь исчерпывающее объяснение этой Linux команды, включая подробные описания всех флагов и опций, альтернативные подходы, распространенные случаи использования и потенциальные подводные камни, которых следует избегать.", IsDefault: true, }, { Mode: "vvv", Name: "Максимально подробный режим", Description: "Полное руководство с примерами и лучшими практиками", Content: "Предоставь полное руководство по этой Linux команде, включая подробные объяснения всех опций, множественные примеры с различными сценариями, альтернативные команды, которые дают аналогичные результаты, лучшие практики, советы по устранению неполадок и связанные команды, которые хорошо работают вместе.", IsDefault: true, }, } if lang == "ru" { return ruPrompts } return enPrompts } // getSystemPromptsWithLang возвращает системные промпты с учетом языка func getSystemPromptsWithLang(prompts []gpt.SystemPrompt, lang string) []gpt.SystemPrompt { // Если язык английский, возвращаем оригинальные промпты if lang == "en" { return prompts } // Для русского языка переводим только встроенные промпты var translatedPrompts []gpt.SystemPrompt for _, prompt := range prompts { // Проверяем, является ли это встроенным промптом if gpt.IsBuiltinPrompt(prompt) { // Переводим встроенные промпты на русский translated := translateSystemPrompt(prompt, lang) translatedPrompts = append(translatedPrompts, translated) } else { translatedPrompts = append(translatedPrompts, prompt) } } return translatedPrompts } // translateSystemPrompt переводит системный промпт на указанный язык func translateSystemPrompt(prompt gpt.SystemPrompt, lang string) gpt.SystemPrompt { // Получаем встроенный промпт для указанного языка из YAML if builtinPrompt := gpt.GetBuiltinPromptByIDAndLanguage(prompt.ID, lang); builtinPrompt != nil { return *builtinPrompt } // Если перевод не найден, возвращаем оригинал return prompt } // handleSaveLang обрабатывает сохранение промптов при переключении языка func handleSaveLang(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов pm := gpt.NewPromptManager(homeDir) // Парсим JSON данные var langData struct { Lang string `json:"lang"` } if err := json.NewDecoder(r.Body).Decode(&langData); err != nil { http.Error(w, "Ошибка парсинга JSON", http.StatusBadRequest) return } // Устанавливаем язык файла pm.SetLanguage(langData.Lang) // Переводим только встроенные промпты (по ID), а пользовательские оставляем как есть var translatedPrompts []gpt.SystemPrompt for _, p := range pm.Prompts { // Проверяем, является ли промпт встроенным по ID (1-8) if pm.IsDefaultPromptByID(p) { // System (1-5) и Verbose (6-8) if p.ID >= 1 && p.ID <= 5 { translatedPrompts = append(translatedPrompts, translateSystemPrompt(p, langData.Lang)) } else if p.ID >= 6 && p.ID <= 8 { translatedPrompts = append(translatedPrompts, translateVerbosePrompt(p, langData.Lang)) } else { translatedPrompts = append(translatedPrompts, p) } } else { // Пользовательские промпты (ID > 8) не трогаем translatedPrompts = append(translatedPrompts, p) } } // Обновляем в pm и сохраняем pm.Prompts = translatedPrompts if err := pm.SaveAllPrompts(); err != nil { http.Error(w, fmt.Sprintf("Ошибка сохранения: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Промпты сохранены")) } // handleRestorePrompt восстанавливает системный промпт к значению по умолчанию func handleRestorePrompt(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Получаем ID из URL idStr := strings.TrimPrefix(r.URL.Path, "/prompts/restore/") id, err := strconv.Atoi(idStr) if err != nil { http.Error(w, "Invalid prompt ID", http.StatusBadRequest) return } // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов pm := gpt.NewPromptManager(homeDir) // Получаем текущий язык currentLang := pm.GetCurrentLanguage() // Получаем встроенный промпт для текущего языка builtinPrompt := gpt.GetBuiltinPromptByIDAndLanguage(id, currentLang) if builtinPrompt == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "error": "Промпт не найден в встроенных", }) return } // Обновляем промпт в списке for i, prompt := range pm.Prompts { if prompt.ID == id { pm.Prompts[i] = *builtinPrompt break } } // Сохраняем изменения if err := pm.SaveAllPrompts(); err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "error": "Ошибка сохранения: " + err.Error(), }) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, }) } // handleRestoreVerbosePrompt восстанавливает verbose промпт к значению по умолчанию func handleRestoreVerbosePrompt(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Получаем режим из URL mode := strings.TrimPrefix(r.URL.Path, "/prompts/restore-verbose/") // Получаем домашнюю директорию пользователя homeDir, err := os.UserHomeDir() if err != nil { http.Error(w, "Ошибка получения домашней директории", http.StatusInternalServerError) return } // Создаем менеджер промптов pm := gpt.NewPromptManager(homeDir) // Получаем текущий язык currentLang := pm.GetCurrentLanguage() // Определяем ID по режиму var id int switch mode { case "v": id = 6 case "vv": id = 7 case "vvv": id = 8 default: w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "error": "Неверный режим промпта", }) return } // Получаем встроенный промпт для текущего языка builtinPrompt := gpt.GetBuiltinPromptByIDAndLanguage(id, currentLang) if builtinPrompt == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "error": "Промпт не найден в встроенных", }) return } // Обновляем промпт в списке for i, prompt := range pm.Prompts { if prompt.ID == id { pm.Prompts[i] = *builtinPrompt break } } // Сохраняем изменения if err := pm.SaveAllPrompts(); err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]interface{}{ "success": false, "error": "Ошибка сохранения: " + err.Error(), }) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "success": true, }) }