Files
go-lcg/serve/results.go

254 lines
7.1 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 serve
import (
"fmt"
"html/template"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"unicode"
"github.com/direct-dev-ru/linux-command-gpt/config"
"github.com/direct-dev-ru/linux-command-gpt/serve/templates"
"github.com/russross/blackfriday/v2"
)
// generateAbbreviation создает аббревиатуру из первых букв слов в названии приложения
func generateAbbreviation(appName string) string {
words := strings.Fields(appName)
var abbreviation strings.Builder
for _, word := range words {
if len(word) > 0 {
// Берем первую букву слова, если это буква
firstRune := []rune(word)[0]
if unicode.IsLetter(firstRune) {
abbreviation.WriteRune(unicode.ToUpper(firstRune))
}
}
}
result := abbreviation.String()
if result == "" {
return "LCG" // Fallback если не удалось сгенерировать аббревиатуру
}
return result
}
// FileInfo содержит информацию о файле
type FileInfo struct {
Name string
Size string
ModTime string
Preview template.HTML
Content string // Полное содержимое для поиска
}
// 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 := templates.ResultsPageTemplate
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
BasePath string
AppName string
AppAbbreviation string
}{
Files: files,
TotalFiles: len(files),
RecentFiles: recentCount,
BasePath: getBasePath(),
AppName: config.AppConfig.AppName,
AppAbbreviation: generateAbbreviation(config.AppConfig.AppName),
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t.Execute(w, data)
}
// 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 символов) как обычный текст
preview := ""
fullContent := ""
if content, err := os.ReadFile(filepath.Join(config.AppConfig.ResultFolder, entry.Name())); err == nil {
// Сохраняем полное содержимое для поиска
fullContent = string(content)
// Берем первые 200 символов как превью
preview = string(content)
// Очищаем от лишних пробелов и переносов
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: template.HTML(preview),
Content: fullContent,
})
}
// Сортируем по времени изменения (новые сверху)
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)
// Создаем данные для шаблона
data := struct {
Filename string
Content template.HTML
}{
Filename: filename,
Content: template.HTML(htmlContent),
}
// Парсим и выполняем шаблон
tmpl := templates.FileViewTemplate
t, err := template.New("file_view").Parse(tmpl)
if err != nil {
http.Error(w, "Ошибка шаблона", http.StatusInternalServerError)
return
}
// Устанавливаем заголовки для отображения HTML
w.Header().Set("Content-Type", "text/html; charset=utf-8")
t.Execute(w, data)
}
// 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("Файл успешно удален"))
}