forked from electricdusk/rushlink
		
	Implement file deletion
This commit is contained in:
		
							parent
							
								
									c82a71f7d0
								
							
						
					
					
						commit
						df86d5836a
					
				
							
								
								
									
										3
									
								
								assets/templates/html/deletePasteSuccess.html.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/templates/html/deletePasteSuccess.html.tmpl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
{{define "title"}}
 | 
			
		||||
Success - rushlink
 | 
			
		||||
{{end}}
 | 
			
		||||
							
								
								
									
										1
									
								
								assets/templates/txt/deletePasteSuccess.txt.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/templates/txt/deletePasteSuccess.txt.tmpl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<{{.Request.Host}}/{{.Paste.Key}}> was succesfully deleted
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
https://{{.Request.Host}}/{{.Paste.Key}}
 | 
			
		||||
{{if .Request.PostForm.deleteToken -}}
 | 
			
		||||
https://{{.Request.Host}}/{{.Paste.Key}}?deleteToken={{.Paste.DeleteToken | urlquery}}
 | 
			
		||||
{{else -}}
 | 
			
		||||
https://{{.Request.Host}}/{{.Paste.Key}}
 | 
			
		||||
{{end -}}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
https://{{.Request.Host}}/{{.Paste.Key}}
 | 
			
		||||
{{if .Request.PostForm.deleteToken -}}
 | 
			
		||||
https://{{.Request.Host}}/{{.Paste.Key}}?deleteToken={{.Paste.DeleteToken | urlquery}}
 | 
			
		||||
{{else -}}
 | 
			
		||||
