andreimarcu
9 years ago
commit
2dbe318b18
10 changed files with 317 additions and 0 deletions
-
24.gitignore
-
24README.md
-
54display.go
-
17pages.go
-
47server.go
-
13templates/base.html
-
6templates/display/file.html
-
6templates/display/image.html
-
8templates/index.html
-
118upload.go
@ -0,0 +1,24 @@ |
|||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects) |
||||
|
*.o |
||||
|
*.a |
||||
|
*.so |
||||
|
|
||||
|
# Folders |
||||
|
_obj |
||||
|
_test |
||||
|
|
||||
|
# Architecture specific extensions/prefixes |
||||
|
*.[568vq] |
||||
|
[568vq].out |
||||
|
|
||||
|
*.cgo1.go |
||||
|
*.cgo2.c |
||||
|
_cgo_defun.c |
||||
|
_cgo_gotypes.go |
||||
|
_cgo_export.* |
||||
|
|
||||
|
_testmain.go |
||||
|
|
||||
|
*.exe |
||||
|
*.test |
||||
|
*.prof |
@ -0,0 +1,24 @@ |
|||||
|
Linx Server |
||||
|
====== |
||||
|
Soon-to-be opensource replacement of Linx |
||||
|
|
||||
|
**Please do not use yet! Consider it in pre-alpha development stages.** |
||||
|
|
||||
|
Build & Run |
||||
|
---------------- |
||||
|
|
||||
|
1. ```go get -u github.com/andreimarcu/linx-server ``` |
||||
|
2. ```cd $GOPATH/src/github.com/andreimarcu/linx-server ``` |
||||
|
3. ```go build && ./linx-server``` |
||||
|
|
||||
|
By default linx will bind to ```http://127.0.0.1:8080/```, use the "files/" files directory and the "linx" sitename. |
||||
|
Configurable flags can be found in ```server.go```. |
||||
|
|
||||
|
|
||||
|
TODO |
||||
|
-------- |
||||
|
Please refer to the [main TODO issue](https://github.com/andreimarcu/linx-server/issues/1) |
||||
|
|
||||
|
Author |
||||
|
------- |
||||
|
Andrei Marcu, http://andreim.net/ |
@ -0,0 +1,54 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"net/http" |
||||
|
"os" |
||||
|
"path" |
||||
|
"strings" |
||||
|
|
||||
|
"github.com/flosch/pongo2" |
||||
|
"github.com/rakyll/magicmime" |
||||
|
"github.com/zenazn/goji/web" |
||||
|
) |
||||
|
|
||||
|
func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) { |
||||
|
filename := c.URLParams["name"] |
||||
|
absPath := path.Join(Config.filesDir, filename) |
||||
|
fileInfo, err := os.Stat(absPath) |
||||
|
|
||||
|
if os.IsNotExist(err) { |
||||
|
http.Error(w, http.StatusText(404), 404) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if err := magicmime.Open(magicmime.MAGIC_MIME_TYPE | |
||||
|
magicmime.MAGIC_SYMLINK | |
||||
|
magicmime.MAGIC_ERROR); err != nil { |
||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||
|
} |
||||
|
defer magicmime.Close() |
||||
|
|
||||
|
mimetype, err := magicmime.TypeByFile(absPath) |
||||
|
if err != nil { |
||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||
|
} |
||||
|
|
||||
|
var tpl *pongo2.Template |
||||
|
|
||||
|
if strings.HasPrefix(mimetype, "image/") { |
||||
|
tpl = pongo2.Must(pongo2.FromCache("templates/display/image.html")) |
||||
|
} else { |
||||
|
tpl = pongo2.Must(pongo2.FromCache("templates/display/file.html")) |
||||
|
} |
||||
|
|
||||
|
err = tpl.ExecuteWriter(pongo2.Context{ |
||||
|
"mime": mimetype, |
||||
|
"sitename": Config.siteName, |
||||
|
"filename": filename, |
||||
|
"size": fileInfo.Size(), |
||||
|
}, w) |
||||
|
|
||||
|
if err != nil { |
||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||
|
} |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"github.com/flosch/pongo2" |
||||
|
"net/http" |
||||
|
|
||||
|
"github.com/zenazn/goji/web" |
||||
|
) |
||||
|
|
||||
|
func indexHandler(c web.C, w http.ResponseWriter, r *http.Request) { |
||||
|
indexTpl := pongo2.Must(pongo2.FromCache("templates/index.html")) |
||||
|
|
||||
|
err := indexTpl.ExecuteWriter(pongo2.Context{"sitename": Config.siteName}, w) |
||||
|
if err != nil { |
||||
|
http.Error(w, err.Error(), http.StatusInternalServerError) |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"flag" |
||||
|
"fmt" |
||||
|
"log" |
||||
|
"net" |
||||
|
"net/http" |
||||
|
"regexp" |
||||
|
|
||||
|
"github.com/zenazn/goji" |
||||
|
// "github.com/zenazn/goji/web"
|
||||
|
) |
||||
|
|
||||
|
var Config struct { |
||||
|
bind string |
||||
|
filesDir string |
||||
|
siteName string |
||||
|
} |
||||
|
|
||||
|
func main() { |
||||
|
flag.StringVar(&Config.bind, "b", "127.0.0.1:8080", |
||||
|
"host to bind to (default: 127.0.0.1:8080)") |
||||
|
flag.StringVar(&Config.filesDir, "d", "files/", |
||||
|
"path to files directory (default: files/)") |
||||
|
flag.StringVar(&Config.siteName, "n", "linx", |
||||
|
"name of the site") |
||||
|
flag.Parse() |
||||
|
|
||||
|
fmt.Printf("About to listen on http://%s\n", Config.bind) |
||||
|
|
||||
|
nameRe := regexp.MustCompile(`^/(?P<name>[a-z0-9-\.]+)$`) |
||||
|
selifRe := regexp.MustCompile(`^/selif/(?P<name>[a-z0-9-\.]+)$`) |
||||
|
|
||||
|
goji.Get("/", indexHandler) |
||||
|
goji.Post("/upload", uploadPostHandler) |
||||
|
goji.Put("/upload", uploadPutHandler) |
||||
|
goji.Get(nameRe, fileDisplayHandler) |
||||
|
goji.Handle(selifRe, http.StripPrefix("/selif/", http.FileServer(http.Dir(Config.filesDir)))) |
||||
|
|
||||
|
listener, err := net.Listen("tcp", Config.bind) |
||||
|
if err != nil { |
||||
|
log.Fatal("Could not bind: ", err) |
||||
|
} |
||||
|
|
||||
|
goji.ServeListener(listener) |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<html> |
||||
|
<head> |
||||
|
<title>{% block title %}{{ sitename }}{% endblock %}</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
|
||||
|
<div> |
||||
|
{% block content %}{% endblock %} |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,6 @@ |
|||||
|
{% extends "../base.html" %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<p>Viewing file {{ filename }} of mime: {{ mime }}</p> |
||||
|
<p><a href="/selif/{{ filename }}">Click to download</a></p> |
||||
|
{% endblock %} |
@ -0,0 +1,6 @@ |
|||||
|
{% extends "../base.html" %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<p>Viewing file {{ filename }}</p> |
||||
|
<img src="/selif/{{ filename }}" /> |
||||
|
{% endblock %} |
@ -0,0 +1,8 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<form enctype="multipart/form-data" action="/upload" method="post"> |
||||
|
<input type="file" name="file" /> |
||||
|
<input type="submit" value="upload" /> |
||||
|
</form> |
||||
|
{% endblock %} |
@ -0,0 +1,118 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"net/http" |
||||
|
"os" |
||||
|
"path" |
||||
|
"regexp" |
||||
|
"strings" |
||||
|
|
||||
|
"code.google.com/p/go-uuid/uuid" |
||||
|
"github.com/zenazn/goji/web" |
||||
|
) |
||||
|
|
||||
|
type UploadRequest struct { |
||||
|
src io.Reader |
||||
|
filename string |
||||
|
expiry int |
||||
|
randomBarename bool |
||||
|
} |
||||
|
|
||||
|
type Upload struct { |
||||
|
Filename string |
||||
|
Size int64 |
||||
|
Expiry int |
||||
|
} |
||||
|
|
||||
|
func uploadPostHandler(c web.C, w http.ResponseWriter, r *http.Request) { |
||||
|
upReq := UploadRequest{} |
||||
|
|
||||
|
file, headers, err := r.FormFile("file") |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(w, err.Error()) |
||||
|
return |
||||
|
} |
||||
|
defer file.Close() |
||||
|
|
||||
|
upReq.src = file |
||||
|
upReq.filename = headers.Filename |
||||
|
|
||||
|
upload, err := processUpload(upReq) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(w, "Failed to upload: %v", err) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
fmt.Fprintf(w, "File %s uploaded successfully.", upload.Filename) |
||||
|
} |
||||
|
|
||||
|
func uploadPutHandler(c web.C, w http.ResponseWriter, r *http.Request) { |
||||
|
upReq := UploadRequest{} |
||||
|
|
||||
|
defer r.Body.Close() |
||||
|
upReq.src = r.Body |
||||
|
|
||||
|
upload, err := processUpload(upReq) |
||||
|
if err != nil { |
||||
|
fmt.Fprintf(w, "Failed to upload") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
fmt.Fprintf(w, "File %s uploaded successfully.", upload.Filename) |
||||
|
} |
||||
|
|
||||
|
func processUpload(upReq UploadRequest) (upload Upload, err error) { |
||||
|
barename, extension := barePlusExt(upReq.filename) |
||||
|
|
||||
|
if upReq.randomBarename || len(barename) == 0 { |
||||
|
barename = generateBarename() |
||||
|
} |
||||
|
|
||||
|
if len(extension) == 0 { |
||||
|
extension = "ext" |
||||
|
} |
||||
|
|
||||
|
upload.Filename = strings.Join([]string{barename, extension}, ".") |
||||
|
|
||||
|
dst, ferr := os.Create(path.Join("files/", upload.Filename)) |
||||
|
defer dst.Close() |
||||
|
if ferr != nil { |
||||
|
err = ferr |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
bytes, cerr := io.Copy(dst, upReq.src) |
||||
|
if cerr != nil { |
||||
|
err = cerr |
||||
|
return |
||||
|
} else if bytes == 0 { |
||||
|
err = errors.New("Empty file") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
upload.Size = bytes |
||||
|
|
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func generateBarename() string { |
||||
|
return uuid.New()[:8] |
||||
|
} |
||||
|
|
||||
|
func barePlusExt(filename string) (barename, extension string) { |
||||
|
re := regexp.MustCompile(`[^A-Za-z0-9\-]`) |
||||
|
|
||||
|
filename = strings.TrimSpace(filename) |
||||
|
filename = strings.ToLower(filename) |
||||
|
|
||||
|
extension = path.Ext(filename) |
||||
|
barename = filename[:len(filename)-len(extension)] |
||||
|
|
||||
|
extension = re.ReplaceAllString(extension, "") |
||||
|
barename = re.ReplaceAllString(barename, "") |
||||
|
|
||||
|
return |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue