tagliatelle

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

include-admin.go (8583B)


      1 package main
      2 
      3 import (
      4     "database/sql"
      5     "encoding/json"
      6     "fmt"
      7     "io"
      8     "io/ioutil"
      9     "net/http"
     10     "os"
     11     "path/filepath"
     12     "strings"
     13     "time"
     14 )
     15 
     16 func loadConfig() error {
     17 	config = Config{
     18 		DatabasePath: "./database.db",
     19 		UploadDir:    "uploads",
     20 		ServerPort:   ":8080",
     21 		InstanceName: "Tagliatelle",
     22 		GallerySize:  "400px",
     23 		ItemsPerPage: "100",
     24 		TagAliases:   []TagAliasGroup{},
     25 	}
     26 
     27 	if data, err := ioutil.ReadFile("config.json"); err == nil {
     28 		if err := json.Unmarshal(data, &config); err != nil {
     29 			return err
     30 		}
     31 	}
     32 
     33 	return os.MkdirAll(config.UploadDir, 0755)
     34 }
     35 
     36 func saveConfig() error {
     37 	data, err := json.MarshalIndent(config, "", "  ")
     38 	if err != nil {
     39 		return err
     40 	}
     41 	return ioutil.WriteFile("config.json", data, 0644)
     42 }
     43 
     44 func validateConfig(newConfig Config) error {
     45 	if newConfig.DatabasePath == "" {
     46 		return fmt.Errorf("database path cannot be empty")
     47 	}
     48 
     49 	if newConfig.UploadDir == "" {
     50 		return fmt.Errorf("upload directory cannot be empty")
     51 	}
     52 
     53 	if newConfig.ServerPort == "" || !strings.HasPrefix(newConfig.ServerPort, ":") {
     54 		return fmt.Errorf("server port must be in format ':8080'")
     55 	}
     56 
     57 	if err := os.MkdirAll(newConfig.UploadDir, 0755); err != nil {
     58 		return fmt.Errorf("cannot create upload directory: %v", err)
     59 	}
     60 
     61 	return nil
     62 }
     63 
     64 func adminHandler(w http.ResponseWriter, r *http.Request) {
     65 	// Get orphaned files
     66 	orphans, _ := getOrphanedFiles(config.UploadDir)
     67 
     68 	// Get video files for thumbnails
     69 	missingThumbnails, _ := getMissingThumbnailVideos()
     70 
     71 	switch r.Method {
     72 	case http.MethodPost:
     73 		action := r.FormValue("action")
     74 
     75 		switch action {
     76 		case "save", "":
     77 			handleSaveSettings(w, r, orphans, missingThumbnails)
     78 			return
     79 
     80 		case "backup":
     81 			err := backupDatabase(config.DatabasePath)
     82 			pageData := buildPageData("Admin", struct {
     83 				Config            Config
     84 				Error             string
     85 				Success           string
     86 				Orphans           []string
     87 				MissingThumbnails []VideoFile
     88 			}{
     89 				Config:            config,
     90 				Error:             errorString(err),
     91 				Success:           successString(err, "Database backup created successfully!"),
     92 				Orphans:           orphans,
     93 				MissingThumbnails: missingThumbnails,
     94 			})
     95 			renderTemplate(w, "admin.html", pageData)
     96 			return
     97 
     98 		case "vacuum":
     99 			err := vacuumDatabase(config.DatabasePath)
    100 			pageData := buildPageData("Admin", struct {
    101 				Config            Config
    102 				Error             string
    103 				Success           string
    104 				Orphans           []string
    105 				MissingThumbnails []VideoFile
    106 			}{
    107 				Config:            config,
    108 				Error:             errorString(err),
    109 				Success:           successString(err, "Database vacuum completed successfully!"),
    110 				Orphans:           orphans,
    111 				MissingThumbnails: missingThumbnails,
    112 			})
    113 			renderTemplate(w, "admin.html", pageData)
    114 			return
    115 
    116 		case "save_aliases":
    117 			handleSaveAliases(w, r, orphans, missingThumbnails)
    118 			return
    119 		}
    120 
    121 	default:
    122 		pageData := buildPageData("Admin", struct {
    123 			Config            Config
    124 			Error             string
    125 			Success           string
    126 			Orphans           []string
    127 			MissingThumbnails []VideoFile
    128 		}{
    129 			Config:            config,
    130 			Error:             "",
    131 			Success:           "",
    132 			Orphans:           orphans,
    133 			MissingThumbnails: missingThumbnails,
    134 		})
    135 		renderTemplate(w, "admin.html", pageData)
    136 	}
    137 }
    138 
    139 func handleSaveAliases(w http.ResponseWriter, r *http.Request, orphans []string, missingThumbnails []VideoFile) {
    140 	aliasesJSON := r.FormValue("aliases_json")
    141 
    142 	var aliases []TagAliasGroup
    143 	if aliasesJSON != "" {
    144 		if err := json.Unmarshal([]byte(aliasesJSON), &aliases); err != nil {
    145 			pageData := buildPageData("Admin", struct {
    146 				Config            Config
    147 				Error             string
    148 				Success           string
    149 				Orphans           []string
    150 				MissingThumbnails []VideoFile
    151 			}{
    152 				Config:            config,
    153 				Error:             "Invalid aliases JSON: " + err.Error(),
    154 				Success:           "",
    155 				Orphans:           orphans,
    156 				MissingThumbnails: missingThumbnails,
    157 			})
    158 			renderTemplate(w, "admin.html", pageData)
    159 			return
    160 		}
    161 	}
    162 
    163 	config.TagAliases = aliases
    164 
    165 	if err := saveConfig(); err != nil {
    166 		pageData := buildPageData("Admin", struct {
    167 			Config            Config
    168 			Error             string
    169 			Success           string
    170 			Orphans           []string
    171 			MissingThumbnails []VideoFile
    172 		}{
    173 			Config:            config,
    174 			Error:             "Failed to save configuration: " + err.Error(),
    175 			Success:           "",
    176 			Orphans:           orphans,
    177 			MissingThumbnails: missingThumbnails,
    178 		})
    179 		renderTemplate(w, "admin.html", pageData)
    180 		return
    181 	}
    182 
    183 	pageData := buildPageData("Admin", struct {
    184 		Config            Config
    185 		Error             string
    186 		Success           string
    187 		Orphans           []string
    188 		MissingThumbnails []VideoFile
    189 	}{
    190 		Config:            config,
    191 		Error:             "",
    192 		Success:           "Tag aliases saved successfully!",
    193 		Orphans:           orphans,
    194 		MissingThumbnails: missingThumbnails,
    195 	})
    196 	renderTemplate(w, "admin.html", pageData)
    197 }
    198 
    199 func handleSaveSettings(w http.ResponseWriter, r *http.Request, orphans []string, missingThumbnails []VideoFile) {
    200 	newConfig := Config{
    201 		DatabasePath: strings.TrimSpace(r.FormValue("database_path")),
    202 		UploadDir:    strings.TrimSpace(r.FormValue("upload_dir")),
    203 		ServerPort:   strings.TrimSpace(r.FormValue("server_port")),
    204 		InstanceName: strings.TrimSpace(r.FormValue("instance_name")),
    205 		GallerySize:  strings.TrimSpace(r.FormValue("gallery_size")),
    206 		ItemsPerPage: strings.TrimSpace(r.FormValue("items_per_page")),
    207 		TagAliases:   config.TagAliases, // Preserve existing aliases
    208 	}
    209 
    210 	if err := validateConfig(newConfig); err != nil {
    211 		pageData := buildPageData("Admin", struct {
    212 			Config            Config
    213 			Error             string
    214 			Success           string
    215 			Orphans           []string
    216 			MissingThumbnails []VideoFile
    217 		}{
    218 			Config:            config,
    219 			Error:             err.Error(),
    220 			Success:           "",
    221 			Orphans:           orphans,
    222 			MissingThumbnails: missingThumbnails,
    223 		})
    224 		renderTemplate(w, "admin.html", pageData)
    225 		return
    226 	}
    227 
    228 	needsRestart := (newConfig.DatabasePath != config.DatabasePath ||
    229 		newConfig.ServerPort != config.ServerPort)
    230 
    231 	config = newConfig
    232 	if err := saveConfig(); err != nil {
    233 		pageData := buildPageData("Admin", struct {
    234 			Config            Config
    235 			Error             string
    236 			Success           string
    237 			Orphans           []string
    238 			MissingThumbnails []VideoFile
    239 		}{
    240 			Config:            config,
    241 			Error:             "Failed to save configuration: " + err.Error(),
    242 			Success:           "",
    243 			Orphans:           orphans,
    244 			MissingThumbnails: missingThumbnails,
    245 		})
    246 		renderTemplate(w, "admin.html", pageData)
    247 		return
    248 	}
    249 
    250 	var message string
    251 	if needsRestart {
    252 		message = "Settings saved successfully! Please restart the server for database/port changes to take effect."
    253 	} else {
    254 		message = "Settings saved successfully!"
    255 	}
    256 
    257 	pageData := buildPageData("Admin", struct {
    258 		Config            Config
    259 		Error             string
    260 		Success           string
    261 		Orphans           []string
    262 		MissingThumbnails []VideoFile
    263 	}{
    264 		Config:            config,
    265 		Error:             "",
    266 		Success:           message,
    267 		Orphans:           orphans,
    268 		MissingThumbnails: missingThumbnails,
    269 	})
    270 	renderTemplate(w, "admin.html", pageData)
    271 }
    272 
    273 func backupDatabase(dbPath string) error {
    274 	if dbPath == "" {
    275 		return fmt.Errorf("database path not configured")
    276 	}
    277 
    278 	timestamp := time.Now().Format("20060102_150405")
    279 	backupPath := fmt.Sprintf("%s_backup_%s.db", strings.TrimSuffix(dbPath, filepath.Ext(dbPath)), timestamp)
    280 
    281 	input, err := os.Open(dbPath)
    282 	if err != nil {
    283 		return fmt.Errorf("failed to open database: %w", err)
    284 	}
    285 	defer input.Close()
    286 
    287 	output, err := os.Create(backupPath)
    288 	if err != nil {
    289 		return fmt.Errorf("failed to create backup file: %w", err)
    290 	}
    291 	defer output.Close()
    292 
    293 	if _, err := io.Copy(output, input); err != nil {
    294 		return fmt.Errorf("failed to copy database: %w", err)
    295 	}
    296 
    297 	return nil
    298 }
    299 
    300 func vacuumDatabase(dbPath string) error {
    301 	db, err := sql.Open("sqlite3", dbPath)
    302 	if err != nil {
    303 		return fmt.Errorf("failed to open database: %w", err)
    304 	}
    305 	defer db.Close()
    306 
    307 	_, err = db.Exec("VACUUM;")
    308 	if err != nil {
    309 		return fmt.Errorf("VACUUM failed: %w", err)
    310 	}
    311 
    312 	return nil
    313 }
    314 
    315 func getFilesInDB() (map[string]bool, error) {
    316 	rows, err := db.Query(`SELECT filename FROM files`)
    317 	if err != nil {
    318 		return nil, err
    319 	}
    320 	defer rows.Close()
    321 
    322 	fileMap := make(map[string]bool)
    323 	for rows.Next() {
    324 		var name string
    325 		rows.Scan(&name)
    326 		fileMap[name] = true
    327 	}
    328 	return fileMap, nil
    329 }