Skip to content

Commit

Permalink
Merge pull request #1 from pldubouilh/mv
Browse files Browse the repository at this point in the history
Mv
  • Loading branch information
pldubouilh authored Nov 16, 2018
2 parents aaf1eb8 + cafc46f commit 13cf1db
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 216 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
debug
test
gossa.go
gossa
gossa-linux
gossa-linux-arm
gossa-linux-arm64
gossa-mac
gossa-windows.exe

.vscode
fixture/*
fixture/*/*
!fixture/compress
Expand Down
44 changes: 22 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@ build:
go build gossa.go
rm gossa.go

embed:
echo "embedding css and js into binary"
cp src/main.go gossa.go
perl -pe 's/css_will_be_here/`cat src\/style.css`/ge' -i gossa.go
perl -pe 's/js_will_be_here/`cat src\/script.js`/ge' -i gossa.go
perl -pe 's/favicon_will_be_here/`base64 -w0 src\/favicon.png`/ge' -i gossa.go

run:
make build
./gossa fixture

ci:
cd src && go vet && go fmt
timeout 5 make run &
cd src && sleep 1 && go test

ci-watch:
ls src/* | entr -rc make ci

watch:
ls src/* | entr -rc make run

build-all:
make embed
env GOOS=linux GOARCH=amd64 go build gossa.go
Expand All @@ -25,25 +47,3 @@ clean:
-rm gossa-linux-arm64
-rm gossa-mac
-rm gossa-windows.exe

embed:
echo "embedding css and js into binary"
cp main.go gossa.go
perl -pe 's/some_css/`cat style.css`/ge' -i gossa.go
perl -pe 's/some_js/`cat script.js`/ge' -i gossa.go
go build gossa.go

ci:
go fmt
go vet
timeout 5 go run main.go fixture &
sleep 1 && go test

ci-watch:
ls main.go script.js main_test.go | entr -rc make ci

debug-watch:
ls main.go script.js | entr -rc go run main.go fixture

run:
go run main.go fixture
23 changes: 11 additions & 12 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,30 @@ gossa
🎢 A fast and simple webserver for your files. It's dependency-free and with under 250 lines for the server code, easily code-reviewable.

### features
* upload files and folders with drag-and-drop
* browse throughout files/directories
* browse through files/directories
* upload with drag-and-drop
* create new folders
* browse throughout pictures with a full-screen carousel
* move files with drag-and-drop and keyboard
* browse through pictures with a full-screen carousel
* simple keyboard navigation/shortcuts
* fast ; fills my 80MB/s AC wifi link

### run
```sh
# run
go run main.go fixture

# build embedding the js/css in the binary
# build
make
./gossa --help

# run CI tests
make ci
# run
./gossa -h 192.168.100.33 ~/storage
```

### keyboard shortcuts
* Arrows/Enter browse throughout the files/directories and pictures
* Ctrl/Meta + C copy selected path to clipboard
* Ctrl/Meta + C copy URL to clipboard
* Ctrl/Meta + D create a new directory
* \<any letter\> search on first letters in filename
* Ctrl/Meta + X cut selected path
* Ctrl/Meta + V paste previously selected paths to directory
* \<any letter\> search

### built blobs
built blobs are available on the [release page](https://github.com/pldubouilh/gossa/releases).
Binary file added src/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 40 additions & 56 deletions main.go β†’ src/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,33 @@ import (
"strings"
)

func check(e error) {
if e != nil {
panic(e)
}
}

var fs http.Handler

var host = flag.String("h", "127.0.0.1", "host to listen to")
var port = flag.String("p", "8001", "port to listen to")
var verb = flag.Bool("verb", true, "verbosity")
var skipHidden = flag.Bool("k", true, "skip hidden files")

var initPath = ""
var css = `some_css`
var jsTag = `some_js`
var css = `css_will_be_here` // js will be embedded here
var js = `js_will_be_here` // id. css
var favicon = "data:image/png;base64,favicon_will_be_here" // id. b64 favicon
var units = [8]string{"k", "M", "G", "T", "P", "E", "Z", "Y"}

var fs http.Handler

type rpcCall struct {
Call string `json:"call"`
Args []string `json:"args"`
}

func check(e error) {
if e != nil {
panic(e)
}
}

func logVerb(s ...interface{}) {
if *verb {
log.Println(s)
log.Println(s...)
}
}

Expand All @@ -55,11 +56,9 @@ func sizeToString(bytes float64) string {
bytes = bytes / 1024
u++
if bytes < 1024 {
break
return strconv.FormatFloat(bytes, 'f', 1, 64) + units[u]
}
}

return strconv.FormatFloat(bytes, 'f', 1, 64) + units[u]
}

func row(name string, href string, size float64, ext string) string {
Expand All @@ -71,7 +70,7 @@ func row(name string, href string, size float64, ext string) string {
<td><i class="btn icon icon-` + strings.ToLower(ext) + ` icon-blank"></i></td>
<td class="file-size"><code>` + sizeToString(size) + `</code></td>
<td class="arrow"><i class="arrow-icon"></i></td>
<td class="display-name"><a href="` + url.PathEscape(href) + `">` + name + `</a></td>
<td class="display-name"><a class="list-links" onclick="return onClickLink(event)" href="` + url.PathEscape(href) + `">` + name + `</a></td>
</tr>`
}

Expand All @@ -84,17 +83,17 @@ func replyList(w http.ResponseWriter, path string) {
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>` + html.EscapeString(path) + `</title>
<script>window.onload = function(){` + jsTag + `}</script>
<title>` + html.EscapeString(path) + `</title>
<link href="` + favicon + `" rel="icon" type="image/png"/>
<script>window.onload = function(){` + js + `}</script>
<style type="text/css">` + css + `</style>
</head>
<body>
<div onclick="window.mkdir()" id="newFolder"></div>
<div onclick="window.picsToggle()" id="picsToggle"></div>
<div id="pics" style="display:none;"> <div onclick="window.picsToggle()" id="picsToggleCinema"></div> <img onclick="window.picsNav()" id="picsHolder"/> <span id="picsLabel"></span> </div>
<body>
<div id="drop-grid"> Drop here to upload </div>
<div id="progressBars"></div>
<h1>.` + html.EscapeString(path) + `</h1>
<div class="icHolder"><div style="display:none;" class="ic icon-large-images" onclick="window.picsToggle()"></div>
<div class="ic icon-large-folder" onclick="window.mkdirBtn()"></div></div>
<div id="pics" style="display:none;"> <div onclick="window.picsToggle()" id="picsToggleCinema"></div> <img onclick="window.picsNav()" id="picsHolder"/> <span id="picsLabel"></span> </div>
<table>`

_files, err := ioutil.ReadDir(initPath + path)
Expand Down Expand Up @@ -122,21 +121,14 @@ func replyList(w http.ResponseWriter, path string) {
}
}

var resp = head + dirs + files + `</table>
<br><address><a href="https://github.com/pldubouilh/gossa">Gossa 🎢</a></address>
</body></html>`

w.Write([]byte(resp))
w.Write([]byte(head + dirs + files + `</table>
<br><address><a href="https://github.com/pldubouilh/gossa">Gossa 🎢</a></address>
<div id="progress" style="display:none;"><span id="dlBarName"></span><div id="dlBarPc">1%</div></div>
</body></html>`))
}

func doContent(w http.ResponseWriter, r *http.Request) {
path := html.UnescapeString(r.URL.Path)

if strings.Contains(path, "/favicon.ico") {
w.Write([]byte(" "))
return
}

fullPath, errPath := checkPath(path)
stat, errStat := os.Stat(fullPath)

Expand Down Expand Up @@ -174,22 +166,28 @@ func upload(w http.ResponseWriter, r *http.Request) {
}

func rpc(w http.ResponseWriter, r *http.Request) {
var err error
bodyBytes, _ := ioutil.ReadAll(r.Body)
bodyString := string(bodyBytes)
var payload rpcCall
json.Unmarshal([]byte(bodyString), &payload)

unparsed, _ := url.PathUnescape(payload.Args[0])
p, err := checkPath(unparsed)
logVerb("RPC", err, unparsed)
for i := range payload.Args {
payload.Args[i], err = checkPath(payload.Args[i])
if err != nil {
logVerb("Cant read path", err, payload)
w.Write([]byte("error"))
return
}
}

if err != nil {
w.Write([]byte("error"))
return
} else if payload.Call == "mkdirp" {
os.MkdirAll(p, os.ModePerm)
if payload.Call == "mkdirp" {
err = os.MkdirAll(payload.Args[0], os.ModePerm)
} else if payload.Call == "mv" {
err = os.Rename(payload.Args[0], payload.Args[1])
}

logVerb("RPC", err, payload)
w.Write([]byte("ok"))
}

Expand All @@ -198,7 +196,7 @@ func checkPath(p string) (string, error) {
fp, err := filepath.Abs(p)

if err != nil || !strings.HasPrefix(fp, initPath) {
return fp, errors.New("error")
return "", errors.New("error")
}

return fp, nil
Expand All @@ -217,20 +215,6 @@ func main() {
initPath, err = filepath.Abs(initPath)
check(err)

// Read CSS file if not embedded
if len(css) < 10 {
c, err := ioutil.ReadFile("./style.css")
check(err)
css = string(c)
}

// Read JS file if not embedded
if len(jsTag) < 10 {
j, err := ioutil.ReadFile("./script.js")
check(err)
jsTag = string(j)
}

var hostString = *host + ":" + *port
fmt.Println("Gossa startig on directory " + initPath)
fmt.Println("Listening on http://" + hostString)
Expand Down
30 changes: 21 additions & 9 deletions main_test.go β†’ src/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,19 @@ func testDefaults(t *testing.T, url string) string {
t.Fatal("error header")
}

if !strings.Contains(bodyStr, `<a href="hols">hols/</a>`) {
if !strings.Contains(bodyStr, `href="hols">hols/</a>`) {
t.Fatal("error hols folder")
}

if !strings.Contains(bodyStr, `<a href="[email protected]%20%2840%25%29">[email protected] (40%)/</a>`) {
if !strings.Contains(bodyStr, `href="[email protected]%20%2840%25%29">[email protected] (40%)/</a>`) {
t.Fatal("error [email protected] (40%) folder")
}

if !strings.Contains(bodyStr, `<a href="%E4%B8%AD%E6%96%87">δΈ­ζ–‡/</a>`) {
if !strings.Contains(bodyStr, `href="%E4%B8%AD%E6%96%87">δΈ­ζ–‡/</a>`) {
t.Fatal("error δΈ­ζ–‡ folder")
}

if !strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-types icon-blank"></i></td> <td class="file-size"><code>0.2k</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a href="custom_mime_type.types">custom_mime_type.types</a></td> </tr>`) {
if !strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-types icon-blank"></i></td> <td class="file-size"><code>0.2k</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a class="list-links" onclick="return onClickLink(event)" href="custom_mime_type.types">custom_mime_type.types</a></td> </tr>`) {
t.Fatal("error row custom_mime_type")
}

Expand Down Expand Up @@ -110,28 +110,40 @@ func TestGetFolder(t *testing.T) {

// ~~~~~~~~~~~~~~~~~
fmt.Println("\r\n~~~~~~~~~~ test mkdir rpc")
bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mkdirp","args":["%2FAAA"]}`)
bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mkdirp","args":["/AAA"]}`)
if !strings.Contains(bodyStr, `ok`) {
t.Fatal("error returned value")
}

bodyStr = testDefaults(t, "http://127.0.0.1:8001/")
if !strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-folder icon-blank"></i></td> <td class="file-size"><code>0</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a href="AAA">AAA/</a></td> </tr>`) {
if !strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-folder icon-blank"></i></td> <td class="file-size"><code>0</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a class="list-links" onclick="return onClickLink(event)" href="AAA">AAA/</a></td> </tr>`) {
t.Fatal("error new folder created")
}

// ~~~~~~~~~~~~~~~~~
fmt.Println("\r\n~~~~~~~~~~ test invalid mkdir rpc")
bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mkdirp","args":["..%2FBBB"]}`)
bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mkdirp","args":["../BBB"]}`)
if !strings.Contains(bodyStr, `error`) {
t.Fatal("error not returned")
}

bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mkdirp","args":["%2F..%2FBBB"]}`)
bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mkdirp","args":["/../BBB"]}`)
if !strings.Contains(bodyStr, `error`) {
t.Fatal("error not returned")
}

// ~~~~~~~~~~~~~~~~~
fmt.Println("\r\n~~~~~~~~~~ test mv rpc")
bodyStr = postJSON(t, "http://127.0.0.1:8001/rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`)
if !strings.Contains(bodyStr, `ok`) {
t.Fatal("error returned value")
}

bodyStr = testDefaults(t, "http://127.0.0.1:8001/")
if strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-folder icon-blank"></i></td> <td class="file-size"><code>0</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a class="list-links" onclick="return onClickLink(event)" href="AAA">AAA/</a></td> </tr>`) {
t.Fatal("error folder moved")
}

// ~~~~~~~~~~~~~~~~~
fmt.Println("\r\n~~~~~~~~~~ test post file")
bodyStr = postDummyFile(t, "http://127.0.0.1:8001/post", "%2F%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1")
Expand All @@ -145,7 +157,7 @@ func TestGetFolder(t *testing.T) {
}

bodyStr = testDefaults(t, "http://127.0.0.1:8001/")
if !strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-α„’α…‘ α„’α…‘ icon-blank"></i></td> <td class="file-size"><code>0.0k</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a href="%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1">α„’α…‘ α„’α…‘</a></td> </tr>`) {
if !strings.Contains(bodyStr, `<tr> <td><i class="btn icon icon-α„’α…‘ α„’α…‘ icon-blank"></i></td> <td class="file-size"><code>0.0k</code></td> <td class="arrow"><i class="arrow-icon"></i></td> <td class="display-name"><a class="list-links" onclick="return onClickLink(event)" href="%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1">α„’α…‘ α„’α…‘</a></td> </tr>`) {
t.Fatal("error checking new file row")
}

Expand Down
Loading

0 comments on commit 13cf1db

Please sign in to comment.