mirror of
https://github.com/schollz/cowyo.git
synced 2023-08-10 21:13:00 +03:00
Add "/uploads" to list all uploaded files
This commit is contained in:
parent
df406ec71b
commit
fc3030339f
59
handlers.go
59
handlers.go
@ -57,6 +57,10 @@ func serve(
|
|||||||
|
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
|
router.SetFuncMap(template.FuncMap{
|
||||||
|
"sniffContentType": sniffContentType,
|
||||||
|
})
|
||||||
|
|
||||||
if hotTemplateReloading {
|
if hotTemplateReloading {
|
||||||
router.LoadHTMLGlob("templates/*.tmpl")
|
router.LoadHTMLGlob("templates/*.tmpl")
|
||||||
} else {
|
} else {
|
||||||
@ -307,23 +311,34 @@ func handlePageRequest(c *gin.Context) {
|
|||||||
c.Data(http.StatusOK, contentType(filename), data)
|
c.Data(http.StatusOK, contentType(filename), data)
|
||||||
return
|
return
|
||||||
} else if page == "uploads" {
|
} else if page == "uploads" {
|
||||||
pathname := path.Join(pathToData, command[1:]+".upload")
|
if len(command) == 0 || command == "/" || command == "/edit" {
|
||||||
|
if !allowFileUploads {
|
||||||
if allowInsecureHtml {
|
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Uploads are disabled on this server"))
|
||||||
c.Header(
|
return
|
||||||
"Content-Disposition",
|
}
|
||||||
`inline; filename="`+c.DefaultQuery("filename", "upload")+`"`,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// Prevent malicious html uploads by forcing type to plaintext and 'download-instead-of-view'
|
command = command[1:]
|
||||||
c.Header("Content-Type", "text/plain")
|
if !strings.HasSuffix(command, ".upload") {
|
||||||
c.Header(
|
command = command + ".upload"
|
||||||
"Content-Disposition",
|
}
|
||||||
`attachment; filename="`+c.DefaultQuery("filename", "upload")+`"`,
|
pathname := path.Join(pathToData, command)
|
||||||
)
|
|
||||||
|
if allowInsecureHtml {
|
||||||
|
c.Header(
|
||||||
|
"Content-Disposition",
|
||||||
|
`inline; filename="`+c.DefaultQuery("filename", "upload")+`"`,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Prevent malicious html uploads by forcing type to plaintext and 'download-instead-of-view'
|
||||||
|
c.Header("Content-Type", "text/plain")
|
||||||
|
c.Header(
|
||||||
|
"Content-Disposition",
|
||||||
|
`attachment; filename="`+c.DefaultQuery("filename", "upload")+`"`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
c.File(pathname)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c.File(pathname)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p := Open(page)
|
p := Open(page)
|
||||||
@ -410,11 +425,20 @@ func handlePageRequest(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var DirectoryEntries []DirectoryEntry
|
var DirectoryEntries []os.FileInfo
|
||||||
if page == "ls" {
|
if page == "ls" {
|
||||||
command = "/view"
|
command = "/view"
|
||||||
DirectoryEntries = DirectoryList()
|
DirectoryEntries = DirectoryList()
|
||||||
}
|
}
|
||||||
|
if page == "uploads" {
|
||||||
|
command = "/view"
|
||||||
|
var err error
|
||||||
|
DirectoryEntries, err = UploadList()
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// swap out /view for /read if it is published
|
// swap out /view for /read if it is published
|
||||||
if p.IsPublished {
|
if p.IsPublished {
|
||||||
@ -432,7 +456,8 @@ func handlePageRequest(c *gin.Context) {
|
|||||||
command[0:2] != "/l" &&
|
command[0:2] != "/l" &&
|
||||||
command[0:2] != "/r" &&
|
command[0:2] != "/r" &&
|
||||||
command[0:2] != "/h",
|
command[0:2] != "/h",
|
||||||
"DirectoryPage": page == "ls",
|
"DirectoryPage": page == "ls" || page == "uploads",
|
||||||
|
"UploadPage": page == "uploads",
|
||||||
"DirectoryEntries": DirectoryEntries,
|
"DirectoryEntries": DirectoryEntries,
|
||||||
"Page": page,
|
"Page": page,
|
||||||
"RenderedPage": template.HTML([]byte(rawHTML)),
|
"RenderedPage": template.HTML([]byte(rawHTML)),
|
||||||
|
53
page.go
53
page.go
@ -54,7 +54,7 @@ func Open(name string) (p *Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DirectoryEntry struct {
|
type DirectoryEntry struct {
|
||||||
Name string
|
Path string
|
||||||
Length int
|
Length int
|
||||||
Numchanges int
|
Numchanges int
|
||||||
LastEdited time.Time
|
LastEdited time.Time
|
||||||
@ -64,23 +64,66 @@ func (d DirectoryEntry) LastEditTime() string {
|
|||||||
return d.LastEdited.Format("Mon Jan 2 15:04:05 MST 2006")
|
return d.LastEdited.Format("Mon Jan 2 15:04:05 MST 2006")
|
||||||
}
|
}
|
||||||
|
|
||||||
func DirectoryList() []DirectoryEntry {
|
func (d DirectoryEntry) Name() string {
|
||||||
|
return d.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DirectoryEntry) Size() int64 {
|
||||||
|
return int64(d.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DirectoryEntry) Mode() os.FileMode {
|
||||||
|
return os.ModePerm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DirectoryEntry) ModTime() time.Time {
|
||||||
|
return d.LastEdited
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DirectoryEntry) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DirectoryEntry) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DirectoryList() []os.FileInfo {
|
||||||
files, _ := ioutil.ReadDir(pathToData)
|
files, _ := ioutil.ReadDir(pathToData)
|
||||||
entries := make([]DirectoryEntry, len(files))
|
entries := make([]os.FileInfo, len(files))
|
||||||
for i, f := range files {
|
for i, f := range files {
|
||||||
name := DecodeFileName(f.Name())
|
name := DecodeFileName(f.Name())
|
||||||
p := Open(name)
|
p := Open(name)
|
||||||
entries[i] = DirectoryEntry{
|
entries[i] = DirectoryEntry{
|
||||||
Name: name,
|
Path: name,
|
||||||
Length: len(p.Text.GetCurrent()),
|
Length: len(p.Text.GetCurrent()),
|
||||||
Numchanges: p.Text.NumEdits(),
|
Numchanges: p.Text.NumEdits(),
|
||||||
LastEdited: time.Unix(p.Text.LastEditTime()/1000000000, 0),
|
LastEdited: time.Unix(p.Text.LastEditTime()/1000000000, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(entries, func(i, j int) bool { return entries[i].LastEdited.After(entries[j].LastEdited) })
|
sort.Slice(entries, func(i, j int) bool { return entries[i].ModTime().After(entries[j].ModTime()) })
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UploadEntry struct {
|
||||||
|
os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadList() ([]os.FileInfo, error) {
|
||||||
|
paths, err := filepath.Glob(path.Join(pathToData, "sha256*"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]os.FileInfo, len(paths))
|
||||||
|
for i := range paths {
|
||||||
|
result[i], err = os.Stat(paths[i])
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func DecodeFileName(s string) string {
|
func DecodeFileName(s string) string {
|
||||||
s2, _ := decodeFromBase32(strings.Split(s, ".")[0])
|
s2, _ := decodeFromBase32(strings.Split(s, ".")[0])
|
||||||
return s2
|
return s2
|
||||||
|
@ -202,18 +202,29 @@
|
|||||||
|
|
||||||
{{ if .DirectoryPage }}
|
{{ if .DirectoryPage }}
|
||||||
<table style="width:100%">
|
<table style="width:100%">
|
||||||
|
{{ $upload := .UploadPage }}
|
||||||
<tr>
|
<tr>
|
||||||
<th>Document</th>
|
<th>Document</th>
|
||||||
<th>Current size</th>
|
<th>Current size</th>
|
||||||
|
{{ if not $upload }}
|
||||||
<th>Num Edits</th>
|
<th>Num Edits</th>
|
||||||
|
{{ end }}
|
||||||
<th>Last Edited</th>
|
<th>Last Edited</th>
|
||||||
</tr>
|
</tr>
|
||||||
{{range .DirectoryEntries}}
|
{{range .DirectoryEntries}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/{{ .Name }}/view">{{ .Name }}</a></td>
|
<td>
|
||||||
<td>{{.Length}}</td>
|
{{ if $upload }}
|
||||||
|
<a href="/uploads/{{ .Name }}">{{ sniffContentType .Name }}</a>
|
||||||
|
{{ else }}
|
||||||
|
<a href="/{{ .Name }}/view">{{ .Name }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</td>
|
||||||
|
<td>{{.Size}}</td>
|
||||||
|
{{ if not $upload }}
|
||||||
<td>{{.Numchanges}}</td>
|
<td>{{.Numchanges}}</td>
|
||||||
<td>{{.LastEditTime}}</td>
|
{{ end }}
|
||||||
|
<td>{{.ModTime.Format "Mon Jan 2 15:04:05 MST 2006" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</table>
|
</table>
|
||||||
|
21
utils.go
21
utils.go
@ -6,7 +6,9 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -92,6 +94,25 @@ func contentType(filename string) string {
|
|||||||
return "text/html"
|
return "text/html"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sniffContentType(name string) (string, error) {
|
||||||
|
file, err := os.Open(path.Join(pathToData, name))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Only the first 512 bytes are used to sniff the content type.
|
||||||
|
buffer := make([]byte, 512)
|
||||||
|
_, err = file.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always returns a valid content-type and "application/octet-stream" if no others seemed to match.
|
||||||
|
return http.DetectContentType(buffer), nil
|
||||||
|
}
|
||||||
|
|
||||||
func timeTrack(start time.Time, name string) {
|
func timeTrack(start time.Time, name string) {
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
log.Debug("%s took %s", name, elapsed)
|
log.Debug("%s took %s", name, elapsed)
|
||||||
|
Loading…
Reference in New Issue
Block a user