Browse Source

Merge branch 'master' into s3_backend

pull/156/head
mutantmonkey 6 years ago
parent
commit
0cdd8b5836
  1. 17
      Dockerfile
  2. 1
      README.md
  3. 1
      csp_test.go
  4. 28
      display.go
  5. 12
      server.go
  6. 4
      server_test.go
  7. 10
      static/css/dropzone.css
  8. 3
      static/css/github-markdown.css
  9. 208
      static/css/linx.css
  10. 6
      static/js/bin.js
  11. 12
      static/js/upload.js
  12. 1
      templates.go
  13. 4
      templates/404.html
  14. 2
      templates/API.html
  15. 1
      templates/base.html
  16. 4
      templates/display/audio.html
  17. 7
      templates/display/base.html
  18. 20
      templates/display/bin.html
  19. 2
      templates/display/file.html
  20. 4
      templates/display/image.html
  21. 4
      templates/display/pdf.html
  22. 20
      templates/display/story.html
  23. 4
      templates/display/video.html
  24. 6
      templates/index.html
  25. 18
      templates/paste.html
  26. 1
      upload.go

17
Dockerfile

@ -1,15 +1,26 @@
FROM golang:alpine
FROM golang:alpine3.8 AS build
COPY . /go/src/github.com/andreimarcu/linx-server
WORKDIR /go/src/github.com/andreimarcu/linx-server
RUN set -ex \ RUN set -ex \
&& apk add --no-cache --virtual .build-deps git \ && apk add --no-cache --virtual .build-deps git \
&& go get github.com/andreimarcu/linx-server \
&& go get -v . \
&& apk del .build-deps && apk del .build-deps
FROM alpine:3.8
COPY --from=build /go/bin/linx-server /usr/local/bin/linx-server
ENV GOPATH /go
COPY static /go/src/github.com/andreimarcu/linx-server/static/
COPY templates /go/src/github.com/andreimarcu/linx-server/templates/
RUN mkdir -p /data/files && mkdir -p /data/meta && chown -R 65534:65534 /data RUN mkdir -p /data/files && mkdir -p /data/meta && chown -R 65534:65534 /data
VOLUME ["/data/files", "/data/meta"] VOLUME ["/data/files", "/data/meta"]
EXPOSE 8080 EXPOSE 8080
USER nobody USER nobody
ENTRYPOINT ["/go/bin/linx-server", "-bind=0.0.0.0:8080", "-filespath=/data/files/", "-metapath=/data/meta/"]
ENTRYPOINT ["/usr/local/bin/linx-server", "-bind=0.0.0.0:8080", "-filespath=/data/files/", "-metapath=/data/meta/"]
CMD ["-sitename=linx", "-allowhotlink"] CMD ["-sitename=linx", "-allowhotlink"]

1
README.md

