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 \
&& apk add --no-cache --virtual .build-deps git \
&& go get github.com/andreimarcu/linx-server \
&& go get -v . \
&& 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
VOLUME ["/data/files", "/data/meta"]
EXPOSE 8080
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"]

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)
- ```-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)
- ```-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/)
- ```-metapath meta/``` -- Path to store information about uploads (default is meta/)
- ```-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.noLogs = true
Config.siteName = "linx"
Config.selifPath = "selif"
Config.contentSecurityPolicy = testCSPHeaders["Content-Security-Policy"]
Config.referrerPolicy = testCSPHeaders["Referrer-Policy"]
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")
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)
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")) {
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)
return
@ -133,13 +134,14 @@ func fileDisplayHandler(c web.C, w http.ResponseWriter, r *http.Request) {
}
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)
if err != nil {

12
server.go

@ -42,6 +42,7 @@ var Config struct {
siteName string
siteURL string
sitePath string
selifPath string
certFile string
keyFile string
contentSecurityPolicy string
@ -131,6 +132,11 @@ func setup() *web.Mux {
Config.sitePath = "/"
}
Config.selifPath = strings.TrimLeft(Config.selifPath, "/")
if lastChar := Config.selifPath[len(Config.selifPath)-1:]; lastChar != "/" {
Config.selifPath = Config.selifPath + "/"
}
if Config.s3Bucket != "" {
storageBackend = s3.NewS3Backend(Config.s3Bucket, Config.s3Region, Config.s3Endpoint)
} else {
@ -154,8 +160,8 @@ func setup() *web.Mux {
// Routing setup
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$`)
if Config.authFile == "" {
@ -215,6 +221,8 @@ func main() {
"name of the site")
flag.StringVar(&Config.siteURL, "siteurl", "",
"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,
"maximum upload file size in bytes (default 4GB)")
flag.Uint64Var(&Config.maxExpiry, "maxexpiry", 0,

4
server_test.go

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

10
static/css/dropzone.css

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

3
static/css/github-markdown.css

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

208
static/css/linx.css

@ -1,56 +1,56 @@
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 {
display: table;
table-layout: fixed;
margin-left: auto;
margin-right: auto;
display: table;
table-layout: fixed;
margin-left: auto;
margin-right: auto;
}
#container {
display: table-cell;
min-width: 200px;
display: table-cell;
min-width: 200px;
}
#header a {
text-decoration: none;
color: #556A7F;
text-decoration: none;
color: #556A7F;
}
#navigation {
margin-top: 4px;
margin-top: 4px;
}
#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 {
background-color: #C7D1EB;
background-color: #C7D1EB;
}
#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 {
color: #556A7F;
color: #556A7F;
}
#normal-content {
@ -62,28 +62,19 @@ body {
margin-bottom: 0;
}
.ninfo {
margin-bottom: 5px;
}
.dinfo {
-moz-box-shadow: 1px 1px 1px 1px #ccc;
-webkit-box-shadow: 1px 1px 1px 1px #ccc;
box-shadow: 1px 1px 1px 1px #ccc;
margin-bottom: 15px;
}
#info {
text-align: left;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
background-color: white;
padding: 5px 5px 5px 5px;
}
#info #filename,
#editform #filename {
width: 232px;
padding: 5px 5px 5px 5px;
}
#info #extension,
@ -91,13 +82,8 @@ body {
width: 40px;
}
#info .float-left {
margin-top: 2px;
margin-right: 20px;
}
#info .right {
font-size: 13px;
#info .text-right {
font-size: 13px;
}
#info a {
@ -110,88 +96,97 @@ body {
background-color: #E8ECF0;
}
#info input[type=text] {
border: 0;
color: #556A7F;
#info input[type=checkbox] {
margin: 0;
vertical-align: bottom;
}
#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 {
color: gray;
text-decoration: none;
color: gray;
text-decoration: none;
}
.normal {
text-align: left;
font-size: 13px;
text-align: left;
font-size: 13px;
}
.normal a {
text-decoration: none;
border-bottom: 1px dotted gray;
text-decoration: none;
border-bottom: 1px dotted gray;
}
.normal a:hover {
color: black;
background-color: #E8ECF0;
color: black;
background-color: #E8ECF0;
}
.normal ul {
padding-left: 15px;
padding-left: 15px;
}
.normal li {
margin-bottom: 3px;
list-style: none;
margin-bottom: 3px;
list-style: none;
}
.normal li a {
font-weight: bold;
font-weight: bold;
}
.fixed {
width: 800px;
width: 80vw;
max-width: 800px;
}
.paste {
width: 70vw;
max-width: 700px;
}
.needs-border {
border-top: 1px solid rgb(214, 214, 214);
border-top: 1px solid rgb(214, 214, 214);
}
.left {
text-align: left;
text-align: left;
}
.float-left {
float: left;
float: left;
}
.pad-left {
padding-left: 10px;
}
.pad-right {
padding-right: 10px;
padding-right: 10px;
}
.text-right {
text-align: right;
text-align: right;
}
.center {
text-align: center;
text-align: center;
}
.float-right, .right {
float: right;
float: right;
}
.clear {
clear: both;
clear: both;
}
#upload_header {
@ -245,19 +240,28 @@ body {
}
#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;
}
#choices label:first-child {
margin-right: 15px;
}
#expiry {
float: right;
padding-top: 1px;
}
#randomize {
vertical-align: bottom;
margin: 0;
}
.oopscontent {
width: 400px;
}
@ -267,13 +271,38 @@ body {
border: 0;
}
.error-404 img {
max-width: 90vw;
}
.padme {
padding-left: 5px;
padding-right: 5px;
}
.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 {
@ -287,7 +316,7 @@ body {
/* Content display {{{ */
.display-audio,
.display-file {
width: 500px;
width: 100%;
}
.display-image {
@ -315,15 +344,16 @@ body {
#editform,
#editform .editor {
display: none;
width: 100%
}
#codeb {
white-space: pre-wrap;
}
#editor {
#inplace-editor {
display: none;
width: 794px;
width: 100%;
height: 800px;
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
var navlist = document.getElementById("info").getElementsByClassName("right")[0];
var navlist = document.getElementById("info").getElementsByClassName("text-right")[0];
init();
@ -32,13 +32,13 @@ function edit(ev) {
var normalcontent = document.getElementById("normal-content");
normalcontent.removeChild(document.getElementById("normal-code"));
var editordiv = document.getElementById("editor");
var editordiv = document.getElementById("inplace-editor");
editordiv.style.display = "block";
editordiv.addEventListener('keydown', handleTab);
}
function paste(ev) {
var editordiv = document.getElementById("editor");
var editordiv = document.getElementById("inplace-editor");
document.getElementById("newcontent").value = editordiv.value;
document.forms["reply"].submit();
}

12
static/js/upload.js

@ -102,8 +102,18 @@ Dropzone.options.dropzone = {
previewsContainer: "#uploads",
parallelUploads: 5,
headers: {"Accept": "application/json"},
dictDefaultMessage: "Click or Drop file(s)",
dictDefaultMessage: "Click or Drop file(s) or Paste image",
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

1
templates.go

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

4
templates/404.html

@ -1,5 +1,7 @@
{% extends "base.html" %}
{% 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 %}

2
templates/API.html

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

1
templates/base.html

@ -3,6 +3,7 @@
<head>
<title>{% block title %}{{ sitename }}{% endblock %}</title>
<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/hint.css' rel='stylesheet' type='text/css'>
<link href='{{ sitepath }}static/images/favicon.gif' rel='icon' type='image/gif'>

4
templates/display/audio.html

@ -2,8 +2,8 @@
{% block main %}
<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>
{% endblock %}

7
templates/display/base.html

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

20
templates/display/bin.html

@ -12,23 +12,17 @@
{% block infoleft %}
<div id="editform">
<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">
<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>
<button id="save">save</button>
</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>
</form>
</div>
@ -41,7 +35,7 @@
{% block main %}
<div id="normal-content" class="normal fixed">
<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>

2
templates/display/file.html

@ -2,7 +2,7 @@
{% block main %}
<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 %}
<p>Contents of the archive:</p>

4
templates/display/image.html

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% 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>
{% endblock %}

4
templates/display/pdf.html

@ -1,10 +1,10 @@
{% extends "base.html" %}
{% 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.
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>
{% endblock %}

20
templates/display/story.html

@ -10,23 +10,17 @@
{% block infoleft %}
<div id="editform">
<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">
<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>
<button id="save">save</button>
</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>
</form>
</div>
@ -39,7 +33,7 @@
{% block main %}
<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>
<textarea id="editor" class="editor">{{ extra.contents }}</textarea>
<textarea id="inplace-editor" class="editor">{{ extra.contents }}</textarea>
</div>

4
templates/display/video.html

@ -2,7 +2,7 @@
{% block main %}
<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>
{% endblock %}

6
templates/index.html

@ -13,12 +13,13 @@
</div>
<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 id="choices">
<label><input name="randomize" id="randomize" type="checkbox" checked /> Randomize filename</label>
<div id="expiry">
<label>File expiry:
<label>File expiry:
<select name="expires" id="expires">
{% for expiry in expirylist %}
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
@ -26,7 +27,6 @@
</select>
</label>
</div>
<label><input name="randomize" id="randomize" type="checkbox" checked /> Randomize filename</label>
</div>
<div class="clear"></div>
</form>

18
templates/paste.html

@ -2,24 +2,24 @@
{% block content %}
<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">
<option disabled="disabled">Expires:</option>
{% for expiry in expirylist %}
<option value="{{ expiry.Seconds }}"{% if forloop.Last %} selected{% endif %}>{{ expiry.Human }}</option>
{% endfor %}
</select>
<input type="submit" value="Paste">
</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>
</form>

1
upload.go

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

Loading…
Cancel
Save