code revamp, missing dir/db fault tolerance, frontend changes, misc cleanup

This commit is contained in:
delorean 2023-12-12 13:42:00 -06:00
parent 979c7e34d4
commit f15f9de04a
5 changed files with 70 additions and 46 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
files/ files/
*.db

View File

@ -4,4 +4,4 @@ vhost = "hardfiles.org"
dbfile = "dbfile.db" dbfile = "dbfile.db"
filelen = 6 filelen = 6
folder = "files" folder = "files"
fileexpiry = 86400 ttl_seconds = 86400

94
main.go
View File

@ -23,13 +23,13 @@ var (
) )
type Config struct { type Config struct {
Webroot string `toml:"webroot"` Webroot string `toml:"webroot"`
LPort string `toml:"lport"` LPort string `toml:"lport"`
VHost string `toml:"vhost"` VHost string `toml:"vhost"`
DBFile string `toml:"dbfile"` DBFile string `toml:"dbfile"`
FileLen int `toml:"filelen"` FileLen int `toml:"filelen"`
FileFolder string `toml:"folder"` FileFolder string `toml:"folder"`
FileExpirySeconds int `toml:"fileexpiry"` TTLSeconds int `toml:"ttl_seconds"`
} }
func LoadConf() { func LoadConf() {
@ -44,18 +44,15 @@ func Shred(path string) error {
return err return err
} }
size := fileinfo.Size() size := fileinfo.Size()
err = Scramble(path, size) if err = Scramble(path, size); err != nil {
if err != nil {
return err return err
} }
err = Zeros(path, size) if err = Zeros(path, size); err != nil {
if err != nil {
return err return err
} }
err = os.Remove(path) if err = os.Remove(path); err != nil {
if err != nil {
return err return err
} }
@ -100,7 +97,7 @@ func Zeros(path string, size int64) error {
} }
func NameGen() string { func NameGen() string {
const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ123456789" const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789"
ll := len(chars) ll := len(chars)
b := make([]byte, conf.FileLen) b := make([]byte, conf.FileLen)
rand.Read(b) // generates len(b) random bytes rand.Read(b) // generates len(b) random bytes
@ -110,6 +107,24 @@ func NameGen() string {
return string(b) return string(b)
} }
func CheckFolder(path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
err := os.Mkdir(path, 0755)
if err != nil {
log.Fatal().Err(err).Msg("unable to create folder")
}
}
}
func CheckDB(path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
_, err := os.Create(path)
if err != nil {
log.Fatal().Err(err).Msg("unable to create database file")
}
}
}
func CheckFile(name string) bool { // false if doesn't exist, true if exists func CheckFile(name string) bool { // false if doesn't exist, true if exists
tfd, err := os.Open(conf.FileFolder + "/" + name) tfd, err := os.Open(conf.FileFolder + "/" + name)
if err != nil { if err != nil {
@ -120,8 +135,8 @@ func CheckFile(name string) bool { // false if doesn't exist, true if exists
} }
func UploadHandler(w http.ResponseWriter, r *http.Request) { func UploadHandler(w http.ResponseWriter, r *http.Request) {
// expiry sanitize // expiry time
twentyfour := int64(conf.FileExpirySeconds) ttl := int64(conf.TTLSeconds)
file, _, err := r.FormFile("file") file, _, err := r.FormFile("file")
if err != nil { if err != nil {
@ -149,31 +164,33 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
err = db.Update(func(tx *bolt.Tx) error { err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("expiry")) 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()+ttl, 10)))
return err return err
}) })
if err != nil { if err != nil {
log.Error().Err(err).Msg("Failed to put expiry") log.Error().Err(err).Msg("failed to put expiry")
} }
log.Info().Int64("expiry", twentyfour).Msg("Writing new file")
f, err := os.OpenFile(conf.FileFolder+"/"+name, os.O_WRONLY|os.O_CREATE, 0644) f, err := os.OpenFile(conf.FileFolder+"/"+name, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Error opening a file for write") log.Error().Err(err).Msg("error opening a file for write")
w.WriteHeader(http.StatusInternalServerError) // change to json w.WriteHeader(http.StatusInternalServerError) // change to json
return return
} }
defer f.Close() defer f.Close()
io.Copy(f, file) io.Copy(f, file)
log.Info().Str("name", name).Int64("ttl", ttl).Msg("wrote new file")
w.Write([]byte("https://" + conf.VHost + "/uploads/" + name)) hostedurl := "https://" + conf.VHost + "/uploads/" + name
w.Header().Set("Location", hostedurl)
w.WriteHeader(http.StatusSeeOther)
w.Write([]byte(hostedurl))
} }
func Cull() { func Cull() {
for { for {
removed := 0
db.Update(func(tx *bolt.Tx) error { db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("expiry")) b := tx.Bucket([]byte("expiry"))
c := b.Cursor() c := b.Cursor()
@ -187,16 +204,13 @@ func Cull() {
if err := Shred(conf.FileFolder + "/" + string(k)); err != nil { if err := Shred(conf.FileFolder + "/" + string(k)); err != nil {
log.Error().Err(err).Msg("shredding failed") log.Error().Err(err).Msg("shredding failed")
} else { } else {
removed += 1 log.Info().Str("name", string(k)).Msg("shredded file")
} }
c.Delete() c.Delete()
} }
} }
return nil return nil
}) })
if removed >= 1 {
log.Info().Int("amount", removed).Msg("shredded")
}
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
} }
} }
@ -205,6 +219,9 @@ func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
LoadConf() LoadConf()
CheckFolder(conf.FileFolder)
CheckDB(conf.DBFile)
err := landlock.V2.BestEffort().RestrictPaths( err := landlock.V2.BestEffort().RestrictPaths(
landlock.RWDirs(conf.FileFolder), landlock.RWDirs(conf.FileFolder),
landlock.RWDirs(conf.Webroot), landlock.RWDirs(conf.Webroot),
@ -217,9 +234,9 @@ func main() {
_, err = os.Open("/etc/passwd") _, err = os.Open("/etc/passwd")
if err == nil { if err == nil {
log.Warn().Msg("landlock failed, could open /etc/passwd") log.Warn().Msg("landlock failed, could open /etc/passwd, are you on a 5.13+ kernel?")
} else { } else {
log.Info().Err(err).Msg("Landlocked") log.Info().Err(err).Msg("landlocked")
} }
db, err = bolt.Open(conf.DBFile, 0600, nil) db, err = bolt.Open(conf.DBFile, 0600, nil)
@ -241,21 +258,21 @@ func main() {
vars := mux.Vars(r) vars := mux.Vars(r)
if !CheckFile(vars["name"]) { if !CheckFile(vars["name"]) {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
w.Write([]byte("file not found"))
} else { } else {
http.ServeFile(w, r, conf.FileFolder+"/"+vars["name"]) http.ServeFile(w, r, conf.FileFolder+"/"+vars["name"])
} }
}).Methods("GET") }).Methods("GET")
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, conf.Webroot+"/index.html") http.ServeFile(w, r, conf.Webroot+"/index.html")
}).Methods("GET") })
r.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/{file}", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, conf.Webroot+"/index.html") file := mux.Vars(r)["file"]
}).Methods("GET") if _, err := os.Stat(conf.Webroot + "/" + file); os.IsNotExist(err) {
r.HandleFunc("/fist.ico", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusSeeOther)
http.ServeFile(w, r, conf.Webroot+"/fist.ico") } else {
}).Methods("GET") http.ServeFile(w, r, conf.Webroot+"/"+file)
r.HandleFunc("/header.png", func(w http.ResponseWriter, r *http.Request) { }
http.ServeFile(w, r, conf.Webroot+"/header.png")
}).Methods("GET") }).Methods("GET")
http.Handle("/", r) http.Handle("/", r)
@ -268,6 +285,7 @@ func main() {
IdleTimeout: 20 * time.Second, IdleTimeout: 20 * time.Second,
} }
log.Warn().Msg("shredding is only effective on HDD volumes")
log.Info().Err(err).Msg("listening on port " + conf.LPort + "...") log.Info().Err(err).Msg("listening on port " + conf.LPort + "...")
if err := serv.ListenAndServe(); err != nil { if err := serv.ListenAndServe(); err != nil {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -1,12 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html style="overflow: hidden;">
<head> <head>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.min.js"></script> <script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="fist.ico"> <link rel="icon" href="fist.ico">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Black+Ops+One&display=swap" rel="stylesheet">
<title>HARDFILES</title> <title>HARDFILES</title>
<style> <style>
body { body {
@ -16,6 +13,7 @@
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
body { body {
overflow: hidden;
background: black; background: black;
background-image: url('https://media.tenor.com/fYnd0R6F-0UAAAAC/gun-revolver.gif'); background-image: url('https://media.tenor.com/fYnd0R6F-0UAAAAC/gun-revolver.gif');
background-size: cover; background-size: cover;
@ -122,7 +120,6 @@
<body> <body>
<div class="container"> <div class="container">
<img src="header.png" width="50%" alt="supernets" style="margin-top: 10rem;"> <img src="header.png" width="50%" alt="supernets" style="margin-top: 10rem;">
<!-- <h1 class="hf" style=" color: #660000; font-weight: 800; font-family: 'Black Ops One', cursive; margin-bottom: 0 !important; margin-top: 5rem !important;">HARDFILES</h1> -->
<h2 style="font-size: 1em; color: #ffffff; font-weight: 200;">curl -F file=@example.png https://hardfiles.org/</h1> <h2 style="font-size: 1em; color: #ffffff; font-weight: 200;">curl -F file=@example.png https://hardfiles.org/</h1>
<form method="POST" class="subform" enctype="multipart/form-data"> <form method="POST" class="subform" enctype="multipart/form-data">
<div class="file-upload" style="display: flex; justify-content: center; width: 25rem;"> <div class="file-upload" style="display: flex; justify-content: center; width: 25rem;">
@ -135,6 +132,14 @@
</div> </div>
</form> </form>
<p style="color: white; font-weight: 300;">⚠️ Uploads are erased after 24 hours</p> <p style="color: white; font-weight: 300;">⚠️ Uploads are erased after 24 hours</p>
</div>
<div style="position: relative;">
<a href="https://supernets.org/" target="_blank" style="position: absolute; bottom: 10px; right:10px; display: flex; align-items: center; text-decoration: none;">
<p style="font-weight:200; color:#ffffff">A SUPERNETS</p>
<img src="https://git.supernets.org/assets/img/logo.png" width="30px" style="margin: 0 5px;">
<p style="font-weight:200; color: #ffffff">SERVICE</p>
</a>
</div> </div>
</body> </body>
</html> </html>