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