@ -42,6 +42,7 @@ allowhotlink = true
- ```-bind 127.0.0.1:8080``` -- what to bind to (default is 127.0.0.1:8080) - ```-bind 127.0.0.1:8080``` -- what to bind to (default is 127.0.0.1:8080)
- ```-sitename myLinx``` -- the site name displayed on top (default is inferred from Host header) - ```-sitename myLinx``` -- the site name displayed on top (default is inferred from Host header)
- ```-siteurl "http://mylinx.example.org/"``` -- the site url (default is inferred from execution context) - ```-siteurl "http://mylinx.example.org/"``` -- the site url (default is inferred from execution context)
- ```-selifpath "selif"``` -- path relative to site base url (the "selif" in https://mylinx.example.org/selif/image.jpg) where files are accessed directly (default: selif)
- ```-filespath files/``` -- Path to store uploads (default is files/) - ```-filespath files/``` -- Path to store uploads (default is files/)
- ```-metapath meta/``` -- Path to store information about uploads (default is meta/) - ```-metapath meta/``` -- Path to store information about uploads (default is meta/)
- ```-maxsize 4294967296``` -- maximum upload file size in bytes (default 4GB) - ```-maxsize 4294967296``` -- maximum upload file size in bytes (default 4GB)

1
csp_test.go

@ -23,6 +23,7 @@ func TestContentSecurityPolicy(t *testing.T) {
Config.maxSize = 1024 * 1024 * 1024 Config.maxSize = 1024 * 1024 * 1024
Config.noLogs = true Config.noLogs = true
Config.siteName = "linx" Config.siteName = "linx"
Config.selifPath = "selif"
Config.contentSecurityPolicy = testCSPHeaders["Content-Security-Policy"] Config.contentSecurityPolicy = testCSPHeaders["Content-Security-Policy"]
Config.referrerPolicy = testCSPHeaders["Referrer-Policy"] Config.referrerPolicy = testCSPHeaders["Referrer-Policy"]
Config.xFrameOptions = testCSPHeaders["X-Frame-Options"] Config.xFrameOptions = testCSPHeaders["X-Frame-Options"]

28
display.go

@ -24,7 +24,7 @@ const maxDisplayFileSizeBytes = 1024 * 512
var cliUserAgentRe = regexp.MustCompile("(?i)(lib)?curl|wget") var cliUserAgentRe = regexp.MustCompile("(?i)(lib)?curl|wget")
func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) { func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) {
if !Config.noDirectAgents && cliUserAgentRe.MatchString(r.Header.Get("User-Agent")) {
if !Config.noDirectAgents && cliUserAgentRe.MatchString(r.Header.Get("User-Agent")) && !strings.EqualFold("application/json", r.Header.Get("Accept")) {
fileServeHandler(c, w, r) fileServeHandler(c, w, r)
return return
} }
@ -54,11 +54,12 @@ func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) {
if strings.EqualFold("application/json", r.Header.Get("Accept")) { if strings.EqualFold("application/json", r.Header.Get("Accept")) {
js, _ := json.Marshal(map[string]string{ js, _ := json.Marshal(map[string]string{
"filename": fileName,
"expiry": strconv.FormatInt(metadata.Expiry.Unix(), 10),
"size": strconv.FormatInt(metadata.Size, 10),
"mimetype": metadata.Mimetype,
"sha256sum": metadata.Sha256sum,
"filename": fileName,
"direct_url": getSiteURL(r) + Config.selifPath + fileName,
"expiry": strconv.FormatInt(metadata.Expiry.Unix(), 10),
"size": strconv.FormatInt(metadata.Size, 10),
"mimetype": metadata.Mimetype,
"sha256sum": metadata.Sha256sum,
}) })
w.Write(js) w.Write(js)
return return
@ -133,13 +134,14 @@ func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) {
} }
err = renderTemplate(tpl, pongo2.Context{ err = renderTemplate(tpl, pongo2.Context{
"mime": metadata.Mimetype,
"filename": fileName,
"size": sizeHuman,
"expiry": expiryHuman,
"extra": extra,
"lines": lines,
"files": metadata.ArchiveFiles,
"mime": metadata.Mimetype,
"filename": fileName,
"size": sizeHuman,
"expiry": expiryHuman,
"expirylist": listExpirationTimes(),
"extra": extra,
"lines": lines,
"files": metadata.ArchiveFiles,
}, r, w) }, r, w)
if err != nil { if err != nil {

12
server.go

@ -42,6 +42,7 @@ var Config struct {
siteName string siteName string
siteURL string siteURL string
sitePath string sitePath string
selifPath string
certFile string certFile string
keyFile string keyFile string
contentSecurityPolicy string contentSecurityPolicy string
@ -131,6 +132,11 @@ func setup() *web.Mux {
Config.sitePath = "/" Config.sitePath = "/"
} }
Config.selifPath = strings.TrimLeft(Config.selifPath, "/")
if lastChar := Config.selifPath[len(Config.selifPath)-1:]; lastChar != "/" {
Config.selifPath = Config.selifPath + "/"
}
if Config.s3Bucket != "" { if Config.s3Bucket != "" {
storageBackend = s3.NewS3Backend(Config.s3Bucket, Config.s3Region, Config.s3Endpoint) storageBackend = s3.NewS3Backend(Config.s3Bucket, Config.s3Region, Config.s3Endpoint)
} else { } else {
@ -154,8 +160,8 @@ func setup() *web.Mux {
// Routing setup // Routing setup
nameRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)$`) nameRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)$`)
selifRe := regexp.MustCompile("^" + Config.sitePath + `selif/(?P<name>[a-z0-9-\.]+)$`)
selifIndexRe := regexp.MustCompile("^" + Config.sitePath + `selif/$`)
selifRe := regexp.MustCompile("^" + Config.sitePath + Config.selifPath + `(?P<name>[a-z0-9-\.]+)$`)
selifIndexRe := regexp.MustCompile("^" + Config.sitePath + Config.selifPath + `$`)
torrentRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)/torrent$`) torrentRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)/torrent$`)
if Config.authFile == "" { if Config.authFile == "" {
@ -215,6 +221,8 @@ func main() {
"name of the site") "name of the site")
flag.StringVar(&Config.siteURL, "siteurl", "", flag.StringVar(&Config.siteURL, "siteurl", "",
"site base url (including trailing slash)") "site base url (including trailing slash)")
flag.StringVar(&Config.selifPath, "selifpath", "selif",
"path relative to site base url where files are accessed directly")
flag.Int64Var(&Config.maxSize, "maxsize", 4*1024*1024*1024, flag.Int64Var(&Config.maxSize, "maxsize", 4*1024*1024*1024,
"maximum upload file size in bytes (default 4GB)") "maximum upload file size in bytes (default 4GB)")
flag.Uint64Var(&Config.maxExpiry, "maxexpiry", 0, flag.Uint64Var(&Config.maxExpiry, "maxexpiry", 0,

4
server_test.go

@ -173,7 +173,7 @@ func TestFileNotFound(t *testing.T) {
filename := generateBarename() filename := generateBarename()
req, err := http.NewRequest("GET", "/selif/"+filename, nil)
req, err := http.NewRequest("GET", "/"+Config.selifPath+filename, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1001,7 +1001,7 @@ func TestPutAndOverwrite(t *testing.T) {
// Make sure it's the new file // Make sure it's the new file
w = httptest.NewRecorder() w = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/selif/"+myjson.Filename, nil)
req, err = http.NewRequest("GET", "/"+Config.selifPath+myjson.Filename, nil)
mux.ServeHTTP(w, req) mux.ServeHTTP(w, req)
if w.Code == 404 { if w.Code == 404 {

10
static/css/dropzone.css

@ -31,17 +31,25 @@
border: 2px solid #FAFBFC; border: 2px solid #FAFBFC;
} }
#dropzone { width: 400px;
#dropzone {
width: 400px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
@media(max-width: 450px) {
#dropzone {
width: auto;
}
}
#uploads { #uploads {
margin-top: 20px; margin-top: 20px;
} }
div.dz-default { div.dz-default {
border: 2px dashed #C9C9C9; border: 2px dashed #C9C9C9;
border-radius: 5px;
color: #C9C9C9; color: #C9C9C9;
font: 14px "helvetica neue",helvetica,arial,sans-serif; font: 14px "helvetica neue",helvetica,arial,sans-serif;
background-color: #FAFBFC; background-color: #FAFBFC;

3
static/css/github-markdown.css

@ -8,7 +8,8 @@
font-size: 12px; font-size: 12px;
line-height: 1.6; line-height: 1.6;
word-wrap: break-word; word-wrap: break-word;
width: 680px;
width: 80vw;
max-width: 680px;
padding: 10px; padding: 10px;
} }

208
static/css/linx.css

@ -1,56 +1,56 @@
body { body {
background-color: #E8ECF0;
color: #556A7F;
background-color: #E8ECF0;
color: #556A7F;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
} }
#container_container { #container_container {
display: table;
table-layout: fixed;
margin-left: auto;
margin-right: auto;
display: table;
table-layout: fixed;
margin-left: auto;
margin-right: auto;
} }
#container { #container {
display: table-cell;
min-width: 200px;
display: table-cell;
min-width: 200px;
} }
#header a { #header a {
text-decoration: none;
color: #556A7F;
text-decoration: none;
color: #556A7F;
} }
#navigation { #navigation {
margin-top: 4px;
margin-top: 4px;
} }
#navigation a { #navigation a {
text-decoration: none;
border-bottom: 1px dotted #556A7F;
color: #556A7F;
text-decoration: none;
border-bottom: 1px dotted #556A7F;
color: #556A7F;
} }
#navigation a:hover { #navigation a:hover {
background-color: #C7D1EB;
background-color: #C7D1EB;
} }
#main { #main {
background-color: white;
background-color: white;
padding: 6px 5px 8px 5px;
padding: 6px 5px 8px 5px;
-moz-box-shadow: 1px 1px 1px 1px #ccc;
-webkit-box-shadow: 1px 1px 1px 1px #ccc;
box-shadow: 1px 1px 1px 1px #ccc;
-moz-box-shadow: 1px 1px 1px 1px #ccc;
-webkit-box-shadow: 1px 1px 1px 1px #ccc;
box-shadow: 1px 1px 1px 1px #ccc;
text-align: center;
text-align: center;
} }
#main a { #main a {
color: #556A7F;
color: #556A7F;
} }
#normal-content { #normal-content {
@ -62,28 +62,19 @@ body {
margin-bottom: 0; margin-bottom: 0;
} }
.ninfo {
margin-bottom: 5px;
}
.dinfo { .dinfo {
-moz-box-shadow: 1px 1px 1px 1px #ccc; -moz-box-shadow: 1px 1px 1px 1px #ccc;
-webkit-box-shadow: 1px 1px 1px 1px #ccc; -webkit-box-shadow: 1px 1px 1px 1px #ccc;
box-shadow: 1px 1px 1px 1px #ccc; box-shadow: 1px 1px 1px 1px #ccc;
margin-bottom: 15px; margin-bottom: 15px;
} }
#info { #info {
text-align: left;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
background-color: white; background-color: white;
padding: 5px 5px 5px 5px;
}
#info #filename,
#editform #filename {
width: 232px;
padding: 5px 5px 5px 5px;
} }
#info #extension, #info #extension,
@ -91,13 +82,8 @@ body {
width: 40px; width: 40px;
} }
#info .float-left {
margin-top: 2px;
margin-right: 20px;
}
#info .right {
font-size: 13px;
#info .text-right {
font-size: 13px;
} }
#info a { #info a {
@ -110,88 +96,97 @@ body {
background-color: #E8ECF0; background-color: #E8ECF0;
} }
#info input[type=text] {
border: 0;
color: #556A7F;
#info input[type=checkbox] {
margin: 0;
vertical-align: bottom;
} }
#footer { #footer {
color: gray;
text-align: right;
margin-top: 30px;
margin-bottom: 10px;
font-size: 11px;
color: gray;
text-align: right;
margin-top: 30px;
margin-bottom: 10px;
font-size: 11px;
} }
#footer a { #footer a {
color: gray;
text-decoration: none;
color: gray;
text-decoration: none;
} }
.normal { .normal {
text-align: left;
font-size: 13px;
text-align: left;
font-size: 13px;
} }
.normal a { .normal a {
text-decoration: none;
border-bottom: 1px dotted gray;
text-decoration: none;
border-bottom: 1px dotted gray;
} }
.normal a:hover { .normal a:hover {
color: black;
background-color: #E8ECF0;
color: black;
background-color: #E8ECF0;
} }
.normal ul { .normal ul {
padding-left: 15px;
padding-left: 15px;
} }
.normal li { .normal li {
margin-bottom: 3px;
list-style: none;
margin-bottom: 3px;
list-style: none;
} }
.normal li a { .normal li a {
font-weight: bold;
font-weight: bold;
} }
.fixed { .fixed {
width: 800px;
width: 80vw;
max-width: 800px;
}
.paste {
width: 70vw;
max-width: 700px;
} }
.needs-border { .needs-border {
border-top: 1px solid rgb(214, 214, 214);
border-top: 1px solid rgb(214, 214, 214);
} }
.left { .left {
text-align: left;
text-align: left;
} }
.float-left { .float-left {
float: left;
float: left;
}
.pad-left {
padding-left: 10px;
} }
.pad-right { .pad-right {
padding-right: 10px;
padding-right: 10px;
} }
.text-right { .text-right {
text-align: right;
text-align: right;
} }
.center { .center {
text-align: center;
text-align: center;
} }
.float-right, .right { .float-right, .right {
float: right;
float: right;
} }
.clear { .clear {
clear: both;
clear: both;
} }
#upload_header { #upload_header {
@ -245,19 +240,28 @@ body {
} }
#choices { #choices {
float: left;
width: 100%;
text-align: left;
vertical-align: bottom;
margin-top: 5px;
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-between;
width: 100%;
margin-top: 5px;
font-size:13px; font-size:13px;
} }
#choices label:first-child {
margin-right: 15px;
}
#expiry { #expiry {
float: right;
padding-top: 1px; padding-top: 1px;
} }
#randomize {
vertical-align: bottom;
margin: 0;
}
.oopscontent { .oopscontent {
width: 400px; width: 400px;
} }
@ -267,13 +271,38 @@ body {
border: 0; border: 0;
} }
.error-404 img {
max-width: 90vw;
}
.padme {
padding-left: 5px;
padding-right: 5px;
}
.editor { .editor {
width: 705px;
height: 450px;
border-color: #cccccc;
font-family: monospace;
resize: none;
overflow: auto;
width: 100%;
height: 450px;
border: 1px solid #eaeaea;
font-family: monospace;
resize: none;
overflow: auto;
border-radius: 2px;
padding: 2px;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
}
#info input[type=text] {
border: 1px solid #eaeaea;
color: #556A7F;
border-radius: 4px 4px 4px 4px;
padding-left: 4px;
padding-right: 4px;
height: 15px;
} }
.storygreen { .storygreen {
@ -287,7 +316,7 @@ body {
/* Content display {{{ */ /* Content display {{{ */
.display-audio, .display-audio,
.display-file { .display-file {
width: 500px;
width: 100%;
} }
.display-image { .display-image {
@ -315,15 +344,16 @@ body {
#editform, #editform,
#editform .editor { #editform .editor {
display: none; display: none;
width: 100%
} }
#codeb { #codeb {
white-space: pre-wrap; white-space: pre-wrap;
} }
#editor {
#inplace-editor {
display: none; display: none;
width: 794px;
width: 100%;
height: 800px; height: 800px;
font-size: 13px; font-size: 13px;
} }

