tagliatelle

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

include-files.go (6001B)


      1 package main
      2 
      3 import (
      4     "fmt"
      5     "log"
      6     "net/http"
      7     "os"
      8     "path/filepath"
      9     "strconv"
     10     "strings"
     11 )
     12 
     13 func fileRouter(w http.ResponseWriter, r *http.Request) {
     14 	parts := strings.Split(r.URL.Path, "/")
     15 
     16 	if len(parts) >= 4 && parts[3] == "delete" {
     17 		fileDeleteHandler(w, r, parts)
     18 		return
     19 	}
     20 
     21 	if len(parts) >= 4 && parts[3] == "rename" {
     22 		fileRenameHandler(w, r, parts)
     23 		return
     24 	}
     25 
     26 	if len(parts) >= 5 && parts[3] == "tag" && parts[4] == "delete" {
     27 		tagActionHandler(w, r, parts)
     28 		return
     29 	}
     30 
     31 	fileHandler(w, r)
     32 }
     33 
     34 func fileDeleteHandler(w http.ResponseWriter, r *http.Request, parts []string) {
     35 	if r.Method != http.MethodPost {
     36 		http.Redirect(w, r, "/file/"+parts[2], http.StatusSeeOther)
     37 		return
     38 	}
     39 
     40 	fileID := parts[2]
     41 
     42 	var currentFile File
     43 	err := db.QueryRow("SELECT id, filename, path FROM files WHERE id=?", fileID).Scan(&currentFile.ID, &currentFile.Filename, &currentFile.Path)
     44 	if err != nil {
     45 		log.Printf("Error: fileDeleteHandler: file not found for id=%s: %v", fileID, err)
     46 		renderError(w, "File not found", http.StatusNotFound)
     47 		return
     48 	}
     49 
     50 	tx, err := db.Begin()
     51 	if err != nil {
     52 		log.Printf("Error: fileDeleteHandler: failed to start transaction for file id=%s: %v", fileID, err)
     53 		renderError(w, "Failed to start transaction", http.StatusInternalServerError)
     54 		return
     55 	}
     56 	defer tx.Rollback()
     57 
     58 	if _, err = tx.Exec("DELETE FROM file_tags WHERE file_id=?", fileID); err != nil {
     59 		log.Printf("Error: fileDeleteHandler: failed to delete file_tags for file id=%s: %v", fileID, err)
     60 		renderError(w, "Failed to delete file tags", http.StatusInternalServerError)
     61 		return
     62 	}
     63 
     64 	if _, err = tx.Exec("DELETE FROM file_properties WHERE file_id=?", fileID); err != nil {
     65 		log.Printf("Error: fileDeleteHandler: failed to delete file_properties for file id=%s: %v", fileID, err)
     66 		renderError(w, "Failed to delete file properties", http.StatusInternalServerError)
     67 		return
     68 	}
     69 
     70 	if _, err = tx.Exec("DELETE FROM files WHERE id=?", fileID); err != nil {
     71 		log.Printf("Error: fileDeleteHandler: failed to delete files record for file id=%s: %v", fileID, err)
     72 		renderError(w, "Failed to delete file record", http.StatusInternalServerError)
     73 		return
     74 	}
     75 
     76 	if err = tx.Commit(); err != nil {
     77 		log.Printf("Error: fileDeleteHandler: failed to commit transaction for file id=%s: %v", fileID, err)
     78 		renderError(w, "Failed to commit transaction", http.StatusInternalServerError)
     79 		return
     80 	}
     81 
     82 	if err = os.Remove(filepath.Join(config.UploadDir, currentFile.Path)); err != nil {
     83 		log.Printf("Warning: Failed to delete physical file %s: %v", currentFile.Path, err)
     84 	}
     85 
     86 	// Delete thumbnail if it exists
     87 	thumbPath := filepath.Join(config.UploadDir, "thumbnails", currentFile.Filename+".jpg")
     88 	if _, err := os.Stat(thumbPath); err == nil {
     89 		if err := os.Remove(thumbPath); err != nil {
     90 			log.Printf("Warning: fileDeleteHandler: failed to delete thumbnail %s: %v", thumbPath, err)
     91 		}
     92 	}
     93 
     94 	http.Redirect(w, r, "/?deleted="+currentFile.Filename, http.StatusSeeOther)
     95 }
     96 
     97 func fileRenameHandler(w http.ResponseWriter, r *http.Request, parts []string) {
     98 	if r.Method != http.MethodPost {
     99 		http.Redirect(w, r, "/file/"+parts[2], http.StatusSeeOther)
    100 		return
    101 	}
    102 
    103 	fileID := parts[2]
    104 	newFilename := sanitizeFilename(strings.TrimSpace(r.FormValue("newfilename")))
    105 
    106 	if newFilename == "" {
    107 		renderError(w, "New filename cannot be empty", http.StatusBadRequest)
    108 		return
    109 	}
    110 
    111 	var currentFilename, currentPath string
    112 	err := db.QueryRow("SELECT filename, path FROM files WHERE id=?", fileID).Scan(&currentFilename, &currentPath)
    113 	if err != nil {
    114 		log.Printf("Error: fileRenameHandler: file not found for id=%s: %v", fileID, err)
    115 		renderError(w, "File not found", http.StatusNotFound)
    116 		return
    117 	}
    118 
    119 	if currentFilename == newFilename {
    120 		http.Redirect(w, r, "/file/"+fileID, http.StatusSeeOther)
    121 		return
    122 	}
    123 
    124 	newPath := filepath.Join(config.UploadDir, newFilename)
    125 	if _, err := os.Stat(newPath); !os.IsNotExist(err) {
    126 		renderError(w, "A file with that name already exists", http.StatusConflict)
    127 		return
    128 	}
    129 
    130 	if err := os.Rename(currentPath, newPath); err != nil {
    131 		renderError(w, "Failed to rename physical file: "+err.Error(), http.StatusInternalServerError)
    132 		return
    133 	}
    134 
    135 	thumbOld := filepath.Join(config.UploadDir, "thumbnails", currentFilename+".jpg")
    136 	thumbNew := filepath.Join(config.UploadDir, "thumbnails", newFilename+".jpg")
    137 
    138 	if _, err := os.Stat(thumbOld); err == nil {
    139 		if err := os.Rename(thumbOld, thumbNew); err != nil {
    140 			os.Rename(newPath, currentPath)
    141 			renderError(w, "Failed to rename thumbnail: "+err.Error(), http.StatusInternalServerError)
    142 			return
    143 		}
    144 	}
    145 
    146 	_, err = db.Exec("UPDATE files SET filename=?, path=? WHERE id=?", newFilename, newPath, fileID)
    147 	if err != nil {
    148 		os.Rename(newPath, currentPath)
    149 		if _, err := os.Stat(thumbNew); err == nil {
    150 			os.Rename(thumbNew, thumbOld)
    151 		}
    152 		renderError(w, "Failed to update database", http.StatusInternalServerError)
    153 		return
    154 	}
    155 
    156 	// Recompute properties in case the extension changed
    157 	db.Exec("DELETE FROM file_properties WHERE file_id = ?", fileID)
    158 	if id, err := strconv.ParseInt(fileID, 10, 64); err == nil {
    159 		computeProperties(id, newPath)
    160 	}
    161 
    162 	http.Redirect(w, r, "/file/"+fileID, http.StatusSeeOther)
    163 }
    164 
    165 func checkFileConflictStrict(filename string) (string, string, int64, error) {
    166 	finalPath := filepath.Join(config.UploadDir, filename)
    167 	if _, err := os.Stat(finalPath); err == nil {
    168 		existingID, dbErr := getFileIDByName(filename)
    169 		if dbErr != nil {
    170 			log.Printf("Warning: checkFileConflictStrict: file %s exists on disk but not in DB: %v", filename, dbErr)
    171 			return "", "", 0, fmt.Errorf("a file with that name already exists")
    172 		}
    173 		return "", "", existingID, nil
    174 	} else if !os.IsNotExist(err) {
    175 		return "", "", 0, fmt.Errorf("failed to check for existing file: %v", err)
    176 	}
    177 	return filename, finalPath, 0, nil
    178 }
    179 
    180 func getFileIDByName(filename string) (int64, error) {
    181 	var id int64
    182 	err := db.QueryRow("SELECT id FROM files WHERE filename = ?", filename).Scan(&id)
    183 	return id, err
    184 }