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