diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..91b9655 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: schollz diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..43d4a53 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +################################### +# 1. Build in a Go-based image # +################################### +FROM golang:1.12-alpine as builder +RUN apk add --no-cache git ca-certificates # add deps here (like make) if needed +WORKDIR /go/hostyoself +COPY . . +# any pre-requisities to building should be added here +RUN go generate -v +RUN go build -v + +################################### +# 2. Copy into a clean image # +################################### +FROM alpine:latest +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /go/hostyoself/hostyoself /hostyoself +VOLUME /data +CMD ["sh","-c","/hostyoself host --folder /data"] + diff --git a/README.md b/README.md index f0a7d78..0c1263e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
Version

A hosting service from the browser, because why not. Try it at hostyoself.com.

@@ -32,6 +32,18 @@ https://hostyoself.com/confidentcat/ Now if you have a file in your folder `README.md` you can access it with the public URL `https://hostyoself.com/confidentcat/README.md`, directly from your computer! +If you're on a Mac, you can install with Homebrew: + +``` +brew tap schollz/homebrew https://github.com/schollz/homebrew-tap.git +brew install hostyoself +``` + +Or you can host your current directory using Docker: + +``` +$ docker run -v `pwd`:/data schollz/hostyoself +``` ## Run your own relay @@ -56,12 +68,14 @@ Just *kidding*! You don't need any of that crap. Just goto [hostyoself.com](http **Won't I have to reload my browser if I change a file?** Yep! Welcome to the joys of Javascript. -**Whats the largest file I can host using this?** `¯\_(ツ)_/¯` +**What's the largest file I can host using this?** `¯\_(ツ)_/¯` **Should I use this to host a website?** Dear god yes. **Does this use AI or blockchain?** Sure, why not. +**Does it scale?** Horizontally, or vertically? Probably neither! + **What inspired this?** [beaker browser](https://beakerbrowser.com/), [ngrok](https://ngrok.com/), [localhost.run](http://localhost.run/), [inlets.dev](https://github.com/alexellis/inlets), Parks and Recreation. **What's the point of this?** You can host a website! You can share a file! Anything you want, directly from your browser! diff --git a/main.go b/main.go index 186fcb7..9a83410 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,7 @@ func main() { app.Version = Version app.Compiled = time.Now() app.Usage = "host your files using websockets from the command line or a browser" - app.UsageText = "use to transfer files or host a impromptu website" + app.UsageText = "use to transfer files or host an impromptu website" app.Commands = []cli.Command{ { Name: "relay", @@ -92,7 +92,15 @@ func host(c *cli.Context) (err error) { if err != nil { return } - return cl.Run() + for { + log.Info("serving forever") + err = cl.Run() + if err != nil { + log.Debug(err) + } + log.Infof("server disconnected, retrying in 10 seconds") + time.Sleep(10 * time.Second) + } } func relay(c *cli.Context) (err error) { diff --git a/pkg/client/client.go b/pkg/client/client.go index 1bab1b3..be03927 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -1,6 +1,7 @@ package client import ( + "encoding/json" "fmt" "io/ioutil" "os" @@ -10,6 +11,7 @@ import ( "sync" "github.com/schollz/hostyoself/pkg/namesgenerator" + "github.com/schollz/hostyoself/pkg/server" "github.com/schollz/hostyoself/pkg/utils" "github.com/schollz/hostyoself/pkg/wsconn" @@ -134,6 +136,31 @@ func (c *client) Run() (err error) { }) log.Infof("%s /%s 200", p.IPAddress, p.Message) } + } else if p.Type == "files" { + c.Lock() + fs := make([]server.File, len(c.fileList)) + i := 0 + for n := range c.fileList { + fs[i] = server.File{ + FullPath: n, + Upload: server.Upload{ + UUID: "", + Total: 0, + Filename: "", + }, + } + i++ + } + c.Unlock() + + b, _ := json.Marshal(fs) + log.Infof("%s sitemap", p.IPAddress) + err = ws.Send(wsconn.Payload{ + Type: "files", + Success: true, + Message: string(b), + Key: c.Key, + }) } if err != nil { log.Debug(err) @@ -195,6 +222,7 @@ func (c *client) watchFileSystem() (err error) { } else { ppath, _ = filepath.Abs(ppath) ppath = strings.TrimPrefix(filepath.ToSlash(ppath), c.Folder+"/") + log.Debugf("%s", ppath) c.Lock() c.fileList[ppath] = struct{}{} c.Unlock() diff --git a/pkg/server/server.go b/pkg/server/server.go index 9e20310..9ecae83 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -1,6 +1,7 @@ package server import ( + "encoding/json" "fmt" "html/template" "math/rand" @@ -79,7 +80,7 @@ Disallow:`)) var b []byte b, err = Asset(r.URL.Path[1:]) if err != nil { - http.Error(w, "file not found", 404) + err = fmt.Errorf("resource '%s' not found", r.URL.Path[1:]) return } var contentType string @@ -168,6 +169,7 @@ Disallow:`)) // send GET request to websockets var data string + var fs []File data, err = s.get(domain, pathToFile, ipAddress) if err != nil { // try index.html if it doesn't exist @@ -176,11 +178,46 @@ Disallow:`)) pathToFile += "/" } pathToFile += "index.html" + log.Debugf("trying 2nd try to get: %s", pathToFile) data, err = s.get(domain, pathToFile, ipAddress) } if err != nil { - log.Debug("problem getting: %s", err.Error()) - return + // try one more time + if strings.HasSuffix(pathToFile, "/index.html") { + pathToFile = strings.TrimSuffix(pathToFile, "/index.html") + log.Debugf("trying 3rd try to get: %s", pathToFile) + data, err = s.get(domain, pathToFile, ipAddress) + } + if err != nil { + if pathToFile == "index.html" { + // just serve files + fs, err = s.getFiles(domain, ipAddress) + log.Debugf("fs: %+v", fs) + if err != nil { + log.Debug(err) + return + } + + b, _ := Asset("templates/files.html") + var t *template.Template + t, err = template.New("files").Parse(string(b)) + if err != nil { + log.Error(err) + return + } + return t.Execute(w, struct { + Files []File + Domain string + }{ + Domain: domain, + Files: fs, + }) + } else { + log.Debugf("problem getting: %s", err.Error()) + err = fmt.Errorf("not found") + return + } + } } } @@ -232,7 +269,8 @@ func (s *server) handleWebsocket(w http.ResponseWriter, r *http.Request) (err er // handle websockets on this page c, errUpgrade := wsupgrader.Upgrade(w, r, nil) if errUpgrade != nil { - return errUpgrade + log.Error(errUpgrade) + return nil } ws := wsconn.New(c) @@ -250,7 +288,7 @@ func (s *server) handleWebsocket(w http.ResponseWriter, r *http.Request) (err er err = fmt.Errorf("got wrong type/domain: %s/%s", p.Type, p.Message) log.Debug(err) ws.Close() - return + return nil } domain := strings.Replace(strings.ToLower(strings.TrimSpace(p.Message)), " ", "-", -1) @@ -276,7 +314,10 @@ func (s *server) handleWebsocket(w http.ResponseWriter, r *http.Request) (err er Message: domain, Success: true, }) - return + if err != nil { + log.Error(err) + } + return nil } func (s *server) isdomain(domain string) bool { @@ -286,6 +327,69 @@ func (s *server) isdomain(domain string) bool { return ok } +type File struct { + FullPath string `json:"fullPath"` + Upload Upload `json:"upload"` +} +type Upload struct { + UUID string `json:"uuid"` + Total int `json:"total"` + Filename string `json:"filename"` +} + +func (s *server) getFiles(domain, ipAddress string) (fs []File, err error) { + var connections []*connection + s.Lock() + if _, ok := s.conn[domain]; ok { + connections = s.conn[domain] + } + s.Unlock() + if connections == nil || len(connections) == 0 { + err = fmt.Errorf("no connections available for domain %s", domain) + log.Debug(err) + return + } + log.Debugf("requesting files of %s from %d connections", domain, len(connections)) + + // any connection that initated with this key is viable + key := connections[0].Key + + // loop through connections randomly and try to get one to serve the file + for _, i := range rand.Perm(len(connections)) { + var p wsconn.Payload + p, err = func() (p wsconn.Payload, err error) { + err = connections[i].ws.Send(wsconn.Payload{ + Type: "files", + Message: "all", + IPAddress: ipAddress, + }) + if err != nil { + return + } + p, err = connections[i].ws.Receive() + return + }() + if err != nil { + log.Debug(err) + s.dumpConnection(domain, connections[i].ID) + continue + } + log.Tracef("recv: %+v", p) + if p.Type == "files" && p.Key == key { + if !p.Success { + err = fmt.Errorf(p.Message) + return + } + + err = json.Unmarshal([]byte(p.Message), &fs) + return + } + log.Debugf("no good data from %d", i) + } + err = fmt.Errorf("invalid response") + return +} + func (s *server) get(domain, filePath, ipAddress string) (payload string, err error) { var connections []*connection s.Lock() diff --git a/static/main.js b/static/main.js index 42132b1..8085ef5 100644 --- a/static/main.js +++ b/static/main.js @@ -48,13 +48,24 @@ var filesize = 0; drop.on('addedfile', function(file) { // console.log(file); - var domain = document.getElementById("inputDomain").value + var domain = document.getElementById("inputDomain").value; files.push(file); - if (files.length == 1) { - relativeDirectory = file.webkitRelativePath.split("/")[0]; - } else if (file.webkitRelativePath.split("/")[0] != relativeDirectory) { - relativeDirectory = ""; + if ("webkitRelativePath" in file) { + if (files.length == 1 && file.webkitRelativePath != "") { + relativeDirectory = file.webkitRelativePath.split("/")[0]; + } else if (file.webkitRelativePath.split("/")[0] != relativeDirectory) { + relativeDirectory = ""; + } } + if ("fullPath" in file) { + if (files.length == 1 && file.fullPath != "") { + relativeDirectory = file.fullPath.split("/")[0]; + } else if (file.fullPath.split("/")[0] != relativeDirectory) { + relativeDirectory = ""; + } + } + + if (!(isConnected)) { isConnected = true; @@ -121,12 +132,35 @@ const socketMessageListener = (event) => { } console.log(data) consoleLog(`[debug] ${data.message}`) - if (data.type == "get") { + if (data.type == "files") { + if (files.length > 0) { + socketSend({ + type: "files", + message: JSON.stringify(files), + success: true, + key: document.getElementById("inputKey").value, + }); + consoleLog( + `${data.ip} [${(new Date()).toUTCString()}] sitemap 200` + ); + } else { + socketSend({ + type: "files", + message: "none found", + success: false, + key: document.getElementById("inputKey").value, + }); + consoleLog( + `${data.ip} [${(new Date()).toUTCString()}] sitemap 404` + ); + } + } else if (data.type == "get") { var foundFile = false var iToSend = 0 for (i = 0; i < files.length; i++) { - if (files[i].webkitRelativePath == data.message || files[i].name == data.message || files[i] - .webkitRelativePath == relativeDirectory + "/" + data.message) { + if (files[i].webkitRelativePath == data.message || files[i].fullPath == data.message || files[i].name == data.message || files[i] + .webkitRelativePath == relativeDirectory + "/" + data.message || files[i] + .fullPath == relativeDirectory + "/" + data.message) { iToSend = i; var reader = new FileReader(); reader.onload = function(theFile) { @@ -164,6 +198,14 @@ const socketMessageListener = (event) => { }; const socketOpenListener = (event) => { consoleLog('[info] connected'); + if (isConnected == true) { + // reconnect if was connected and got disconnected + socketSend({ + type: "domain", + message: document.getElementById("inputDomain").value, + key: document.getElementById("inputKey").value, + }) + } }; const socketCloseListener = (event) => { diff --git a/templates/files.html b/templates/files.html new file mode 100644 index 0000000..237b285 --- /dev/null +++ b/templates/files.html @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/templates/view.html b/templates/view.html index cad45f7..f8eacf9 100644 --- a/templates/view.html +++ b/templates/view.html @@ -60,12 +60,10 @@ computer turns off then your site is down. Welcome to the joys of hosting a site on the internet.

Won't I have to reload my browser if I change a file? Yep! Welcome to the joys of Javascript.

-

Whats the largest file I can host using this? ¯\_(ツ)_/¯

+

What's the largest file I can host using this? ¯\_(ツ)_/¯

Should I use this to host a website? Dear god yes.

-

Does this use AI or blockchain? Sure, why not.

-

What inspired this? websocketd - which shows the magic of websockets and beaker browser which - shows the magic of browser hosting.

+

Does it scale? Horizontally, or vertically? Probably neither!

+

What inspired this? beaker browser, ngrok, localhost.run, inlets.dev, Parks and Recreation.

What's the point of this? You can host a website! You can share a file! Anything you want, directly from your browser!

@@ -242,4 +240,4 @@ - \ No newline at end of file +