File edit modal

This commit is contained in:
2021-02-23 16:50:13 +01:00
parent 346fa355c4
commit 5e8598f112
10 changed files with 180 additions and 290 deletions

View File

@@ -1,20 +1,25 @@
function EditWindow() { function EditWindow() {
this.visible = false this.visible = false
this.modal = new Modal( this.modal = new Modal(
document.getElementById("file_viewer"), document.getElementById("file_viewer"),
() => { this.toggle() }, () => { this.toggle() },
"Edit File", "1000px", "auto", "Edit File", "1000px", "auto",
) )
let clone = document.getElementById("tpl_edit_file").content.cloneNode(true) let clone = document.getElementById("tpl_edit_file").content.cloneNode(true)
this.notification = clone.querySelector(".edit_file_notification")
this.fileNameField = clone.querySelector(".edit_file_name_field")
clone.querySelector(".btn_delete_file").addEventListener("click", () => { this.deleteFile() }) clone.querySelector(".btn_delete_file").addEventListener("click", () => { this.deleteFile() })
clone.querySelector(".edit_file_name_form").addEventListener("submit", e => { this.renameFile(e) })
this.modal.setBody(clone) this.modal.setBody(clone)
this.btnEdit = document.getElementById("btn_edit") this.btnEdit = document.getElementById("btn_edit")
this.btnEdit.addEventListener("click", () => { this.toggle() }) this.btnEdit.addEventListener("click", () => { this.toggle() })
} }
EditWindow.prototype.toggle = function() { EditWindow.prototype.toggle = function () {
if (this.visible) { if (this.visible) {
this.modal.close() this.modal.close()
this.btnEdit.classList.remove("button_highlight") this.btnEdit.classList.remove("button_highlight")
@@ -26,9 +31,11 @@ EditWindow.prototype.toggle = function() {
} }
} }
EditWindow.prototype.setFile = function(file) { EditWindow.prototype.setFile = function (file) {
this.file = file this.file = file
this.modal.setTitle("Editing "+file.name) this.modal.setTitle("Editing " + file.name)
this.notification.style.display = "none"
this.fileNameField.value = file.name
if (this.file.can_edit) { if (this.file.can_edit) {
this.btnEdit.style.display = "" this.btnEdit.style.display = ""
@@ -37,16 +44,44 @@ EditWindow.prototype.setFile = function(file) {
} }
} }
EditWindow.prototype.deleteFile = function() { EditWindow.prototype.notify = function (success, content) {
if (!confirm("Are you sure you want to delete '"+this.file.name+"'?")) { this.notification.style.display = ""
this.notification.classList = "edit_file_notification " + (success ? "highlight_green" : "highlight_red")
this.notification.innerHTML = content
}
EditWindow.prototype.deleteFile = async function () {
if (!confirm("Are you sure you want to delete '" + this.file.name + "'?")) {
return return
} }
fetch( try {
this.file.get_href, {method: "DELETE"} const resp = await fetch(this.file.get_href, { method: "DELETE" });
).then(resp => { if (resp.status >= 400) {
this.modal.setBody(document.createTextNode("This file has been deleted")) throw (await resp.json()).message
}).catch(err => { }
alert("Error! Could not delete file")
}) this.notify(true, "This file has been deleted, you can close the page")
} catch (err) {
this.notify(false, "Could not delete file: " + err)
}
}
EditWindow.prototype.renameFile = async function (e) {
e.preventDefault()
const form = new FormData()
form.append("action", "rename")
form.append("name", this.fileNameField.value)
try {
const resp = await fetch(this.file.get_href, { method: "POST", body: form });
if (resp.status >= 400) {
throw (await resp.json()).message
}
this.notify(true, "File name has been changed. Reload the page to see the changes")
} catch (err) {
this.notify(false, "Could not change file name: " + err)
}
} }

View File

@@ -1,21 +1,21 @@
function Viewer(type, viewToken, data) { function Viewer(type, viewToken, data) {
// Set defaults // Set defaults
this.toolbar = null this.toolbar = null
this.listNavigator = null this.listNavigator = null
this.detailsWindow = null this.detailsWindow = null
this.divFilepreview = null this.divFilepreview = null
this.file = null this.file = null
this.title = "" // Contains either the file name or list title this.title = "" // Contains either the file name or list title
this.listId = "" this.listId = ""
this.viewToken = viewToken this.viewToken = viewToken
this.isList = false this.isList = false
this.isFile = false this.isFile = false
this.initialized = false this.initialized = false
this.viewerScript = null this.viewerScript = null
this.toolbar = new Toolbar(this) this.toolbar = new Toolbar(this)
this.detailsWindow = new DetailsWindow(this) this.detailsWindow = new DetailsWindow(this)
this.editWindow = new EditWindow() this.editWindow = new EditWindow()
this.divFilepreview = document.getElementById("filepreview") this.divFilepreview = document.getElementById("filepreview")
@@ -59,11 +59,11 @@ function Viewer(type, viewToken, data) {
this.initialized = true this.initialized = true
} }
Viewer.prototype.getFile = function() { Viewer.prototype.getFile = function () {
return this.file return this.file
} }
Viewer.prototype.setFile = function(file) { Viewer.prototype.setFile = function (file) {
this.file = file this.file = file
if (this.isList) { if (this.isList) {
@@ -84,11 +84,10 @@ Viewer.prototype.setFile = function(file) {
// do anything about it anyway // do anything about it anyway
if (file.view_href !== "") { if (file.view_href !== "") {
fetch(file.view_href, { fetch(file.view_href, {
method: "POST", method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"}, headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: "token="+this.viewToken body: "token=" + this.viewToken
} })
)
} }
// Clean up the previous viewer if possible // Clean up the previous viewer if possible
@@ -108,8 +107,8 @@ Viewer.prototype.setFile = function(file) {
} }
if (file.abuse_type !== "") { if (file.abuse_type !== "") {
this.viewerScript = new AbuseViewer(this,file) this.viewerScript = new AbuseViewer(this, file)
}else if (file.mime_type.startsWith("image")) { } else if (file.mime_type.startsWith("image")) {
this.viewerScript = new ImageViewer(this, file) this.viewerScript = new ImageViewer(this, file)
} else if ( } else if (
file.mime_type.startsWith("video") || file.mime_type.startsWith("video") ||
@@ -140,21 +139,21 @@ Viewer.prototype.setFile = function(file) {
this.viewerScript.render(this.divFilepreview) this.viewerScript.render(this.divFilepreview)
} }
Viewer.prototype.renderSponsors = function() { Viewer.prototype.renderSponsors = function () {
// Check if the ad is enabled // Check if the ad is enabled
if (document.querySelector(".sponsors_banner") == null) { return } if (document.querySelector(".sponsors_banner") == null) { return }
let scaleWidth = 1 let scaleWidth = 1
let scaleHeight = 1 let scaleHeight = 1
let minWindowHeight = 800 let minWindowHeight = 800
let bannerWidth = document.querySelector(".sponsors_banner").offsetWidth let bannerWidth = document.querySelector(".sponsors_banner").offsetWidth
let bannerHeight = document.querySelector(".sponsors_banner").offsetHeight let bannerHeight = document.querySelector(".sponsors_banner").offsetHeight
if (window.innerWidth < bannerWidth) { if (window.innerWidth < bannerWidth) {
scaleWidth = window.innerWidth/bannerWidth scaleWidth = window.innerWidth / bannerWidth
} }
if (window.innerHeight < minWindowHeight) { if (window.innerHeight < minWindowHeight) {
scaleHeight = window.innerHeight/minWindowHeight scaleHeight = window.innerHeight / minWindowHeight
} }
// The smaller scale is the scale we'll use // The smaller scale is the scale we'll use
@@ -165,63 +164,66 @@ Viewer.prototype.renderSponsors = function() {
// width of the viewport - the width of the ad to calculate the amount of // width of the viewport - the width of the ad to calculate the amount of
// pixels around the ad. We multiply the ad size by the scale we calcualted // pixels around the ad. We multiply the ad size by the scale we calcualted
// to account for the smaller size. // to account for the smaller size.
let offset = (window.innerWidth - (bannerWidth*scale)) / 2 let offset = (window.innerWidth - (bannerWidth * scale)) / 2
document.querySelector(".sponsors").style.height = (bannerHeight*scale)+"px" document.querySelector(".sponsors").style.height = (bannerHeight * scale) + "px"
document.querySelector(".sponsors_banner").style.marginLeft = offset+"px" document.querySelector(".sponsors_banner").style.marginLeft = offset + "px"
document.querySelector(".sponsors_banner").style.transform = "scale("+scale+")" document.querySelector(".sponsors_banner").style.transform = "scale(" + scale + ")"
} }
Viewer.prototype.keyboardEvent = function(evt) { Viewer.prototype.keyboardEvent = function (evt) {
if (evt.ctrlKey || evt.altKey || evt.metaKey) { if (evt.ctrlKey || evt.altKey || evt.metaKey) {
return // prevent custom shortcuts from interfering with system shortcuts return // prevent custom shortcuts from interfering with system shortcuts
} }
if (document.activeElement.type && document.activeElement.type === "text") {
return // Prevent shortcuts from interfering with input fields
}
console.debug("Key pressed: "+evt.keyCode) console.debug("Key pressed: " + evt.keyCode)
switch (evt.keyCode) { switch (evt.keyCode) {
case 65: // A or left arrow key go to previous file case 65: // A or left arrow key go to previous file
case 37: case 37:
if (this.listNavigator != null) { if (this.listNavigator != null) {
this.listNavigator.previousItem() this.listNavigator.previousItem()
} }
break break
case 68: // D or right arrow key go to next file case 68: // D or right arrow key go to next file
case 39: case 39:
if (this.listNavigator != null) { if (this.listNavigator != null) {
this.listNavigator.nextItem() this.listNavigator.nextItem()
} }
break break
case 83: case 83:
if (evt.shiftKey) { if (evt.shiftKey) {
this.listNavigator.downloadList() // SHIFT + S downloads all files in list this.listNavigator.downloadList() // SHIFT + S downloads all files in list
} else { } else {
this.toolbar.download() // S to download the current file this.toolbar.download() // S to download the current file
} }
break break
case 82: // R to toggle list shuffle case 82: // R to toggle list shuffle
if (this.listNavigator != null) { if (this.listNavigator != null) {
this.listNavigator.toggleShuffle() this.listNavigator.toggleShuffle()
} }
break break
case 67: // C to copy to clipboard case 67: // C to copy to clipboard
this.toolbar.copyUrl() this.toolbar.copyUrl()
break break
case 73: // I to open the details window case 73: // I to open the details window
this.detailsWindow.toggle() this.detailsWindow.toggle()
break break
case 69: // E to open the edit window case 69: // E to open the edit window
this.editWindow.toggle() this.editWindow.toggle()
break break
case 81: // Q to close the window case 81: // Q to close the window
window.close() window.close()
break break
} }
} }
// Against XSS attacks // Against XSS attacks
function escapeHTML(str) { function escapeHTML(str) {
return String(str) return String(str)
.replace(/&/g, '&amp;') .replace(/&/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;') .replace(/>/g, '&gt;')
@@ -229,15 +231,15 @@ function escapeHTML(str) {
} }
function fileFromAPIResp(resp) { function fileFromAPIResp(resp) {
resp.date_created = new Date(resp.date_upload) resp.date_created = new Date(resp.date_upload)
resp.date_last_view = new Date(resp.date_last_view) resp.date_last_view = new Date(resp.date_last_view)
resp.icon_href = apiEndpoint+"/file/"+resp.id+"/thumbnail" resp.icon_href = apiEndpoint + "/file/" + resp.id + "/thumbnail"
resp.get_href = apiEndpoint+"/file/"+resp.id resp.get_href = apiEndpoint + "/file/" + resp.id
resp.download_href = apiEndpoint+"/file/"+resp.id+"?download" resp.download_href = apiEndpoint + "/file/" + resp.id + "?download"
resp.view_href = apiEndpoint+"/file/"+resp.id+"/view" resp.view_href = apiEndpoint + "/file/" + resp.id + "/view"
resp.timeseries_href = apiEndpoint+"/file/"+resp.id+"/timeseries" resp.timeseries_href = apiEndpoint + "/file/" + resp.id + "/timeseries"
resp.stats_href = apiEndpoint+"/file/"+resp.id+"/stats" resp.stats_href = apiEndpoint + "/file/" + resp.id + "/stats"
resp.link = domainURL()+"/u/"+resp.id resp.link = domainURL() + "/u/" + resp.id
if (resp.description === undefined) { if (resp.description === undefined) {
resp.description = "" resp.description = ""
} }
@@ -246,12 +248,12 @@ function fileFromAPIResp(resp) {
} }
function fileFromSkyNet(resp) { function fileFromSkyNet(resp) {
let file = fileFromAPIResp(resp) let file = fileFromAPIResp(resp)
file.icon_href = "/res/img/mime/empty.png" file.icon_href = "/res/img/mime/empty.png"
file.get_href = "https://skydrain.net/file/"+resp.id file.get_href = "https://siasky.net/file/" + resp.id
file.download_href = "https://skydrain.net/file/"+resp.id+"?attachment=1" file.download_href = "https://siasky.net/file/" + resp.id + "?attachment=1"
file.view_href = "" file.view_href = ""
file.timeseries_href = "" file.timeseries_href = ""
file.stats_href = "" file.stats_href = ""
file.link = domainURL()+"/s/"+resp.id file.link = domainURL() + "/s/" + resp.id
return file return file
} }

View File

@@ -244,7 +244,15 @@
</template> </template>
<template id="tpl_edit_file"> <template id="tpl_edit_file">
<h3>Delete file</h3> <div class="edit_file_notification" style="display: none;"></div>
<h3>Rename</h3>
<form class="edit_file_name_form" style="display: flex; width: 100%">
<input class="edit_file_name_field" type="text" style="flex: 1 1 auto"/>
<button class="edit_file_name_submit" role="submit" style="flex: 0 0 auto">
<i class="icon">save</i> Save
</button>
</form>
<h3>Delete</h3>
<p> <p>
When you delete a file it cannot be recovered. When you delete a file it cannot be recovered.
Nobody will be able to download it and the link will Nobody will be able to download it and the link will

View File

@@ -1,40 +1,33 @@
{{ define "filesystem" }}<!DOCTYPE html> {{define "filesystem"}}
<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
{{template "meta_tags" .Title}} <title>{{.Title}}</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{template "user_style" .}} {{template "user_style" .}}
<style>{{template "file_manager.css" .}}</style>
<script>var apiEndpoint = '{{.APIEndpoint}}';</script> <link rel="icon" sizes="32x32" href="/res/img/pixeldrain_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/pixeldrain_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/pixeldrain_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<meta name="theme-color" content="#75AD38"/>
{{ template "opengraph" .OGData }}
<script>
const initialNode = {{.Other}};
window.api_endpoint = '{{.APIEndpoint}}';
</script>
<link rel='stylesheet' href='/res/svelte/filesystem.css'>
<script defer src='/res/svelte/filesystem.js'></script>
</head> </head>
<body></body>
<body>
{{template "page_top" .}}
<h1>{{ .Title}}</h1>
<div class="page_content">
<div class="limit_width">
<h2>
<a href="/fs">Buckets</a>
{{ range $node := .Other.Path }}
/ <a href="{{ $node.HREF }}">{{ $node.Name }}</a>
{{ end }}
{{ if ne .Other.Base.Name "" }}
/ {{ .Other.Base.Name }}
{{ end }}
</h2>
{{ range $node := .Other.Children }}
<a class="node" href="{{ $node.HREF }}"><div>
<img src="{{ $node.Icon }}"/>
<span>{{ $node.Name }}</span>
</div></a>
{{ end }}
</div>
</div>
{{template "page_bottom" .}}
{{template "analytics"}}
</body>
</html> </html>
{{ end }} {{end}}

View File

@@ -1,33 +0,0 @@
{{define "filesystem_svelte"}}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{.Title}}</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
{{template "user_style" .}}
<link rel="icon" sizes="32x32" href="/res/img/pixeldrain_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/pixeldrain_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/pixeldrain_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<meta name="theme-color" content="#75AD38"/>
{{ template "opengraph" .OGData }}
<script>
const initialNode = {{.Other}};
window.api_endpoint = '{{.APIEndpoint}}';
</script>
<link rel='stylesheet' href='/res/svelte/filesystem.css'>
<script defer src='/res/svelte/filesystem.js'></script>
</head>
<body></body>
</html>
{{end}}

View File

@@ -13,8 +13,7 @@ let new_bucket_name
const get_buckets = async () => { const get_buckets = async () => {
loading = true; loading = true;
try { try {
let resp = await fs_get_buckets(); buckets = await fs_get_buckets();
buckets = resp.buckets;
} catch (err) { } catch (err) {
alert(err); alert(err);
} finally { } finally {

View File

@@ -28,7 +28,7 @@ func (wc *WebController) adminGlobalsForm(td *TemplateData, r *http.Request) (f
return f return f
} }
var globalsMap = make(map[string]string) var globalsMap = make(map[string]string)
for _, v := range globals.Globals { for _, v := range globals {
f.Fields = append(f.Fields, Field{ f.Fields = append(f.Fields, Field{
Name: v.Key, Name: v.Key,
DefaultValue: v.Value, DefaultValue: v.Value,

View File

@@ -225,7 +225,7 @@ func (wc *WebController) serveSkynetViewer(w http.ResponseWriter, r *http.Reques
// Get the first few bytes from the file to probe the content type and // Get the first few bytes from the file to probe the content type and
// length // length
rq, err := http.NewRequest("GET", "https://skydrain.net/file/"+p.ByName("id"), nil) rq, err := http.NewRequest("GET", "https://siasky.net/file/"+p.ByName("id"), nil)
if err != nil { if err != nil {
log.Warn("Failed to make request to sia portal: %s", err) log.Warn("Failed to make request to sia portal: %s", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)

View File

@@ -3,123 +3,12 @@ package webcontroller
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"sort"
"strings" "strings"
"fornaxian.tech/pixeldrain_server/api/restapi/apitype"
"github.com/Fornaxian/log" "github.com/Fornaxian/log"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
) )
type filesystemPath struct {
Path []filesystemNode
Base filesystemNode
Children []filesystemNode
}
type filesystemNode struct {
HREF string
Icon string
Name string
Type string
FileSize int64
FileType string
}
func convFilesystemNode(bucketID string, v apitype.FilesystemNode) (node filesystemNode) {
node = filesystemNode{
HREF: "/fs/" + bucketID + v.Path,
Type: v.Type,
Name: v.Name,
FileSize: v.FileSize,
FileType: v.FileType,
}
if node.Type == "dir" {
node.Icon = "/res/img/mime/folder.png"
} else {
node.Icon = "/res/img/mime/empty.png"
}
return node
}
func (wc *WebController) serveFilesystem(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var err error
var td = wc.newTemplateData(w, r)
var path = strings.TrimPrefix(p.ByName("path"), "/")
var fsPath filesystemPath
if path == "" {
buckets, err := td.PixelAPI.GetFilesystemBuckets()
if err != nil {
if err.Error() == "not_found" {
wc.templates.Get().ExecuteTemplate(w, "404", td)
} else if err.Error() == "authentication_required" {
http.Redirect(w, r, "/login", http.StatusSeeOther)
} else {
log.Error("Failed to get buckets: %s", err)
wc.templates.Get().ExecuteTemplate(w, "500", td)
}
return
}
for _, v := range buckets.Buckets {
fsPath.Children = append(fsPath.Children, filesystemNode{
HREF: "/fs/" + v.ID,
Icon: "/res/img/mime/folder-remote.png",
Type: "dir",
Name: v.Name,
FileSize: 0,
FileType: "inode/directory",
})
}
} else {
log.Info("getting path %s", path)
node, err := td.PixelAPI.GetFilesystemPath(path)
if err != nil {
if err.Error() == "not_found" || err.Error() == "path_not_found" {
wc.templates.Get().ExecuteTemplate(w, "404", td)
} else {
log.Error("Failed to get path: %s", err)
wc.templates.Get().ExecuteTemplate(w, "500", td)
}
return
}
if node.Base.Type == "file" {
http.Redirect(w, r, "/api/filesystem/"+path, http.StatusSeeOther)
return
}
for _, v := range node.Parents {
fsPath.Path = append(fsPath.Path, convFilesystemNode(node.Bucket.ID, v))
}
fsPath.Base = convFilesystemNode(node.Bucket.ID, node.Base)
for _, v := range node.Base.Children {
fsPath.Children = append(fsPath.Children, convFilesystemNode(node.Bucket.ID, v))
}
}
sort.Slice(fsPath.Children, func(i, j int) (less bool) {
// Directories always come first. Make sure we're comparing apples with
// apples
if fsPath.Children[i].Type != fsPath.Children[j].Type {
return fsPath.Children[i].Type == "dir"
}
return fsPath.Children[i].Name < fsPath.Children[j].Name
})
td.Title = "Filesystem"
td.Other = fsPath
err = wc.templates.Get().ExecuteTemplate(w, "filesystem", td)
if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template filesystem: %s", err)
}
}
func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request, p httprouter.Params) { func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var err error var err error
var td = wc.newTemplateData(w, r) var td = wc.newTemplateData(w, r)
@@ -138,7 +27,7 @@ func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request,
td.Title = fmt.Sprintf("%s ~ pixeldrain", node.Base.Name) td.Title = fmt.Sprintf("%s ~ pixeldrain", node.Base.Name)
td.Other = node td.Other = node
err = wc.templates.Get().ExecuteTemplate(w, "filesystem_svelte", td) err = wc.templates.Get().ExecuteTemplate(w, "filesystem", td)
if err != nil && !strings.Contains(err.Error(), "broken pipe") { if err != nil && !strings.Contains(err.Error(), "broken pipe") {
log.Error("Error executing template filesystem: %s", err) log.Error("Error executing template filesystem: %s", err)
} }

View File

@@ -114,6 +114,7 @@ func New(
{GET, "u/:id" /* */, wc.serveFileViewer}, {GET, "u/:id" /* */, wc.serveFileViewer},
{GET, "u/:id/preview" /* */, wc.serveFilePreview}, {GET, "u/:id/preview" /* */, wc.serveFilePreview},
{GET, "l/:id" /* */, wc.serveListViewer}, {GET, "l/:id" /* */, wc.serveListViewer},
{GET, "d/*path" /* */, wc.serveDirectory},
{GET, "s/:id" /* */, wc.serveSkynetViewer}, {GET, "s/:id" /* */, wc.serveSkynetViewer},
{GET, "t" /* */, wc.serveTemplate("text_editor", false)}, {GET, "t" /* */, wc.serveTemplate("text_editor", false)},
{GET, "donation" /* */, wc.serveMarkdown("donation.md", false)}, {GET, "donation" /* */, wc.serveMarkdown("donation.md", false)},
@@ -150,10 +151,6 @@ func New(
{GET, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, false)}, {GET, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, false)},
{PST, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, false)}, {PST, "user/password_reset_confirm" /**/, wc.serveForm(wc.passwordResetConfirmForm, false)},
// Filesystem
{GET, "fs/*path", wc.serveFilesystem},
{GET, "d/*path", wc.serveDirectory},
{GET, "patreon_activate" /* */, wc.serveForm(wc.patreonLinkForm, true)}, {GET, "patreon_activate" /* */, wc.serveForm(wc.patreonLinkForm, true)},
{PST, "patreon_activate" /* */, wc.serveForm(wc.patreonLinkForm, true)}, {PST, "patreon_activate" /* */, wc.serveForm(wc.patreonLinkForm, true)},