mirror of
https://github.com/schollz/hostyoself.git
synced 2026-01-23 10:25:26 +00:00
Compare commits
21 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04ad1997a6 | ||
|
|
caaed23f57 | ||
|
|
bdd0536998 | ||
|
|
47ef115491 | ||
|
|
e98f05e6dc | ||
|
|
ab215af53e | ||
|
|
3a62f092f1 | ||
|
|
d34a5bca3f | ||
|
|
e96dfb99e7 | ||
|
|
b458bac63f | ||
|
|
d74ce01023 | ||
|
|
2804887a56 | ||
|
|
ef28306e6d | ||
|
|
d709d471be | ||
|
|
479838af2a | ||
|
|
78f06e76ca | ||
|
|
3520f36f72 | ||
|
|
2670c964d1 | ||
|
|
bd01535efa | ||
|
|
8ff76da85e | ||
|
|
66d39b9503 |
9 changed files with 251 additions and 18 deletions
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: schollz
|
||||
20
Dockerfile
Normal file
20
Dockerfile
Normal file
|
|
@ -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"]
|
||||
|
||||
16
README.md
16
README.md
|
|
@ -4,7 +4,7 @@
|
|||
<br>
|
||||
<a
|
||||
href="https://github.com/schollz/hostyoself/releases/latest"><img
|
||||
src="https://img.shields.io/badge/version-0.0.4-brightgreen.svg?style=flat-square"
|
||||
src="https://img.shields.io/badge/version-0.0.6-brightgreen.svg?style=flat-square"
|
||||
alt="Version"></a> </p>
|
||||
|
||||
<p align="center">A hosting service from the browser, because why not. Try it at <a href="https://hostyoself.com">hostyoself.com</a>.</p>
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -62,6 +74,8 @@ Just *kidding*! You don't need any of that crap. Just goto [hostyoself.com](http
|
|||
|
||||
**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!
|
||||
|
|
|
|||
12
main.go
12
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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
err = fmt.Errorf("resource '%s' not found",r.URL.Path[1:])
|
||||
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.Debugf("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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -290,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()
|
||||
|
|
|
|||
58
static/main.js
vendored
58
static/main.js
vendored
|
|
@ -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) => {
|
||||
|
|
|
|||
20
templates/files.html
vendored
Normal file
20
templates/files.html
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ul>
|
||||
{{range .Files}}
|
||||
<li>
|
||||
{{ if .FullPath }}
|
||||
<a href="/{{$.Domain}}/{{.FullPath}}">{{.FullPath}}</a>
|
||||
{{else}}
|
||||
<a href="/{{$.Domain}}/{{.Upload.Filename}}">{{.Upload.Filename}}</a>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
6
templates/view.html
vendored
6
templates/view.html
vendored
|
|
@ -62,10 +62,8 @@
|
|||
Javascript.</p>
|
||||
<p><strong>What's the largest file I can host using this?</strong> <code>¯\_(ツ)_/¯</code></p>
|
||||
<p><strong>Should I use this to host a website?</strong> Dear god yes.</p>
|
||||
<p><strong>Does this use AI or blockchain?</strong> Sure, why not. </p>
|
||||
<p><strong>What inspired this?</strong> <a href="https://github.com/joewalnes/websocketd">websocketd</a>
|
||||
which shows the magic of websockets and <a href="https://beakerbrowser.com/">beaker browser</a> which
|
||||
shows the magic of browser hosting.</p>
|
||||
<p><strong>Does it scale?</strong> Horizontally, or vertically? Probably neither!</p>
|
||||
<p><strong>What inspired this?</strong> <a href="https://beakerbrowser.com/">beaker browser</a>, <a href="https://ngrok.com/">ngrok</a>, <a href="http://localhost.run/">localhost.run</a>, <a href="https://github.com/alexellis/inlets">inlets.dev</a>, Parks and Recreation.</p>
|
||||
<p><strong>What's the point of this?</strong> You can host a website! You can share a file! Anything you
|
||||
want, directly from your browser!</p>
|
||||
<p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue