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(¤tFile.ID, ¤tFile.Filename, ¤tFile.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(¤tFilename, ¤tPath) 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 }