tagliatelle

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

include-admin.go (7994B)


      1 package main
      2 
      3 import (
      4 	"fmt"
      5 	"io"
      6 	"log"
      7 	"net/http"
      8 	"os"
      9 	"path/filepath"
     10 	"strconv"
     11 	"strings"
     12 	"time"
     13 )
     14 
     15 type AdminPageData struct {
     16 	Config            Config
     17 	Error             string
     18 	Success           string
     19 	OrphanData        OrphanData
     20 	ActiveTab         string
     21 	MissingThumbnails []VideoFile
     22 }
     23 
     24 func renderAdminPage(w http.ResponseWriter, r *http.Request, data AdminPageData) {
     25 	if data.ActiveTab == "" {
     26 		data.ActiveTab = r.FormValue("active_tab")
     27 	}
     28 	pageData := buildPageData("Admin", data)
     29 	renderTemplate(w, "admin.html", pageData)
     30 }
     31 
     32 func currentAdminState(r *http.Request, orphanData OrphanData, missingThumbnails []VideoFile) AdminPageData {
     33 	return AdminPageData{
     34 		Config:            config,
     35 		OrphanData:        orphanData,
     36 		ActiveTab:         r.FormValue("active_tab"),
     37 		MissingThumbnails: missingThumbnails,
     38 	}
     39 }
     40 
     41 func validateConfig(newConfig Config) error {
     42 	if newConfig.GallerySize == "" {
     43 		return fmt.Errorf("gallery size cannot be empty")
     44 	}
     45 	if newConfig.ItemsPerPage == "" {
     46 		return fmt.Errorf("items per page cannot be empty")
     47 	}
     48 	return nil
     49 }
     50 
     51 func adminHandler(w http.ResponseWriter, r *http.Request) {
     52 	// Get orphaned files
     53 	orphanData, err := getOrphanedFiles(config.UploadDir)
     54 	if err != nil {
     55 		log.Printf("Warning: adminHandler: failed to get orphaned files: %v", err)
     56 	}
     57 
     58 	// Get video files for thumbnails
     59 	missingThumbnails, err := getMissingThumbnailVideos()
     60 	if err != nil {
     61 		log.Printf("Warning: adminHandler: failed to get missing thumbnails: %v", err)
     62 	}
     63 
     64 	switch r.Method {
     65 	case http.MethodPost:
     66 		switch r.FormValue("action") {
     67 		case "save", "":
     68 			handleSaveSettings(w, r, orphanData, missingThumbnails)
     69 
     70 		case "backup":
     71 			err := backupDatabase(config.DatabasePath)
     72 			if err != nil {
     73 				log.Printf("Error: adminHandler: database backup failed: %v", err)
     74 			}
     75 			data := currentAdminState(r, orphanData, missingThumbnails)
     76 			data.Error = errorString(err)
     77 			data.Success = successString(err, "Database backup created successfully!")
     78 			renderAdminPage(w, r, data)
     79 
     80 		case "vacuum":
     81 			err := vacuumDatabase()
     82 			if err != nil {
     83 				log.Printf("Error: adminHandler: database vacuum failed: %v", err)
     84 			}
     85 			data := currentAdminState(r, orphanData, missingThumbnails)
     86 			data.Error = errorString(err)
     87 			data.Success = successString(err, "Database vacuum completed successfully!")
     88 			renderAdminPage(w, r, data)
     89 
     90 		case "save_aliases":
     91 			handleSaveAliases(w, r, orphanData, missingThumbnails)
     92 
     93 		case "save_sed_rules":
     94 			handleSaveSedRules(w, r, orphanData, missingThumbnails)
     95 
     96 		case "compute_properties":
     97 			handleComputeProperties(w, r, orphanData, missingThumbnails)
     98 		}
     99 
    100 	default:
    101 		renderAdminPage(w, r, currentAdminState(r, orphanData, missingThumbnails))
    102 	}
    103 }
    104 
    105 func parseAliasesFromForm(r *http.Request) []TagAliasGroup {
    106 	var groups []TagAliasGroup
    107 	for i := 0; ; i++ {
    108 		category := strings.TrimSpace(r.FormValue(fmt.Sprintf("aliases[%d][category]", i)))
    109 		if category == "" {
    110 			break
    111 		}
    112 		var aliases []string
    113 		for j := 0; ; j++ {
    114 			v := strings.TrimSpace(r.FormValue(fmt.Sprintf("aliases[%d][aliases][%d]", i, j)))
    115 			if v == "" {
    116 				break
    117 			}
    118 			aliases = append(aliases, v)
    119 		}
    120 		if len(aliases) >= 2 {
    121 			groups = append(groups, TagAliasGroup{Category: category, Aliases: aliases})
    122 		} else {
    123 			log.Printf("Warning: parseAliasesFromForm: alias group for category %q has fewer than 2 aliases (%d) and was discarded", category, len(aliases))
    124 		}
    125 	}
    126 	return groups
    127 }
    128 
    129 func parseSedRulesFromForm(r *http.Request) ([]SedRule, error) {
    130 	var rules []SedRule
    131 	for i := 0; ; i++ {
    132 		name := strings.TrimSpace(r.FormValue(fmt.Sprintf("sed_rules[%d][name]", i)))
    133 		if name == "" {
    134 			break
    135 		}
    136 		command := strings.TrimSpace(r.FormValue(fmt.Sprintf("sed_rules[%d][command]", i)))
    137 		if command == "" {
    138 			return nil, fmt.Errorf("rule %s is missing a command", strconv.Itoa(i+1))
    139 		}
    140 		rules = append(rules, SedRule{
    141 			Name:        name,
    142 			Description: strings.TrimSpace(r.FormValue(fmt.Sprintf("sed_rules[%d][description]", i))),
    143 			Command:     command,
    144 		})
    145 	}
    146 	return rules, nil
    147 }
    148 
    149 func handleSaveAliases(w http.ResponseWriter, r *http.Request, orphanData OrphanData, missingThumbnails []VideoFile) {
    150 	config.TagAliases = parseAliasesFromForm(r)
    151 
    152 	if err := SaveConfig(db, config); err != nil {
    153 		log.Printf("Error: handleSaveAliases: failed to save configuration: %v", err)
    154 		data := currentAdminState(r, orphanData, missingThumbnails)
    155 		data.Error = "Failed to save configuration: " + err.Error()
    156 		renderAdminPage(w, r, data)
    157 		return
    158 	}
    159 
    160 	data := currentAdminState(r, orphanData, missingThumbnails)
    161 	data.Success = "Tag aliases saved successfully!"
    162 	renderAdminPage(w, r, data)
    163 }
    164 
    165 func handleSaveSettings(w http.ResponseWriter, r *http.Request, orphanData OrphanData, missingThumbnails []VideoFile) {
    166 	newConfig := config // preserve runtime fields
    167 	newConfig.GallerySize = strings.TrimSpace(r.FormValue("gallery_size"))
    168 	newConfig.ItemsPerPage = strings.TrimSpace(r.FormValue("items_per_page"))
    169 
    170 	if err := validateConfig(newConfig); err != nil {
    171 		data := currentAdminState(r, orphanData, missingThumbnails)
    172 		data.Error = err.Error()
    173 		renderAdminPage(w, r, data)
    174 		return
    175 	}
    176 
    177 	config = newConfig
    178 
    179 	if err := SaveConfig(db, config); err != nil {
    180 		log.Printf("Error: handleSaveSettings: failed to save configuration: %v", err)
    181 		data := currentAdminState(r, orphanData, missingThumbnails)
    182 		data.Error = "Failed to save configuration: " + err.Error()
    183 		renderAdminPage(w, r, data)
    184 		return
    185 	}
    186 
    187 	data := currentAdminState(r, orphanData, missingThumbnails)
    188 	data.Success = "Settings saved successfully!"
    189 	renderAdminPage(w, r, data)
    190 }
    191 
    192 func handleSaveSedRules(w http.ResponseWriter, r *http.Request, orphanData OrphanData, missingThumbnails []VideoFile) {
    193 	rules, err := parseSedRulesFromForm(r)
    194 	if err != nil {
    195 		data := currentAdminState(r, orphanData, missingThumbnails)
    196 		data.Error = err.Error()
    197 		renderAdminPage(w, r, data)
    198 		return
    199 	}
    200 
    201 	config.SedRules = rules
    202 
    203 	if err := SaveConfig(db, config); err != nil {
    204 		log.Printf("Error: handleSaveSedRules: failed to save configuration: %v", err)
    205 		data := currentAdminState(r, orphanData, missingThumbnails)
    206 		data.Error = "Failed to save configuration: " + err.Error()
    207 		renderAdminPage(w, r, data)
    208 		return
    209 	}
    210 
    211 	data := currentAdminState(r, orphanData, missingThumbnails)
    212 	data.Success = "Sed rules saved successfully!"
    213 	renderAdminPage(w, r, data)
    214 }
    215 
    216 func backupDatabase(dbPath string) error {
    217 	if dbPath == "" {
    218 		return fmt.Errorf("database path not configured")
    219 	}
    220 
    221 	backupDir := filepath.Join(filepath.Dir(dbPath), "backups")
    222 	if err := os.MkdirAll(backupDir, 0755); err != nil {
    223 		return fmt.Errorf("failed to create backups directory: %w", err)
    224 	}
    225 
    226 	dbName := strings.TrimSuffix(filepath.Base(dbPath), filepath.Ext(dbPath))
    227 	timestamp := time.Now().Format("20060102_150405")
    228 	backupPath := filepath.Join(backupDir, fmt.Sprintf("%s_backup_%s.db", dbName, timestamp))
    229 
    230 	input, err := os.Open(dbPath)
    231 	if err != nil {
    232 		return fmt.Errorf("failed to open database: %w", err)
    233 	}
    234 	defer input.Close()
    235 
    236 	output, err := os.Create(backupPath)
    237 	if err != nil {
    238 		return fmt.Errorf("failed to create backup file: %w", err)
    239 	}
    240 	defer output.Close()
    241 
    242 	if _, err := io.Copy(output, input); err != nil {
    243 		return fmt.Errorf("failed to copy database: %w", err)
    244 	}
    245 
    246 	log.Printf("Info: backupDatabase: backup written to %s", backupPath)
    247 	return nil
    248 }
    249 
    250 func vacuumDatabase() error {
    251 	if _, err := db.Exec("VACUUM;"); err != nil {
    252 		return fmt.Errorf("VACUUM failed: %w", err)
    253 	}
    254 
    255 	log.Printf("Info: vacuumDatabase: VACUUM completed successfully")
    256 	return nil
    257 }
    258 
    259 func getFilesInDB() (map[string]bool, error) {
    260 	rows, err := db.Query("SELECT filename FROM files")
    261 	if err != nil {
    262 		return nil, err
    263 	}
    264 	defer rows.Close()
    265 
    266 	fileMap := make(map[string]bool)
    267 	for rows.Next() {
    268 		var name string
    269 		if err := rows.Scan(&name); err != nil {
    270 			return nil, err
    271 		}
    272 		fileMap[name] = true
    273 	}
    274 	if err := rows.Err(); err != nil {
    275 		return nil, err
    276 	}
    277 	return fileMap, nil
    278 }