6
static/js/bin.js

@ -1,6 +1,6 @@
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later // @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later
var navlist = document.getElementById("info").getElementsByClassName("right")[0];
var navlist = document.getElementById("info").getElementsByClassName("text-right")[0];
init(); init();
@ -32,13 +32,13 @@ function edit(ev) {
var normalcontent = document.getElementById("normal-content"); var normalcontent = document.getElementById("normal-content");
normalcontent.removeChild(document.getElementById("normal-code")); normalcontent.removeChild(document.getElementById("normal-code"));
var editordiv = document.getElementById("editor");
var editordiv = document.getElementById("inplace-editor");
editordiv.style.display = "block"; editordiv.style.display = "block";
editordiv.addEventListener('keydown', handleTab); editordiv.addEventListener('keydown', handleTab);
} }
function paste(ev) { function paste(ev) {
var editordiv = document.getElementById("editor");
var editordiv = document.getElementById("inplace-editor");
document.getElementById("newcontent").value = editordiv.value; document.getElementById("newcontent").value = editordiv.value;
document.forms["reply"].submit(); document.forms["reply"].submit();
} }

12
static/js/upload.js

@ -102,8 +102,18 @@ Dropzone.options.dropzone = {
previewsContainer: "#uploads", previewsContainer: "#uploads",
parallelUploads: 5, parallelUploads: 5,
headers: {"Accept": "application/json"}, headers: {"Accept": "application/json"},
dictDefaultMessage: "Click or Drop file(s)",
dictDefaultMessage: "Click or Drop file(s) or Paste image",
dictFallbackMessage: "" dictFallbackMessage: ""
}; };
document.onpaste = function(event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (index in items) {
var item = items[index];
if (item.kind === "file") {
Dropzone.forElement("#dropzone").addFile(item.getAsFile());
}
}
};
// @end-license // @end-license

