basic functions working
This commit is contained in:
parent
abc6159044
commit
ffeeae5702
22 changed files with 420 additions and 129 deletions
|
|
@ -10,6 +10,8 @@ var (
|
||||||
|
|
||||||
RawPrefix = "/raw"
|
RawPrefix = "/raw"
|
||||||
IndexPrefix = "/index"
|
IndexPrefix = "/index"
|
||||||
|
UploadPrefix = "/upload"
|
||||||
|
NewPrefix = "/new"
|
||||||
MovePrefix = "/move"
|
MovePrefix = "/move"
|
||||||
DeletePrefix = "/delete"
|
DeletePrefix = "/delete"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type Result struct {
|
||||||
type Path struct {
|
type Path struct {
|
||||||
Name string
|
Name string
|
||||||
Abs string
|
Abs string
|
||||||
Indexed bool
|
Flag bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Paths []Path
|
type Paths []Path
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ func (db *DB) GetPaths(res []core.Result) error {
|
||||||
|
|
||||||
func (db *DB) GetIndexed(p core.Paths) {
|
func (db *DB) GetIndexed(p core.Paths) {
|
||||||
for i, v := range p {
|
for i, v := range p {
|
||||||
p[i].Indexed = db.IsIndexed(v.Abs)
|
p[i].Flag = db.IsIndexed(v.Abs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,22 @@ package fs
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Mode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Undefined Mode = iota
|
||||||
|
IsDir
|
||||||
|
IsFile
|
||||||
|
)
|
||||||
|
|
||||||
func (fs *Filesystem) GetFile(path string) (f *os.File, fi os.FileInfo, err error) {
|
func (fs *Filesystem) GetFile(path string) (f *os.File, fi os.FileInfo, err error) {
|
||||||
path = strings.TrimPrefix(filepath.Clean(path), core.RawPrefix)
|
path = strings.TrimPrefix(Clean(path), core.RawPrefix)
|
||||||
p := filepath.Join(fs.Base, path)
|
p := filepath.Join(fs.Base, path)
|
||||||
f, err = os.Open(p)
|
f, err = os.Open(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -25,8 +34,8 @@ func (fs *Filesystem) GetFile(path string) (f *os.File, fi os.FileInfo, err erro
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Mode(path string) (mode FileMode, err error) {
|
func (fs *Filesystem) Mode(path string) (mode Mode, err error) {
|
||||||
path = filepath.Clean(path)
|
path = Clean(path)
|
||||||
p := filepath.Join(fs.Base, path)
|
p := filepath.Join(fs.Base, path)
|
||||||
fi, err := os.Stat(p)
|
fi, err := os.Stat(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -41,17 +50,46 @@ func (fs *Filesystem) Mode(path string) (mode FileMode, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *Filesystem) NewFile(r io.Reader, path, orig, name string) (newfile string, err error) {
|
||||||
|
path = Clean(path)
|
||||||
|
orig = Clean(orig)
|
||||||
|
_, orig = filepath.Split(orig)
|
||||||
|
name = Clean(name)
|
||||||
|
_, name = filepath.Split(name)
|
||||||
|
if name == "" {
|
||||||
|
name = orig
|
||||||
|
}
|
||||||
|
newfile = filepath.Join(path, name)
|
||||||
|
f, err := os.OpenFile(filepath.Join(fs.Base, newfile), os.O_RDWR|os.O_CREATE, FileMode)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = io.Copy(f, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *Filesystem) Mkdir(path, name string) error {
|
||||||
|
path = Clean(path)
|
||||||
|
name = Clean(name)
|
||||||
|
return os.Mkdir(filepath.Join(fs.Base, path, name), DirMode)
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Move(path, dest, name string) (string, error) {
|
func (fs *Filesystem) Move(path, dest, name string) (string, error) {
|
||||||
path = filepath.Clean(path)
|
path = Clean(path)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
_, name = filepath.Split(path)
|
_, name = filepath.Split(path)
|
||||||
}
|
}
|
||||||
newfile := filepath.Clean(filepath.Join(dest, name))
|
fullpath := filepath.Join(fs.Base, path)
|
||||||
return newfile, os.Rename(filepath.Join(fs.Base, path), filepath.Join(fs.Base, newfile))
|
if fullpath == fs.Base {
|
||||||
|
return "", errors.New("the root path can not be moved")
|
||||||
|
}
|
||||||
|
newfile := Clean(filepath.Join(dest, name))
|
||||||
|
return newfile, os.Rename(fullpath, filepath.Join(fs.Base, newfile))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Delete(path string) (removed []string, err error) {
|
func (fs *Filesystem) Delete(path string) (removed []string, err error) {
|
||||||
path = filepath.Clean(path)
|
path = Clean(path)
|
||||||
fullpath := filepath.Join(fs.Base, path)
|
fullpath := filepath.Join(fs.Base, path)
|
||||||
if fullpath == fs.Base {
|
if fullpath == fs.Base {
|
||||||
return nil, errors.New("the root path can not be deleted")
|
return nil, errors.New("the root path can not be deleted")
|
||||||
|
|
|
||||||
26
pkg/fs/fs.go
26
pkg/fs/fs.go
|
|
@ -9,12 +9,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileMode int
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Undefined FileMode = iota
|
DirMode = 0750
|
||||||
IsDir
|
FileMode = 0640
|
||||||
IsFile
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Directory represents a directory and its contents
|
// Directory represents a directory and its contents
|
||||||
|
|
@ -40,7 +37,7 @@ func NewFilesystem(base string) (fs *Filesystem, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) ReadDir(path string) (d *Directory) {
|
func (fs *Filesystem) ReadDir(path string) (d *Directory) {
|
||||||
path = filepath.Clean(path)
|
path = Clean(path)
|
||||||
combined := filepath.Join(fs.Base, path)
|
combined := filepath.Join(fs.Base, path)
|
||||||
f, err := os.Open(combined)
|
f, err := os.Open(combined)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -70,7 +67,12 @@ func (fs *Filesystem) ReadDir(path string) (d *Directory) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Recursive() (p core.Paths) {
|
func (fs *Filesystem) Recursive(p string) (paths core.Paths) {
|
||||||
|
p = Clean(p)
|
||||||
|
p, _ = filepath.Split(p)
|
||||||
|
if len(p) >= 2 {
|
||||||
|
p = p[:len(p)-1]
|
||||||
|
}
|
||||||
filepath.Walk(fs.Base, func(path string, info os.FileInfo, err error) error {
|
filepath.Walk(fs.Base, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -80,16 +82,16 @@ func (fs *Filesystem) Recursive() (p core.Paths) {
|
||||||
if a == "" {
|
if a == "" {
|
||||||
a = "/"
|
a = "/"
|
||||||
}
|
}
|
||||||
p = append(p, core.Path{Abs: a})
|
paths = append(paths, core.Path{Abs: a, Flag: a == p})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
sort.Sort(p)
|
sort.Sort(paths)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) RecursiveFiles(p string) (files []string) {
|
func (fs *Filesystem) RecursiveFiles(p string) (files []string) {
|
||||||
p = filepath.Clean(p)
|
p = Clean(p)
|
||||||
fullpath := filepath.Join(fs.Base, p)
|
fullpath := filepath.Join(fs.Base, p)
|
||||||
filepath.Walk(fullpath, func(path string, info os.FileInfo, err error) error {
|
filepath.Walk(fullpath, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -104,7 +106,7 @@ func (fs *Filesystem) RecursiveFiles(p string) (files []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Paths(path string) (p core.Paths) {
|
func Paths(path string) (p core.Paths) {
|
||||||
path = filepath.Clean(path)
|
path = Clean(path)
|
||||||
s := strings.Split(path, string(filepath.Separator))
|
s := strings.Split(path, string(filepath.Separator))
|
||||||
for i, v := range s[1:] {
|
for i, v := range s[1:] {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
|
|
@ -117,7 +119,7 @@ func Paths(path string) (p core.Paths) {
|
||||||
p = append(p, core.Path{Name: v, Abs: filepath.Join(p[i-1].Abs, v)})
|
p = append(p, core.Path{Name: v, Abs: filepath.Join(p[i-1].Abs, v)})
|
||||||
}
|
}
|
||||||
if len(p) != 0 {
|
if len(p) != 0 {
|
||||||
p[len(p)-1].Indexed = true
|
p[len(p)-1].Flag = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
pkg/fs/helper.go
Normal file
9
pkg/fs/helper.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
const sep = "/"
|
||||||
|
|
||||||
|
func Clean(path string) string {
|
||||||
|
return filepath.FromSlash(filepath.Clean(sep + path))
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||||
|
"git.giftfish.de/ston1th/docstore/pkg/fs"
|
||||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||||
"git.giftfish.de/ston1th/godrop/v2"
|
"git.giftfish.de/ston1th/godrop/v2"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -72,7 +73,7 @@ func New(base string, langs []string, timeout time.Duration) (s *Scanner, err er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
||||||
file = filepath.Clean(file)
|
file = fs.Clean(file)
|
||||||
scanfile := filepath.Join(s.base, file)
|
scanfile := filepath.Join(s.base, file)
|
||||||
var pdffile string
|
var pdffile string
|
||||||
pdf := isPDF(scanfile)
|
pdf := isPDF(scanfile)
|
||||||
|
|
@ -91,7 +92,7 @@ func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pdffile = filepath.Join(s.base, filename)
|
pdffile = filepath.Join(s.base, filename)
|
||||||
f, err = os.OpenFile(pdffile, os.O_RDWR|os.O_CREATE, 0640)
|
f, err = os.OpenFile(pdffile, os.O_RDWR|os.O_CREATE, fs.FileMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"git.giftfish.de/ston1th/jwt/v3"
|
"git.giftfish.de/ston1th/jwt/v3"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -27,7 +28,7 @@ func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) *Context
|
||||||
h.Set("X-Frame-Options", "sameorigin")
|
h.Set("X-Frame-Options", "sameorigin")
|
||||||
h.Set("X-Content-Type-Options", "nosniff")
|
h.Set("X-Content-Type-Options", "nosniff")
|
||||||
h.Set("X-XSS-Protection", "1; mode=block")
|
h.Set("X-XSS-Protection", "1; mode=block")
|
||||||
h.Set("Content-Security-Policy", "default-src 'none';object-src 'self';frame-src 'self';style-src 'self';img-src 'self' https: data:;connect-src 'self';frame-ancestors 'self'")
|
h.Set("Content-Security-Policy", "default-src 'none';object-src 'self';frame-src 'self';style-src 'self';img-src 'self' data:;connect-src 'self';frame-ancestors 'self'")
|
||||||
h.Set("Referrer-Policy", "same-origin")
|
h.Set("Referrer-Policy", "same-origin")
|
||||||
return &Context{
|
return &Context{
|
||||||
Request: r,
|
Request: r,
|
||||||
|
|
@ -202,6 +203,15 @@ func (c *Context) Var(name string) (ret string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) File(name string) (io.Reader, string, error) {
|
||||||
|
var filename string
|
||||||
|
f, fh, err := c.Request.FormFile(name)
|
||||||
|
if fh != nil {
|
||||||
|
filename = fh.Filename
|
||||||
|
}
|
||||||
|
return f, filename, err
|
||||||
|
}
|
||||||
|
|
||||||
// CheckXsrf validates the xsrf token
|
// CheckXsrf validates the xsrf token
|
||||||
func (c *Context) CheckXsrf() (ok bool) {
|
func (c *Context) CheckXsrf() (ok bool) {
|
||||||
ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize])
|
ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize])
|
||||||
|
|
|
||||||
|
|
@ -216,18 +216,85 @@ func indexHandler(ctx *Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uploadHandler(ctx *Context) {
|
||||||
|
path := strings.TrimPrefix(ctx.Path(), core.UploadPrefix)
|
||||||
|
ctx.Data = webData{
|
||||||
|
Title: "Upload",
|
||||||
|
BodyTitle: "Upload",
|
||||||
|
}
|
||||||
|
ctx.Data.Path = path
|
||||||
|
ctx.Template("uploadHandler")
|
||||||
|
switch ctx.Method() {
|
||||||
|
case "GET":
|
||||||
|
ctx.Exec()
|
||||||
|
case "POST":
|
||||||
|
f, orig, err := ctx.File("file")
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newfile, err := ctx.Srv.FS.NewFile(f, path, orig, ctx.Form("name"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Redirect(newfile, http.StatusFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDirHandler(ctx *Context) {
|
||||||
|
path := strings.TrimPrefix(ctx.Path(), core.NewPrefix)
|
||||||
|
ctx.Data = webData{
|
||||||
|
Title: "Create Directory",
|
||||||
|
BodyTitle: "Create Directory",
|
||||||
|
}
|
||||||
|
ctx.Data.Path = path
|
||||||
|
ctx.Template("newDirHandler")
|
||||||
|
switch ctx.Method() {
|
||||||
|
case "GET":
|
||||||
|
ctx.Exec()
|
||||||
|
case "POST":
|
||||||
|
err := ctx.Srv.FS.Mkdir(path, ctx.Form("name"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Redirect(path, http.StatusFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func moveHandler(ctx *Context) {
|
func moveHandler(ctx *Context) {
|
||||||
path := strings.TrimPrefix(ctx.Path(), core.MovePrefix)
|
path := strings.TrimPrefix(ctx.Path(), core.MovePrefix)
|
||||||
|
mode, err := ctx.Srv.FS.Mode(path)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch mode {
|
||||||
|
case fs.IsFile:
|
||||||
ctx.Data = webData{
|
ctx.Data = webData{
|
||||||
Title: "Move File",
|
Title: "Move File",
|
||||||
BodyTitle: "Move File",
|
BodyTitle: "Move File",
|
||||||
}
|
}
|
||||||
|
case fs.IsDir:
|
||||||
|
ctx.Data = webData{
|
||||||
|
Title: "Move Directory",
|
||||||
|
BodyTitle: "Move Directory",
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ctx.Data = webData{
|
||||||
|
Title: "Move",
|
||||||
|
BodyTitle: "Move",
|
||||||
|
}
|
||||||
|
ctx.Error(noSuchFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data.Path = path
|
||||||
|
ctx.Data.Paths = ctx.Srv.FS.Recursive(path)
|
||||||
|
_, ctx.Data.Data = filepath.Split(path)
|
||||||
ctx.Template("moveHandler")
|
ctx.Template("moveHandler")
|
||||||
switch ctx.Method() {
|
switch ctx.Method() {
|
||||||
case "GET":
|
case "GET":
|
||||||
ctx.Data.Path = path
|
|
||||||
ctx.Data.Paths = ctx.Srv.FS.Recursive()
|
|
||||||
_, ctx.Data.Data = filepath.Split(path)
|
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
case "POST":
|
case "POST":
|
||||||
name := ctx.Form("name")
|
name := ctx.Form("name")
|
||||||
|
|
@ -320,6 +387,7 @@ func dirHandler(ctx *Context) {
|
||||||
}
|
}
|
||||||
dir := ctx.Srv.FS.ReadDir(path)
|
dir := ctx.Srv.FS.ReadDir(path)
|
||||||
ctx.Srv.DB.GetIndexed(dir.Files)
|
ctx.Srv.DB.GetIndexed(dir.Files)
|
||||||
|
ctx.Data.Path = path
|
||||||
ctx.Data.Data = dir
|
ctx.Data.Data = dir
|
||||||
ctx.Data.Paths = fs.Paths(path)
|
ctx.Data.Paths = fs.Paths(path)
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
|
|
@ -328,11 +396,14 @@ func dirHandler(ctx *Context) {
|
||||||
Title: "Document Viewer",
|
Title: "Document Viewer",
|
||||||
}
|
}
|
||||||
ctx.Template("fileHandler")
|
ctx.Template("fileHandler")
|
||||||
ctx.Data.Data = core.RawPrefix + path
|
ctx.Data.Data = core.Path{
|
||||||
|
Name: path,
|
||||||
|
Abs: core.RawPrefix + path,
|
||||||
|
Flag: ctx.Srv.DB.IsIndexed(path),
|
||||||
|
}
|
||||||
ctx.Data.Paths = fs.Paths(path)
|
ctx.Data.Paths = fs.Paths(path)
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
}
|
}
|
||||||
case "POST":
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,20 @@ var prefixRoutes = []route{
|
||||||
indexHandler)),
|
indexHandler)),
|
||||||
[]string{"GET", "POST"},
|
[]string{"GET", "POST"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
core.UploadPrefix,
|
||||||
|
jwtHandler(
|
||||||
|
authHandler(
|
||||||
|
uploadHandler)),
|
||||||
|
[]string{"GET", "POST"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
core.NewPrefix,
|
||||||
|
jwtHandler(
|
||||||
|
authHandler(
|
||||||
|
newDirHandler)),
|
||||||
|
[]string{"GET", "POST"},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
core.MovePrefix,
|
core.MovePrefix,
|
||||||
jwtHandler(
|
jwtHandler(
|
||||||
|
|
@ -69,7 +83,7 @@ var prefixRoutes = []route{
|
||||||
jwtHandler(
|
jwtHandler(
|
||||||
authHandler(
|
authHandler(
|
||||||
dirHandler)),
|
dirHandler)),
|
||||||
[]string{"GET", "POST", "PUT", "DELETE"},
|
[]string{"GET"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -82,6 +82,8 @@ func (s *HTTPServer) loadTemplates() {
|
||||||
|
|
||||||
s.templ["dirHandler"] = parse(index, menu, dir)
|
s.templ["dirHandler"] = parse(index, menu, dir)
|
||||||
s.templ["fileHandler"] = parse(index, menu, file)
|
s.templ["fileHandler"] = parse(index, menu, file)
|
||||||
|
s.templ["uploadHandler"] = parse(index, menu, upload)
|
||||||
|
s.templ["newDirHandler"] = parse(index, menu, newDir)
|
||||||
s.templ["moveHandler"] = parse(index, menu, move)
|
s.templ["moveHandler"] = parse(index, menu, move)
|
||||||
s.templ["deleteHandler"] = parse(index, menu, delete)
|
s.templ["deleteHandler"] = parse(index, menu, delete)
|
||||||
s.templ["notFoundHandler"] = parse(index, menu, notFound)
|
s.templ["notFoundHandler"] = parse(index, menu, notFound)
|
||||||
|
|
|
||||||
12
templates/bootstrap.css
vendored
12
templates/bootstrap.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -41,6 +41,9 @@ a.anchor {
|
||||||
top: -70px;
|
top: -70px;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
a.mono {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
.alert-primary {
|
.alert-primary {
|
||||||
background-color: #375a7f;
|
background-color: #375a7f;
|
||||||
border-color: #375a7f;
|
border-color: #375a7f;
|
||||||
|
|
@ -71,10 +74,6 @@ h5 {
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
.md-text {
|
|
||||||
font-family: 'Courier New';
|
|
||||||
font-size: 14;
|
|
||||||
}
|
|
||||||
ul.toc {
|
ul.toc {
|
||||||
list-style-type: disc;
|
list-style-type: disc;
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
|
|
@ -119,6 +118,12 @@ input.menu {
|
||||||
.menu-search-input {
|
.menu-search-input {
|
||||||
background-color: #2f2f2f !important;
|
background-color: #2f2f2f !important;
|
||||||
}
|
}
|
||||||
|
.custom-file-label {
|
||||||
|
background-color: #444 !important;
|
||||||
|
}
|
||||||
|
.custom-file-label::after {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
html, body, .container, .content {
|
html, body, .container, .content {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
@ -132,21 +137,18 @@ html, body, .container, .content {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
}
|
}
|
||||||
.wrapper {
|
|
||||||
min-height: 100%;
|
|
||||||
height: auto !important;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 auto -60px;
|
|
||||||
}
|
|
||||||
.push {
|
.push {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.footer-wrapper {
|
.relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 20px;
|
}
|
||||||
height: 50px;
|
.btn-breadcrumb {
|
||||||
color: #888888;
|
position: absolute;
|
||||||
|
right: 1rem;
|
||||||
|
top: 38%;
|
||||||
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ol class="breadcrumb full-width">
|
<div class="nav-container full-width relative">
|
||||||
|
<ol class="breadcrumb full-width">
|
||||||
<li class="breadcrumb-item"></li>
|
<li class="breadcrumb-item"></li>
|
||||||
<li class="breadcrumb-item"><a href="/">root</a></li>
|
<li class="breadcrumb-item"><a href="/">root</a></li>
|
||||||
{{range $item := .Paths}}
|
{{range $item := .Paths}}
|
||||||
<li class="breadcrumb-item{{if $item.Indexed}} active{{end}}">{{if $item.Indexed}}{{$item.Name}}{{else}}<a href="{{$item.Abs}}">{{$item.Name}}</a>{{end}}</li>
|
<li class="breadcrumb-item{{if $item.Flag}} active{{end}}">{{if $item.Flag}}{{$item.Name}}{{else}}<a href="{{$item.Abs}}">{{$item.Name}}</a>{{end}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ol>
|
</ol>
|
||||||
|
<div class="btn-group btn-breadcrumb">
|
||||||
|
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
|
||||||
|
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New Directory</a>
|
||||||
|
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
|
||||||
|
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<table class="table table-stripped table-hover">
|
<table class="table table-stripped table-hover">
|
||||||
|
|
@ -18,10 +26,10 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{if .Data.Dirs}}
|
{{if .Data}}
|
||||||
{{range $item := .Data.Dirs}}
|
{{range $item := .Data.Dirs}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{$item.Abs}}">{{$item.Name}}/</a></td>
|
<th><a href="{{$item.Abs}}">{{$item.Name}}/</a></th>
|
||||||
<td>Directory</td>
|
<td>Directory</td>
|
||||||
<td><div class="btn-group">
|
<td><div class="btn-group">
|
||||||
<a href="/move{{$item.Abs}}" class="btn btn-sm btn-primary">Move</a>
|
<a href="/move{{$item.Abs}}" class="btn btn-sm btn-primary">Move</a>
|
||||||
|
|
@ -29,15 +37,13 @@
|
||||||
</div></td>
|
</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
|
||||||
{{if .Data.Files}}
|
|
||||||
{{range $item := .Data.Files}}
|
{{range $item := .Data.Files}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{$item.Abs}}">{{$item.Name}}</a></td>
|
<th><a href="{{$item.Abs}}">{{$item.Name}}</a></th>
|
||||||
<td>File</td>
|
<td>File</td>
|
||||||
<td><div class="btn-group">
|
<td><div class="btn-group">
|
||||||
{{if not $item.Indexed}}
|
{{if not $item.Flag}}
|
||||||
<a href="/index{{$item.Abs}}" class="btn btn-sm btn-primary">Index</a>
|
<a href="/index{{$item.Abs}}" class="btn btn-sm btn-success">Index</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<a href="/move{{$item.Abs}}" class="btn btn-sm btn-primary">Move</a>
|
<a href="/move{{$item.Abs}}" class="btn btn-sm btn-primary">Move</a>
|
||||||
<a href="/delete{{$item.Abs}}" class="btn btn-sm btn-danger">Delete</a>
|
<a href="/delete{{$item.Abs}}" class="btn btn-sm btn-danger">Delete</a>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ol class="breadcrumb full-width">
|
<div class="nav-container full-width relative">
|
||||||
|
<ol class="breadcrumb full-width">
|
||||||
<li class="breadcrumb-item"></li>
|
<li class="breadcrumb-item"></li>
|
||||||
<li class="breadcrumb-item"><a href="/">root</a></li>
|
<li class="breadcrumb-item"><a href="/">root</a></li>
|
||||||
{{range $item := .Paths}}
|
{{range $item := .Paths}}
|
||||||
<li class="breadcrumb-item{{if $item.Indexed}} active{{end}}">{{if $item.Indexed}}{{$item.Name}}{{else}}<a href="{{$item.Abs}}">{{$item.Name}}</a>{{end}}</li>
|
<li class="breadcrumb-item{{if $item.Flag}} active{{end}}">{{if $item.Flag}}{{$item.Name}}{{else}}<a href="{{$item.Abs}}">{{$item.Name}}</a>{{end}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ol>
|
</ol>
|
||||||
|
<div class="btn-group btn-breadcrumb">
|
||||||
|
{{if not .Data.Flag}}
|
||||||
|
<a href="/index{{.Data.Name}}" class="btn btn-sm btn-success">Index</a>
|
||||||
|
{{end}}
|
||||||
|
<a href="/move{{.Data.Name}}" class="btn btn-sm btn-primary">Move</a>
|
||||||
|
<a href="/delete{{.Data.Name}}" class="btn btn-sm btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<embed src="{{.Data}}" width="100%" height="700px" toolbar="1" statusbar="1" navpanes="1"></embed>
|
<embed src="{{.Data.Abs}}" width="100%" height="700px" toolbar="1" statusbar="1" navpanes="1"></embed>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
<head>
|
<head>
|
||||||
<title>DocStore | {{.Title}}</title>
|
<title>DocStore | {{.Title}}</title>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
|
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-iYnSZSZLepCN7vabI6deUcbcw+1UNCmKhXlJ75O+S6Y="></link>
|
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-L50A9TBaaysKaYOQWW5+H5nHTNLFLH4+F7Au3o4enAM="></link>
|
||||||
<meta name="theme-color" content="#375a7f">
|
<meta name="theme-color" content="#375a7f">
|
||||||
<meta name="description" content="{{.Title}}">
|
<meta name="description" content="{{.Title}}">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
<div class="alert alert-danger msg">
|
<div class="alert alert-danger msg">
|
||||||
<h4>Error!</h4>
|
<h4>Error!</h4>
|
||||||
<p>{{.Data}}</p>
|
<p>{{.Data}}</p>
|
||||||
<a href="{{.Path}}" class="btn btn-sm btn-primary">Back</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<a href="{{.Path}}" class="btn btn-sm btn-primary">Back</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<label class="col-form-label" for="destination">Destination</label>
|
<label class="col-form-label" for="destination">Destination</label>
|
||||||
<select class="form-control input-sm" id="destination" name="destination" required>
|
<select class="form-control input-sm" id="destination" name="destination" required>
|
||||||
{{range $item := .Paths}}
|
{{range $item := .Paths}}
|
||||||
<option value="{{$item.Abs}}">{{$item.Abs}}</option>
|
<option value="{{$item.Abs}}"{{if $item.Flag}} selected{{end}}>{{$item.Abs}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
<input class="form-control input-sm" type="text" id="name" name="name" autocomplete="off" value="{{.Data}}">
|
<input class="form-control input-sm" type="text" id="name" name="name" autocomplete="off" value="{{.Data}}">
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Move</button>
|
<button class="btn btn-sm btn-primary" type="submit">Move</button>
|
||||||
|
<a href="{{.Path}}" class="btn btn-sm btn-primary">Back</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
22
templates/newDir.html
Normal file
22
templates/newDir.html
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{{define "body"}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-4 offset-4">
|
||||||
|
<div class="card border-primary mx-auto">
|
||||||
|
<div class="card-header">
|
||||||
|
<strong>{{.BodyTitle}}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Create new directory in <a href="{{.Path}}">{{.Path}}</a>.</p>
|
||||||
|
<form class="form-horizontal" action="/new{{.Path}}" method="post">
|
||||||
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-form-label" for="name">Name</label>
|
||||||
|
<input class="form-control input-sm" type="text" id="name" name="name" autocomplete="off" value="{{.Data}}">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-primary" type="submit">Create</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<div class="col">
|
<div class="col">
|
||||||
{{if .Data}}
|
{{if .Data}}
|
||||||
{{range $item := .Data}}
|
{{range $item := .Data}}
|
||||||
<a href="{{$item.Path}}"><b>{{$item.Path}}</b></a>
|
<a class="mono" href="{{$item.Path}}"><b>{{$item.Path}}</b></a>
|
||||||
<pre class="wrap">{{$item.HTML}}</pre><br>
|
<pre class="wrap">{{$item.HTML}}</pre><br>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
||||||
30
templates/upload.html
Normal file
30
templates/upload.html
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{{define "body"}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-4 offset-4">
|
||||||
|
<div class="card border-primary mx-auto">
|
||||||
|
<div class="card-header">
|
||||||
|
<strong>{{.BodyTitle}}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form class="form-horizontal" action="/upload{{.Path}}" method="post" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-form-label" for="name">Name</label>
|
||||||
|
<input class="form-control input-sm" type="text" id="name" name="name" autocomplete="off" value="{{.Data}}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="custom-file">
|
||||||
|
<input type="file" class="custom-file-input" id="file" name="file">
|
||||||
|
<label class="custom-file-label" for="file">Choose file</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-success" type="submit">Upload</button>
|
||||||
|
<a href="{{.Path}}" class="btn btn-sm btn-primary">Back</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue