mobile version styled -ready for new version 2.0.1

This commit is contained in:
2025-10-22 18:37:08 +06:00
parent 344f763bb4
commit e37599d3ef
28 changed files with 4998 additions and 2224 deletions

205
serve/api.go Normal file
View File

@@ -0,0 +1,205 @@
package serve
import (
"encoding/json"
"fmt"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/direct-dev-ru/linux-command-gpt/config"
)
// SaveResultRequest представляет запрос на сохранение результата
type SaveResultRequest struct {
Prompt string `json:"prompt"`
Command string `json:"command"`
Explanation string `json:"explanation,omitempty"`
Model string `json:"model"`
}
// SaveResultResponse представляет ответ на сохранение результата
type SaveResultResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
File string `json:"file,omitempty"`
Error string `json:"error,omitempty"`
}
// AddToHistoryRequest представляет запрос на добавление в историю
type AddToHistoryRequest struct {
Prompt string `json:"prompt"`
Command string `json:"command"`
Response string `json:"response"`
Explanation string `json:"explanation,omitempty"`
System string `json:"system"`
}
// AddToHistoryResponse представляет ответ на добавление в историю
type AddToHistoryResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Error string `json:"error,omitempty"`
}
// handleSaveResult обрабатывает сохранение результата
func handleSaveResult(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req SaveResultRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if req.Prompt == "" || req.Command == "" {
http.Error(w, "Prompt and command are required", http.StatusBadRequest)
return
}
// Создаем папку результатов если не существует
if err := os.MkdirAll(config.AppConfig.ResultFolder, 0755); err != nil {
apiJsonResponse(w, SaveResultResponse{
Success: false,
Error: "Failed to create result folder",
})
return
}
// Генерируем имя файла
timestamp := time.Now().Format("2006-01-02_15-04-05")
filename := fmt.Sprintf("gpt_request_%s_%s.md", req.Model, timestamp)
filePath := path.Join(config.AppConfig.ResultFolder, filename)
title := truncateTitle(req.Prompt)
// Формируем содержимое
var content string
if strings.TrimSpace(req.Explanation) != "" {
content = fmt.Sprintf("# %s\n\n## Prompt\n\n%s\n\n## Response\n\n%s\n\n## Explanation\n\n%s\n",
title, req.Prompt, req.Command, req.Explanation)
} else {
content = fmt.Sprintf("# %s\n\n## Prompt\n\n%s\n\n## Response\n\n%s\n",
title, req.Prompt, req.Command)
}
// Сохраняем файл
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
apiJsonResponse(w, SaveResultResponse{
Success: false,
Error: "Failed to save file",
})
return
}
// Debug вывод для сохранения результата
PrintWebSaveDebugInfo("SAVE_RESULT", req.Prompt, req.Command, req.Explanation, req.Model, filename)
apiJsonResponse(w, SaveResultResponse{
Success: true,
Message: "Result saved successfully",
File: filename,
})
}
// handleAddToHistory обрабатывает добавление в историю
func handleAddToHistory(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req AddToHistoryRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if req.Prompt == "" || req.Command == "" || req.Response == "" {
http.Error(w, "Prompt, command and response are required", http.StatusBadRequest)
return
}
// Проверяем, есть ли уже такой запрос в истории
entries, err := Read(config.AppConfig.ResultHistory)
if err != nil {
// Если файл не существует, создаем пустой массив
entries = []HistoryEntry{}
}
// Ищем дубликат
duplicateIndex := -1
for i, entry := range entries {
if strings.EqualFold(strings.TrimSpace(entry.Command), strings.TrimSpace(req.Prompt)) {
duplicateIndex = i
break
}
}
// Создаем новую запись
newEntry := HistoryEntry{
Index: len(entries) + 1,
Command: req.Prompt,
Response: req.Response,
Explanation: req.Explanation,
System: req.System,
Timestamp: time.Now(),
}
if duplicateIndex == -1 {
// Добавляем новую запись
entries = append(entries, newEntry)
} else {
// Перезаписываем существующую
newEntry.Index = entries[duplicateIndex].Index
entries[duplicateIndex] = newEntry
}
// Сохраняем историю
if err := Write(config.AppConfig.ResultHistory, entries); err != nil {
apiJsonResponse(w, AddToHistoryResponse{
Success: false,
Error: "Failed to save to history",
})
return
}
message := "Added to history successfully"
if duplicateIndex != -1 {
message = "Updated existing history entry"
}
// Debug вывод для добавления в историю
PrintWebSaveDebugInfo("ADD_TO_HISTORY", req.Prompt, req.Command, req.Explanation, req.System, "")
apiJsonResponse(w, AddToHistoryResponse{
Success: true,
Message: message,
})
}
// apiJsonResponse отправляет JSON ответ
func apiJsonResponse(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(data); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
}
}
// truncateTitle сокращает строку до 120 символов (по рунам), добавляя " ..." при усечении
func truncateTitle(s string) string {
const maxLen = 120
if runeCount := len([]rune(s)); runeCount <= maxLen {
return s
}
const head = 116
r := []rune(s)
if len(r) <= head {
return s
}
return string(r[:head]) + " ..."
}