1
templates.go

@ -83,6 +83,7 @@ func renderTemplate(tpl *pongo2.Template, context pongo2.Context, r *http.Reques
} }
context["sitepath"] = Config.sitePath context["sitepath"] = Config.sitePath
context["selifpath"] = Config.selifPath
context["using_auth"] = Config.authFile != "" context["using_auth"] = Config.authFile != ""
return tpl.ExecuteWriter(context, writer) return tpl.ExecuteWriter(context, writer)

4
templates/404.html

@ -1,5 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<a href="{{ sitepath }}"><img src='{{ sitepath }}static/images/404.jpg'></a>
<div class="error-404">
<a href="{{ sitepath }}"><img src='{{ sitepath }}static/images/404.jpg'></a>
</div>
{% endblock %} {% endblock %}

2
templates/API.html

@ -41,6 +41,7 @@
<blockquote> <blockquote>
<p>“url”: the publicly available upload url<br/> <p>“url”: the publicly available upload url<br/>
“direct_url”: the url to access the file directly<br/>
“filename”: the (optionally generated) filename<br/> “filename”: the (optionally generated) filename<br/>
“delete_key”: the (optionally generated) deletion key,<br/> “delete_key”: the (optionally generated) deletion key,<br/>
“expiry”: the unix timestamp at which the file will expire (0 if never)<br/> “expiry”: the unix timestamp at which the file will expire (0 if never)<br/>
@ -121,6 +122,7 @@ DELETED</code></pre>
<blockquote> <blockquote>
<p>“url”: the publicly available upload url<br/> <p>“url”: the publicly available upload url<br/>
“direct_url”: the url to access the file directly<br/>
“filename”: the (optionally generated) filename<br/> “filename”: the (optionally generated) filename<br/>
“expiry”: the unix timestamp at which the file will expire (0 if never)<br/> “expiry”: the unix timestamp at which the file will expire (0 if never)<br/>
“size”: the size in bytes of the file<br/> “size”: the size in bytes of the file<br/>

