diff --git a/.gitignore b/.gitignore index 7d2cb50..1e3a9af 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -files/ \ No newline at end of file +files/ +dbfile.db +hardfile diff --git a/README.md b/README.md index fd59fbf..bfd9624 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Start by adjusting the necessary configuration variables in `config.toml`. Execute the following commands to build and initiate HardFiles: ``` go build -o hardfiles main.go -mkdir files ./hardfiles ``` @@ -46,7 +45,8 @@ docker compose up -d ``` #### 3. Default Port: -By default, HardFiles listens on port `5000`. For production environments, it's recommended to use a robust web server like Nginx or Apache to proxy traffic to this port. + +By default, HardFiles listens on port `5000`. For production environments, it's recommended to use a robust web server like Nginx or Caddy to proxy traffic to this port. Just don't use Apache ever. ###### Using Nginx as a Reverse Proxy: @@ -62,9 +62,11 @@ server { location / { proxy_pass http://localhost:5000; proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; } + access_log /dev/null; + error_log /dev/null; + listen 443 ssl; ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem; @@ -77,21 +79,38 @@ For obtaining the Let's Encrypt certificates, you can use tools like `certbot` t Remember, by using a reverse proxy, you can run HardFiles without needing root privileges and maintain a more secure environment. +###### Using Caddy as a Reverse Proxy: + +```caddy +your_domain.com { + reverse_proxy localhost:5000 + + tls /etc/letsencrypt/live/your_domain.com/fullchain.pem /etc/letsencrypt/live/your_domain.com/privkey.pem + + log { + output discard + } +} +``` + ## cURL Uploads & Bash Alias If you frequently upload files to HardFiles via the command line, you can streamline the process by setting up a bash alias. This allows you to use a simple command, like `upload`, to push your files to HardFiles using `curl`. #### Setting Up: + 1. **Edit your `.bashrc` file:** Open your `~/.bashrc` file in a text editor. You can use `nano` or `vim` for this purpose: ```shell nano ~/.bashrc ``` + 2. **Add the `upload` function:** At the end of the `.bashrc` file, append the following function (replace the domain if you are running your own instance): ```shell upload() { curl -F file=@$1 https://hardfiles.org/ } ``` + 3. Reload your .bashrc file: To make the new function available in your current session, reload your .bashrc: ```shell source ~/.bashrc diff --git a/main.go b/main.go index dee78fd..65a9b63 100644 --- a/main.go +++ b/main.go @@ -99,12 +99,12 @@ func Zeros(path string, size int64) error { return nil } -func NameGen() string { - const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789" +func NameGen(fileNameLength int) string { + const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ1234567890-_" ll := len(chars) - b := make([]byte, conf.FileLen) + b := make([]byte, fileNameLength) rand.Read(b) // generates len(b) random bytes - for i := int64(0); i < int64(conf.FileLen); i++ { + for i := int64(0); i < int64(fileNameLength); i++ { b[i] = chars[int(b[i])%ll] } return string(b) @@ -120,8 +120,9 @@ func CheckFile(name string) bool { // false if doesn't exist, true if exists } func UploadHandler(w http.ResponseWriter, r *http.Request) { - // expiry sanitize - twentyfour := int64(conf.FileExpirySeconds) + var name string + var expiryTime int64 + var fileNameLength int file, _, err := r.FormFile("file") if err != nil { @@ -137,10 +138,41 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) { } file.Seek(0, 0) + // Check if expiry time is present and length is too long + if r.PostFormValue("expiry") != "" { + expiryTime, err = strconv.ParseInt(r.PostFormValue("expiry"), 10, 64) + if err != nil { + log.Error().Err(err).Msg("expiry could not be parsed") + } else { + // 5 days max + if expiryTime < 1 || expiryTime > 432000 { + w.WriteHeader(http.StatusBadRequest) + return + } + } + } else { + expiryTime = int64(conf.FileExpirySeconds) + } + + // Check if the file length parameter exists and also if it's too long + if r.PostFormValue("url_len") != "" { + fileNameLength, err = strconv.Atoi(r.PostFormValue("url_len")) + if err != nil { + log.Error().Err(err).Msg("url_len could not be parsed") + } else { + // if the length is < 3 and > 128 return error + if fileNameLength < 3 || fileNameLength > 128 { + w.WriteHeader(http.StatusBadRequest) + return + } + } + } else { + fileNameLength = conf.FileLen + } + // generate + check name - var name string for { - id := NameGen() + id := NameGen(fileNameLength) name = id + mtype.Extension() if !CheckFile(name) { break @@ -149,14 +181,14 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) { err = db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("expiry")) - err := b.Put([]byte(name), []byte(strconv.FormatInt(time.Now().Unix()+twentyfour, 10))) + err := b.Put([]byte(name), []byte(strconv.FormatInt(time.Now().Unix()+expiryTime, 10))) return err }) if err != nil { log.Error().Err(err).Msg("Failed to put expiry") } - log.Info().Int64("expiry", twentyfour).Msg("Writing new file") + log.Info().Int64("expiry", expiryTime).Msg("Writing new file") f, err := os.OpenFile(conf.FileFolder+"/"+name, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { @@ -235,6 +267,11 @@ func main() { return nil }) + // Create the files directory if it doesn't already exist + if _, err := os.Stat(conf.FileFolder); os.IsNotExist(err) { + os.Mkdir(conf.FileFolder, 0755) + } + r := mux.NewRouter() r.HandleFunc("/", UploadHandler).Methods("POST") r.HandleFunc("/uploads/{name}", func(w http.ResponseWriter, r *http.Request) {