https://{{.Request.Host}}/{{.Paste.Key}}
 | 
			
		||||
{{end -}}
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ func (t fileUploadState) String() string {
 | 
			
		||||
 | 
			
		||||
func OpenFileStore(path string) error {
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		return errors.New("file store path not set")
 | 
			
		||||
		return errors.New("file-store not set")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try to create the file store directory if it does not yet exist
 | 
			
		||||
@ -158,11 +158,23 @@ func (fu *fileUpload) save(tx *bolt.Tx) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fu *fileUpload) delete(tx *bolt.Tx) error {
 | 
			
		||||
	// Replace the old paste with a new empty paste
 | 
			
		||||
	return (&fileUpload{
 | 
			
		||||
	// Remove the file in the backend
 | 
			
		||||
	filePath := fileStorePath(fu.ID, fu.FileName)
 | 
			
		||||
	if err := os.Remove(filePath); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update the file in the server
 | 
			
		||||
	if err := (&fileUpload{
 | 
			
		||||
		ID:    fu.ID,
 | 
			
		||||
		State: fileUploadStateDeleted,
 | 
			
		||||
	}).save(tx)
 | 
			
		||||
	}).save(tx); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Cleanup the parent directory
 | 
			
		||||
	wrap := "deletion succeeded, but removing the file directory has failed"
 | 
			
		||||
	return errors.Wrap(os.Remove(path.Dir(filePath)), wrap)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fu *fileUpload) url() *url.URL {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										70
									
								
								handlers.go
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								handlers.go
									
									
									
									
									
								
							@ -173,47 +173,34 @@ func viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPas
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newPasteHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	newPasteHandlerMultipart(w, r, func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		newPasteHandlerURLEncoded(w, r, func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			renderError(w, r, http.StatusBadRequest, "no form data in request\n")
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Try to parse the new-paste request as multi-part form.
 | 
			
		||||
//
 | 
			
		||||
// If there is no multi-part form in the request, this handler will call next.
 | 
			
		||||
func newPasteHandlerMultipart(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
 | 
			
		||||
	reader, err := r.MultipartReader()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		next(w, r)
 | 
			
		||||
	file, fileHeader, err := r.FormFile("file")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		newFileUploadPasteHandler(w, r, file, *fileHeader)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	part, err := reader.NextPart()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			renderError(w, r, http.StatusBadRequest, "multipart form is empty\n")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		panic(errors.Wrap(err, "multipart.Reader.NextPart"))
 | 
			
		||||
	}
 | 
			
		||||
	if part.FormName() != "file" {
 | 
			
		||||
		msg := fmt.Sprintf("invalid multipart form name: %v", part.FormName())
 | 
			
		||||
	} else if err == http.ErrMissingFile {
 | 
			
		||||
		// Fallthrough
 | 
			
		||||
	} else {
 | 
			
		||||
		msg := fmt.Sprintf("could not parse form: %v\n", err)
 | 
			
		||||
		renderError(w, r, http.StatusBadRequest, msg)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newFileUploadPasteHandler(w, r, *part)
 | 
			
		||||
	shorten := r.FormValue("shorten")
 | 
			
		||||
	if shorten != "" {
 | 
			
		||||
		newRedirectPasteHandler(w, r, shorten)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	renderError(w, r, http.StatusBadRequest, "no 'file' and no 'shorten' fields given in form\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFileUploadPasteHandler(w http.ResponseWriter, r *http.Request, part multipart.Part) {
 | 
			
		||||
func newFileUploadPasteHandler(w http.ResponseWriter, r *http.Request, file multipart.File, header multipart.FileHeader) {
 | 
			
		||||
	var fu *fileUpload
 | 
			
		||||
	var paste *paste
 | 
			
		||||
	if err := DB.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		var err error
 | 
			
		||||
		// Create the fileUpload in the database
 | 
			
		||||
		fu, err = newFileUpload(tx, &part, part.FileName(), part.Header.Get("Content-Type"))
 | 
			
		||||
		fu, err = newFileUpload(tx, file, header.Filename, header.Header.Get("Content-Type"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(errors.Wrap(err, "creating fileUpload"))
 | 
			
		||||
		}
 | 
			
		||||
@ -240,8 +227,7 @@ func newPasteHandlerURLEncoded(w http.ResponseWriter, r *http.Request, next http
 | 
			
		||||
	newRedirectPasteHandler(w, r, shorten)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request, shorten string) {
 | 
			
		||||
	rawurl := r.PostForm.Get("shorten")
 | 
			
		||||
func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request, rawurl string) {
 | 
			
		||||
	userURL, err := url.ParseRequestURI(rawurl)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		msg := fmt.Sprintf("invalid url (%v): %v", err, rawurl)
 | 
			
		||||
@ -259,7 +245,6 @@ func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request, shorten str
 | 
			
		||||
 | 
			
		||||
	var paste *paste
 | 
			
		||||
	if err := DB.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		// Generate a new delete token for this paste
 | 
			
		||||
		var err error
 | 
			
		||||
		paste, err = shortenURL(tx, userURL)
 | 
			
		||||
		return err
 | 
			
		||||
@ -282,22 +267,35 @@ func deletePasteHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var errorCode int
 | 
			
		||||
	var paste paste
 | 
			
		||||
	if err := DB.Update(func(tx *bolt.Tx) error {
 | 
			
		||||
		p, err := getPaste(tx, key)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			errorCode = http.StatusNotFound
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(p.DeleteToken)) == 1 {
 | 
			
		||||
			p.delete(tx)
 | 
			
		||||
		if p.State == pasteStateDeleted {
 | 
			
		||||
			errorCode = http.StatusGone
 | 
			
		||||
			return errors.New("already deleted")
 | 
			
		||||
		}
 | 
			
		||||
		errorCode = http.StatusForbidden
 | 
			
		||||
		return errors.New("invalid delete token")
 | 
			
		||||
		if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(p.DeleteToken)) == 0 {
 | 
			
		||||
			errorCode = http.StatusForbidden
 | 
			
		||||
			return errors.New("invalid delete token")
 | 
			
		||||
		}
 | 
			
		||||
		if err := p.delete(tx); err != nil {
 | 
			
		||||
			errorCode = http.StatusInternalServerError
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		paste = *p
 | 
			
		||||
		return nil
 | 
			
		||||
	}); err != nil {
 | 
			
		||||
		log.Printf("error: %v\n", err)
 | 
			
		||||
		renderError(w, r, errorCode, fmt.Sprintf("error: %v\n", err))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := map[string]interface{}{"Paste": paste}
 | 
			
		||||
	render(w, r, "deletePasteSuccess", data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add a new fileUpload redirect to the database
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								paste.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								paste.go
									
									
									
									
									
								
							@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	bolt "go.etcd.io/bbolt"
 | 
			
		||||
)
 | 
			
		||||
@ -44,6 +45,8 @@ func (t pasteType) String() string {
 | 
			
		||||
		return "paste"
 | 
			
		||||
	case pasteTypeRedirect:
 | 
			
		||||
		return "redirect"
 | 
			
		||||
	case pasteTypeFileUpload:
 | 
			
		||||
		return "file"
 | 
			
		||||
	default:
 | 
			
		||||
		return "invalid"
 | 
			
		||||
	}
 | 
			
		||||
@ -94,12 +97,29 @@ func (p *paste) save(tx *bolt.Tx) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *paste) delete(tx *bolt.Tx) error {
 | 
			
		||||
	// Remove the (maybe) attached file
 | 
			
		||||
	if p.Type == pasteTypeFileUpload {
 | 
			
		||||
		fuID, err := uuid.FromBytes(p.Content)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "failed to parse uuid")
 | 
			
		||||
		}
 | 
			
		||||
		fu, err := getFileUpload(tx, fuID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "failed to find file in database")
 | 
			
		||||
		}
 | 
			
		||||
		if err := fu.delete(tx); err != nil {
 | 
			
		||||
			return errors.Wrap(err, "failed to remove file")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Replace the old paste with a new empty paste
 | 
			
		||||
	return (&paste{
 | 
			
		||||
		Key:         p.Key,
 | 
			
		||||
		State:       pasteStateDeleted,
 | 
			
		||||
		DeleteToken: p.DeleteToken,
 | 
			
		||||
	}).save(tx)
 | 
			
		||||
	p.Type = pasteTypeUndef
 | 
			
		||||
	p.State = pasteStateDeleted
 | 
			
		||||
	p.Content = []byte{}
 | 
			
		||||
	if err := p.save(tx); err != nil {
 | 
			
		||||
		return errors.Wrap(err, "failed to delete paste in database")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the URL from this paste.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user