1
templates/base.html

@ -3,6 +3,7 @@
<head> <head>
<title>{% block title %}{{ sitename }}{% endblock %}</title> <title>{% block title %}{{ sitename }}{% endblock %}</title>
<meta charset='utf-8' content='text/html' http-equiv='content-type'> <meta charset='utf-8' content='text/html' http-equiv='content-type'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link href='{{ sitepath }}static/css/linx.css' media='screen, projection' rel='stylesheet' type='text/css'> <link href='{{ sitepath }}static/css/linx.css' media='screen, projection' rel='stylesheet' type='text/css'>
<link href='{{ sitepath }}static/css/hint.css' rel='stylesheet' type='text/css'> <link href='{{ sitepath }}static/css/hint.css' rel='stylesheet' type='text/css'>
<link href='{{ sitepath }}static/images/favicon.gif' rel='icon' type='image/gif'> <link href='{{ sitepath }}static/images/favicon.gif' rel='icon' type='image/gif'>

4
templates/display/audio.html

@ -2,8 +2,8 @@
{% block main %} {% block main %}
<audio class="display-audio" controls preload='auto'> <audio class="display-audio" controls preload='auto'>
<source src='{{ sitepath }}selif/{{ filename }}'>
<a href='{{ sitepath }}selif/{{ filename }}'>Download it instead</a>
<source src='{{ sitepath }}{{ selifpath }}{{ filename }}'>
<a href='{{ sitepath }}{{ selifpath }}{{ filename }}'>Download it instead</a>
</audio> </audio>
{% endblock %} {% endblock %}

