include-files.go (4967B)
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(¤tFile.ID, ¤tFile.Filename, ¤tFile.Path) 44 if err != nil { 45 renderError(w, "File not found", http.StatusNotFound) 46 return 47 } 48 49 tx, err := db.Begin() 50 if err != nil { 51 renderError(w, "Failed to start transaction", http.StatusInternalServerError) 52 return 53 } 54 defer tx.Rollback() 55 56 if _, err = tx.Exec("DELETE FROM file_tags WHERE file_id=?", fileID); err != nil { 57 renderError(w, "Failed to delete file tags", http.StatusInternalServerError) 58 return 59 } 60 61 if _, err = tx.Exec("DELETE FROM files WHERE id=?", fileID); err != nil { 62 renderError(w, "Failed to delete file record", http.StatusInternalServerError) 63 return 64 } 65 66 if err = tx.Commit(); err != nil { 67 renderError(w, "Failed to commit transaction", http.StatusInternalServerError) 68 return 69 } 70 71 if err = os.Remove(currentFile.Path); err != nil { 72 log.Printf("Warning: Failed to delete physical file %s: %v", currentFile.Path, err) 73 } 74 75 // Delete thumbnail if it exists 76 thumbPath := filepath.Join(config.UploadDir, "thumbnails", currentFile.Filename+".jpg") 77 if _, err := os.Stat(thumbPath); err == nil { 78 if err := os.Remove(thumbPath); err != nil { 79 log.Printf("Warning: Failed to delete thumbnail %s: %v", thumbPath, err) 80 } 81 } 82 83 http.Redirect(w, r, "/?deleted="+currentFile.Filename, http.StatusSeeOther) 84 } 85 86 func fileRenameHandler(w http.ResponseWriter, r *http.Request, parts []string) { 87 if r.Method != http.MethodPost { 88 http.Redirect(w, r, "/file/"+parts[2], http.StatusSeeOther) 89 return 90 } 91 92 fileID := parts[2] 93 newFilename := sanitizeFilename(strings.TrimSpace(r.FormValue("newfilename"))) 94 95 if newFilename == "" { 96 renderError(w, "New filename cannot be empty", http.StatusBadRequest) 97 return 98 } 99 100 var currentFilename, currentPath string 101 err := db.QueryRow("SELECT filename, path FROM files WHERE id=?", fileID).Scan(¤tFilename, ¤tPath) 102 if err != nil { 103 renderError(w, "File not found", http.StatusNotFound) 104 return 105 } 106 107 if currentFilename == newFilename { 108 http.Redirect(w, r, "/file/"+fileID, http.StatusSeeOther) 109 return 110 } 111 112 newPath := filepath.Join(config.UploadDir, newFilename) 113 if _, err := os.Stat(newPath); !os.IsNotExist(err) { 114 renderError(w, "A file with that name already exists", http.StatusConflict) 115 return 116 } 117 118 if err := os.Rename(currentPath, newPath); err != nil { 119 renderError(w, "Failed to rename physical file: "+err.Error(), http.StatusInternalServerError) 120 return 121 } 122 123 thumbOld := filepath.Join(config.UploadDir, "thumbnails", currentFilename+".jpg") 124 thumbNew := filepath.Join(config.UploadDir, "thumbnails", newFilename+".jpg") 125 126 if _, err := os.Stat(thumbOld); err == nil { 127 if err := os.Rename(thumbOld, thumbNew); err != nil { 128 os.Rename(newPath, currentPath) 129 renderError(w, "Failed to rename thumbnail: "+err.Error(), http.StatusInternalServerError) 130 return 131 } 132 } 133 134 _, err = db.Exec("UPDATE files SET filename=?, path=? WHERE id=?", newFilename, newPath, fileID) 135 if err != nil { 136 os.Rename(newPath, currentPath) 137 if _, err := os.Stat(thumbNew); err == nil { 138 os.Rename(thumbNew, thumbOld) 139 } 140 renderError(w, "Failed to update database", http.StatusInternalServerError) 141 return 142 } 143 144 // Recompute properties in case the extension changed 145 db.Exec("DELETE FROM file_properties WHERE file_id = ?", fileID) 146 if id, err := strconv.ParseInt(fileID, 10, 64); err == nil { 147 computeProperties(id, newPath) 148 } 149 150 http.Redirect(w, r, "/file/"+fileID, http.StatusSeeOther) 151 } 152 153 func checkFileConflictStrict(filename string) (string, string, int64, error) { 154 finalPath := filepath.Join(config.UploadDir, filename) 155 if _, err := os.Stat(finalPath); err == nil { 156 existingID, dbErr := getFileIDByName(filename) 157 if dbErr != nil { 158 return "", "", 0, fmt.Errorf("a file with that name already exists") 159 } 160 return "", "", existingID, nil 161 } else if !os.IsNotExist(err) { 162 return "", "", 0, fmt.Errorf("failed to check for existing file: %v", err) 163 } 164 return filename, finalPath, 0, nil 165 } 166 167 func getFileIDByName(filename string) (int64, error) { 168 var id int64 169 err := db.QueryRow("SELECT id FROM files WHERE filename = ?", filename).Scan(&id) 170 return id, err 171 }