tagliatelle

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

include-files.go (7396B)


      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 	absPath := filepath.Join(config.UploadDir, currentFile.Path)
     83 	if err = os.Remove(absPath); err != nil {
     84 		log.Printf("Warning: fileDeleteHandler: failed to delete physical file %s: %v", absPath, err)
     85 	}
     86 
     87 	// Delete thumbnail if it exists
     88 	thumbPath := filepath.Join(config.UploadDir, "thumbnails", currentFile.Filename+".jpg")
     89 	if _, err := os.Stat(thumbPath); err == nil {
     90 		if err := os.Remove(thumbPath); err != nil {
     91 			log.Printf("Warning: fileDeleteHandler: failed to delete thumbnail %s: %v", thumbPath, err)
     92 		}
     93 	}
     94 
     95 	http.Redirect(w, r, "/?deleted="+currentFile.Filename, http.StatusSeeOther)
     96 }
     97 
     98 func fileRenameHandler(w http.ResponseWriter, r *http.Request, parts []string) {
     99 	if r.Method != http.MethodPost {
    100 		http.Redirect(w, r, "/file/"+parts[2], http.StatusSeeOther)
    101 		return
    102 	}
    103 
    104 	fileID := parts[2]
    105 	newFilename := sanitizeFilename(strings.TrimSpace(r.FormValue("newfilename")))
    106 
    107 	if newFilename == "" {
    108 		renderError(w, "New filename cannot be empty", http.StatusBadRequest)
    109 		return
    110 	}
    111 
    112 	var currentFilename, currentRelPath string
    113 	err := db.QueryRow("SELECT filename, path FROM files WHERE id=?", fileID).Scan(&currentFilename, &currentRelPath)
    114 	if err != nil {
    115 		log.Printf("Error: fileRenameHandler: file not found for id=%s: %v", fileID, err)
    116 		renderError(w, "File not found", http.StatusNotFound)
    117 		return
    118 	}
    119 
    120 	if currentFilename == newFilename {
    121 		http.Redirect(w, r, "/file/"+fileID, http.StatusSeeOther)
    122 		return
    123 	}
    124 
    125 	currentAbsPath := filepath.Join(config.UploadDir, currentRelPath)
    126 	newPath := filepath.Join(config.UploadDir, newFilename)
    127 	if _, err := os.Stat(newPath); !os.IsNotExist(err) {
    128 		renderError(w, "A file with that name already exists", http.StatusConflict)
    129 		return
    130 	}
    131 
    132 	if err := os.Rename(currentAbsPath, newPath); err != nil {
    133 		log.Printf("Error: fileRenameHandler: failed to rename %s -> %s: %v", currentAbsPath, newPath, err)
    134 		renderError(w, "Failed to rename physical file: "+err.Error(), http.StatusInternalServerError)
    135 		return
    136 	}
    137 
    138 	thumbOld := filepath.Join(config.UploadDir, "thumbnails", currentFilename+".jpg")
    139 	thumbNew := filepath.Join(config.UploadDir, "thumbnails", newFilename+".jpg")
    140 
    141 	if _, err := os.Stat(thumbOld); err == nil {
    142 		if err := os.Rename(thumbOld, thumbNew); err != nil {
    143 			log.Printf("Error: fileRenameHandler: failed to rename thumbnail %s -> %s: %v", thumbOld, thumbNew, err)
    144 			if renameErr := os.Rename(newPath, currentAbsPath); renameErr != nil {
    145 				log.Printf("Error: fileRenameHandler: failed to roll back file rename %s -> %s: %v", newPath, currentAbsPath, renameErr)
    146 			}
    147 			renderError(w, "Failed to rename thumbnail: "+err.Error(), http.StatusInternalServerError)
    148 			return
    149 		}
    150 	}
    151 
    152 	newRelPath, err := filepath.Rel(config.UploadDir, newPath)
    153 	if err != nil {
    154 		log.Printf("Error: fileRenameHandler: failed to compute relative path for %s: %v", newPath, err)
    155 		newRelPath = newFilename
    156 	}
    157 
    158 	_, err = db.Exec("UPDATE files SET filename=?, path=? WHERE id=?", newFilename, newRelPath, fileID)
    159 	if err != nil {
    160 		log.Printf("Error: fileRenameHandler: failed to update database for file id=%s: %v", fileID, err)
    161 		if renameErr := os.Rename(newPath, currentAbsPath); renameErr != nil {
    162 			log.Printf("Error: fileRenameHandler: failed to roll back file rename %s -> %s: %v", newPath, currentAbsPath, renameErr)
    163 		}
    164 		if _, statErr := os.Stat(thumbNew); statErr == nil {
    165 			if renameErr := os.Rename(thumbNew, thumbOld); renameErr != nil {
    166 				log.Printf("Error: fileRenameHandler: failed to roll back thumbnail rename %s -> %s: %v", thumbNew, thumbOld, renameErr)
    167 			}
    168 		}
    169 		renderError(w, "Failed to update database", http.StatusInternalServerError)
    170 		return
    171 	}
    172 
    173 	// Recompute properties in case the extension changed
    174 	if _, err := db.Exec("DELETE FROM file_properties WHERE file_id = ?", fileID); err != nil {
    175 		log.Printf("Warning: fileRenameHandler: failed to delete old properties for file id=%s: %v", fileID, err)
    176 	}
    177 	if id, err := strconv.ParseInt(fileID, 10, 64); err == nil {
    178 		computeProperties(id, newPath)
    179 	} else {
    180 		log.Printf("Warning: fileRenameHandler: failed to parse file id %s for property recompute: %v", fileID, err)
    181 	}
    182 
    183 	http.Redirect(w, r, "/file/"+fileID, http.StatusSeeOther)
    184 }
    185 
    186 func checkFileConflictStrict(filename string) (string, string, int64, error) {
    187 	finalPath := filepath.Join(config.UploadDir, filename)
    188 	if _, err := os.Stat(finalPath); err == nil {
    189 		existingID, dbErr := getFileIDByName(filename)
    190 		if dbErr != nil {
    191 			log.Printf("Warning: checkFileConflictStrict: file %s exists on disk but not in DB: %v", filename, dbErr)
    192 			return "", "", 0, fmt.Errorf("a file with that name already exists")
    193 		}
    194 		return "", "", existingID, nil
    195 	} else if !os.IsNotExist(err) {
    196 		return "", "", 0, fmt.Errorf("failed to check for existing file: %v", err)
    197 	}
    198 	return filename, finalPath, 0, nil
    199 }
    200 
    201 func getFileIDByName(filename string) (int64, error) {
    202 	var id int64
    203 	err := db.QueryRow("SELECT id FROM files WHERE filename = ?", filename).Scan(&id)
    204 	return id, err
    205 }