7
templates/display/base.html

@ -7,22 +7,21 @@
{% block content %} {% block content %}
<div id="info" class="dinfo"> <div id="info" class="dinfo">
<div class="float-left" id="filename">
<div id="filename">
{{ filename }} {{ filename }}
</div> </div>
<div class="right">
<div class='text-right pad-left'>
{% if expiry %} {% if expiry %}
<span>file expires in {{ expiry }}</span> | <span>file expires in {{ expiry }}</span> |
{% endif %} {% endif %}
{% block infomore %}{% endblock %} {% block infomore %}{% endblock %}
<span>{{ size }}</span> | <span>{{ size }}</span> |
<a href="{{ filename }}/torrent" download>torrent</a> | <a href="{{ filename }}/torrent" download>torrent</a> |
<a href="{{ sitepath }}selif/{{ filename }}" download>get</a>
<a href="{{ sitepath }}{{ selifpath }}{{ filename }}" download>get</a>
</div> </div>
{% block infoleft %}{% endblock %} {% block infoleft %}{% endblock %}
<div class="clear"></div>
</div> </div>
<div id="main" {% block mainmore %}{% endblock %}> <div id="main" {% block mainmore %}{% endblock %}>

20
templates/display/bin.html

@ -12,23 +12,17 @@
{% block infoleft %} {% block infoleft %}
<div id="editform"> <div id="editform">
<form id="reply" action='{{ sitepath }}upload' method='post' > <form id="reply" action='{{ sitepath }}upload' method='post' >
<div class="right">
<div class="right pad-left">
<button id="save">save</button>
<select id="expiry" name="expires"> <select id="expiry" name="expires">
<option disabled=disabled>Expires:</option> <option disabled=disabled>Expires:</option>
<option value="0">never</option>
<option value="60">a minute</option>
<option value="300">5 minutes</option>
<option value="3600">an hour</option>
<option value="86400">a day</option>
<option value="604800">a week</option>
<option value="2419200">a month</option>
<option value="29030400">a year</option>
{% for expiry in expirylist %}
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
{% endfor %}
</select> </select>
<button id="save">save</button>
</div> </div>
<input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename (empty for random filename)">.<input id="extension" class="codebox" name='extension' type='text' value="{{ extra.extension }}" placeholder="txt">
<input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename">.<input id="extension" class="codebox" name='extension' type='text' value="{{ extra.extension }}" placeholder="txt">
<textarea name='content' id="newcontent" class="editor"></textarea> <textarea name='content' id="newcontent" class="editor"></textarea>
</form> </form>
</div> </div>
@ -41,7 +35,7 @@
{% block main %} {% block main %}
<div id="normal-content" class="normal fixed"> <div id="normal-content" class="normal fixed">
<pre id="normal-code"><code id="codeb" class="{{ extra.lang_hl }}">{{ extra.contents }}</code></pre> <pre id="normal-code"><code id="codeb" class="{{ extra.lang_hl }}">{{ extra.contents }}</code></pre>
<textarea id="editor" class="editor">{{ extra.contents }}</textarea>
<textarea id="inplace-editor" class="editor">{{ extra.contents }}</textarea>
</div> </div>

2
templates/display/file.html

@ -2,7 +2,7 @@
{% block main %} {% block main %}
<div class="normal display-file"> <div class="normal display-file">
<p class="center">You are requesting <a href="{{ sitepath }}selif/{{ filename }}">{{ filename }}</a>, <a href="{{ sitepath }}selif/{{ filename }}">click here</a> to download.</p>
<p class="center">You are requesting <a href="{{ sitepath }}{{ selifpath }}{{ filename }}">{{ filename }}</a>, <a href="{{ sitepath }}{{ selifpath }}{{ filename }}">click here</a> to download.</p>
{% if files|length > 0 %} {% if files|length > 0 %}
<p>Contents of the archive:</p> <p>Contents of the archive:</p>

4
templates/display/image.html

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block main %} {% block main %}
<a href="{{ sitepath }}selif/{{ filename }}">
<img class="display-image" src="{{ sitepath }}selif/{{ filename }}" />
<a href="{{ sitepath }}{{ selifpath }}{{ filename }}">
<img class="display-image" src="{{ sitepath }}{{ selifpath }}{{ filename }}" />
</a> </a>
{% endblock %} {% endblock %}

4
templates/display/pdf.html

@ -1,10 +1,10 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block main %} {% block main %}
<object class="display-pdf" data="{{ sitepath }}selif/{{ filename }}" type="application/pdf">
<object class="display-pdf" data="{{ sitepath }}{{ selifpath }}{{ filename }}" type="application/pdf">
<p>It appears your Web browser is not configured to display PDF files. <p>It appears your Web browser is not configured to display PDF files.
No worries, just <a href="{{ sitepath }}selif/{{ filename }}">click here to download the PDF file.</a></p>
No worries, just <a href="{{ sitepath }}{{ selifpath }}{{ filename }}">click here to download the PDF file.</a></p>
</object> </object>
{% endblock %} {% endblock %}

20
templates/display/story.html

@ -10,23 +10,17 @@
{% block infoleft %} {% block infoleft %}
<div id="editform"> <div id="editform">
<form id="reply" action='{{ sitepath }}upload' method='post' > <form id="reply" action='{{ sitepath }}upload' method='post' >
<div class="right">
<div class="right pad-left">
<button id="save">save</button>
<select id="expiry" name="expires"> <select id="expiry" name="expires">
<option disabled=disabled>Expires:</option> <option disabled=disabled>Expires:</option>
<option value="0">never</option>
<option value="60">a minute</option>
<option value="300">5 minutes</option>
<option value="3600">an hour</option>
<option value="86400">a day</option>
<option value="604800">a week</option>
<option value="2419200">a month</option>
<option value="29030400">a year</option>
{% for expiry in expirylist %}
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
{% endfor %}
</select> </select>
<button id="save">save</button>
</div> </div>
<input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename (empty for random filename)">.<input id="extension" class="codebox" name='extension' type='text' value="story" placeholder="txt">
<input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename">.<input id="extension" class="codebox" name='extension' type='text' value="story" placeholder="txt">
<textarea name='content' id="newcontent" class="editor"></textarea> <textarea name='content' id="newcontent" class="editor"></textarea>
</form> </form>
</div> </div>
@ -39,7 +33,7 @@
{% block main %} {% block main %}
<div id="normal-content" class="normal"> <div id="normal-content" class="normal">
<pre id="normal-code"><code id="codeb" class="story">{% for line in lines %}{% if line|make_list|first == ">" %}<span class="storygreen">{{ line }}</span>{% else %}<span class="storyred">{{ line }}</span>{% endif %}{% endfor %}</code></pre> <pre id="normal-code"><code id="codeb" class="story">{% for line in lines %}{% if line|make_list|first == ">" %}<span class="storygreen">{{ line }}</span>{% else %}<span class="storyred">{{ line }}</span>{% endif %}{% endfor %}</code></pre>
<textarea id="editor" class="editor">{{ extra.contents }}</textarea>
<textarea id="inplace-editor" class="editor">{{ extra.contents }}</textarea>
</div> </div>

4
templates/display/video.html

@ -2,7 +2,7 @@
{% block main %} {% block main %}
<video class="display-video" controls autoplay> <video class="display-video" controls autoplay>
<source src="{{ sitepath }}selif/{{ filename }}"/>
<a href='{{ sitepath }}selif/{{ filename }}'>Download it instead</a>
<source src="{{ sitepath }}{{ selifpath }}{{ filename }}"/>
<a href='{{ sitepath }}{{ selifpath }}{{ filename }}'>Download it instead</a>
</video> </video>
{% endblock %} {% endblock %}

6
templates/index.html

@ -13,12 +13,13 @@
</div> </div>
<div id="dzone" class="dz-default dz-message"> <div id="dzone" class="dz-default dz-message">
<span>Click or Drop file(s)</span>
<span>Click or Drop file(s) or Paste image</span>
</div> </div>
<div id="choices"> <div id="choices">
<label><input name="randomize" id="randomize" type="checkbox" checked /> Randomize filename</label>
<div id="expiry"> <div id="expiry">
<label>File expiry:
<label>File expiry:
<select name="expires" id="expires"> <select name="expires" id="expires">
{% for expiry in expirylist %} {% for expiry in expirylist %}
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option> <option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
@ -26,7 +27,6 @@
</select> </select>
</label> </label>
</div> </div>
<label><input name="randomize" id="randomize" type="checkbox" checked /> Randomize filename</label>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</form> </form>

18
templates/paste.html

@ -2,24 +2,24 @@
{% block content %} {% block content %}
<form id="reply" action='{{ sitepath }}upload' method='post'> <form id="reply" action='{{ sitepath }}upload' method='post'>
<div id="main">
<div id="info" class="ninfo">
<input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename (empty for random filename)" />.<span class="hint--top hint--bounce" data-hint="Enable syntax highlighting by adding the extension"><input id="extension" class="codebox" name='extension' type='text' value="" placeholder="txt" /></span>
<div class="right">
<div id="main" class="paste">
<div id="info">
<div>
<span class="hint--top hint--bounce" data-hint="Leave empty for random filename"><input class="codebox" name='filename' id="filename" type='text' value="" placeholder="filename" /></span>.<span class="hint--top hint--bounce" data-hint="Enable syntax highlighting by adding the extension"><input id="extension" class="codebox" name='extension' type='text' value="" placeholder="txt" /></span>
</div>
<div>
<input type="submit" value="Paste">
<select id="expiry" name="expires"> <select id="expiry" name="expires">
<option disabled="disabled">Expires:</option> <option disabled="disabled">Expires:</option>
{% for expiry in expirylist %} {% for expiry in expirylist %}
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option> <option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
{% endfor %} {% endfor %}
</select> </select>
<input type="submit" value="Paste">
</div> </div>
</div> </div>
<div id="inner_content">
<textarea name='content' id="content" class="editor"></textarea>
<div id="inner_content" class="padme">
<textarea name='content' id="content" class="editor"></textarea>
</div> </div>
</div> </div>
</form> </form>

1
upload.go

@ -306,6 +306,7 @@ func generateBarename() string {
func generateJSONresponse(upload Upload, r *http.Request) []byte { func generateJSONresponse(upload Upload, r *http.Request) []byte {
js, _ := json.Marshal(map[string]string{ js, _ := json.Marshal(map[string]string{
"url": getSiteURL(r) + upload.Filename, "url": getSiteURL(r) + upload.Filename,
"direct_url": getSiteURL(r) + Config.selifPath + upload.Filename,
"filename": upload.Filename, "filename": upload.Filename,
"delete_key": upload.Metadata.DeleteKey, "delete_key": upload.Metadata.DeleteKey,
"expiry": strconv.FormatInt(upload.Metadata.Expiry.Unix(), 10), "expiry": strconv.FormatInt(upload.Metadata.Expiry.Unix(), 10),

Loading…
Cancel
Save