ready for v1.0rc1
This commit is contained in:
parent
84d48a6eea
commit
ce5764840f
15 changed files with 140 additions and 49 deletions
16
Makefile
16
Makefile
|
|
@ -4,23 +4,29 @@ VERSION=$(shell cat VERSION)
|
||||||
GCFLAGS=-gcflags '-e'
|
GCFLAGS=-gcflags '-e'
|
||||||
LDFLAGS=-ldflags '-X main.version=$(VERSION) -s -w'
|
LDFLAGS=-ldflags '-X main.version=$(VERSION) -s -w'
|
||||||
PROGRAM=docstore
|
PROGRAM=docstore
|
||||||
ENV=CGO_ENABLED=0
|
ENV=CGO_ENABLED=0 GO111MODULE=on
|
||||||
|
|
||||||
all: $(PROGRAM)
|
all: $(PROGRAM)
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
$(CC) get github.com/client9/misspell
|
$(CC) get github.com/client9/misspell
|
||||||
|
|
||||||
vendor: clean codeqa
|
release: clean generate gofmt codeqa
|
||||||
|
$(ENV) $(CC) $(BUILD) $(LDFLAGS)
|
||||||
|
|
||||||
|
release-vendor: clean generate gofmt codeqa
|
||||||
$(ENV) $(CC) $(BUILD) -mod=vendor $(LDFLAGS)
|
$(ENV) $(CC) $(BUILD) -mod=vendor $(LDFLAGS)
|
||||||
|
|
||||||
$(PROGRAM): clean codeqa
|
vendor: clean generate gofmt
|
||||||
|
$(ENV) $(CC) $(BUILD) -mod=vendor $(LDFLAGS)
|
||||||
|
|
||||||
|
$(PROGRAM): clean generate gofmt
|
||||||
$(ENV) $(CC) $(BUILD) $(LDFLAGS)
|
$(ENV) $(CC) $(BUILD) $(LDFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(CC) clean -x
|
$(CC) clean -x
|
||||||
|
|
||||||
codeqa: generate gofmt misspell golint test
|
codeqa: misspell golint test
|
||||||
|
|
||||||
generate:
|
generate:
|
||||||
$(CC) generate
|
$(CC) generate
|
||||||
|
|
@ -45,4 +51,4 @@ else
|
||||||
$(info skipping tests of other platforms)
|
$(info skipping tests of other platforms)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: setup vendor build clean codeqa generate gofmt golint misspell test
|
.PHONY: setup release release-vendor vendor build clean codeqa generate gofmt golint misspell test
|
||||||
|
|
|
||||||
28
README.md
28
README.md
|
|
@ -25,3 +25,31 @@ GOOS=openbsd make vendor
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
See the `scripts/` directory on how to install docstore on OpenBSD.
|
See the `scripts/` directory on how to install docstore on OpenBSD.
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"run_dir": "/var/lib/docstore", // running directory. data will be stored here
|
||||||
|
"user": "docstore", // drop privileges to user
|
||||||
|
"group": "docstore", // drop privileges to group
|
||||||
|
"listen_addr": "127.0.0.1:8080", // listening <address>:<port>
|
||||||
|
"log_file": "docstore.log", // log file (use - for stdout, this only works in combination with 'foreground: true')
|
||||||
|
"secret": "", // static hmac secret (at least 64 chars, used for JWT signing)
|
||||||
|
"langs": ["deu", "eng"], // languages for tesseract ocr
|
||||||
|
"cmd_timeout": 600, // timeout in seconds for executed teseract and pdftotext commands
|
||||||
|
"tesseract_threads": 1, // tesseract thread limit
|
||||||
|
"tesseract_oem": 1, // tesseract oem value
|
||||||
|
"foreground": false, // do not fork into the background
|
||||||
|
"secure_cookie": false, // enable secure cookie flag
|
||||||
|
"debug": false // enable debugging
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
DocStore manages all files and directories under the `$run_dir/data` directory.
|
||||||
|
|
||||||
|
Do not delete or move files around in the `$run_dir/data` directory using the filesystem as this messes with the index mapping.
|
||||||
|
|
||||||
|
You can however add more files and directories using the underlying file system.
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
//go:generate ./templates.sh
|
//go:generate ./templates.sh
|
||||||
|
|
||||||
|
// +build go1.12
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "git.giftfish.de/ston1th/docstore/pkg/cmd"
|
import "git.giftfish.de/ston1th/docstore/pkg/cmd"
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,9 @@ const (
|
||||||
defRunGroup = "docstore"
|
defRunGroup = "docstore"
|
||||||
|
|
||||||
defTimeout = 600
|
defTimeout = 600
|
||||||
|
|
||||||
tesseractThreads = 1
|
tesseractThreads = 1
|
||||||
|
tesseractOEM = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -76,7 +78,7 @@ func initServer(cfg core.Config) (err error) {
|
||||||
debugCfg.Secret = "***hidden***"
|
debugCfg.Secret = "***hidden***"
|
||||||
log.Debugf("docstore config: %+v", debugCfg)
|
log.Debugf("docstore config: %+v", debugCfg)
|
||||||
|
|
||||||
scanner, err := scan.New(cfg.DataDir, cfg.Langs, cfg.TesseractThreads, time.Second*time.Duration(cfg.CMDTimeout))
|
scanner, err := scan.New(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("scanner: " + err.Error())
|
return errors.New("scanner: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -265,6 +267,12 @@ func serverFlags() []cli.Flag {
|
||||||
Usage: "tesseract thread limit (1-4)",
|
Usage: "tesseract thread limit (1-4)",
|
||||||
Destination: &config.TesseractThreads,
|
Destination: &config.TesseractThreads,
|
||||||
},
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "oem",
|
||||||
|
Value: tesseractOEM,
|
||||||
|
Usage: "tesseract oem value (1-4)",
|
||||||
|
Destination: &config.TesseractOEM,
|
||||||
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "foreground, f",
|
Name: "foreground, f",
|
||||||
Usage: "do not fork into the background",
|
Usage: "do not fork into the background",
|
||||||
|
|
@ -272,7 +280,7 @@ func serverFlags() []cli.Flag {
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "secure, s",
|
Name: "secure, s",
|
||||||
Usage: "enable secure cookie",
|
Usage: "enable secure cookie flag",
|
||||||
Destination: &config.SecureCookie,
|
Destination: &config.SecureCookie,
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ type Config struct {
|
||||||
Langs []string `json:"langs,omitempty"`
|
Langs []string `json:"langs,omitempty"`
|
||||||
CMDTimeout int `json:"cmd_timeout,omitempty"`
|
CMDTimeout int `json:"cmd_timeout,omitempty"`
|
||||||
TesseractThreads int `json:"tesseract_threads,omitempty"`
|
TesseractThreads int `json:"tesseract_threads,omitempty"`
|
||||||
|
TesseractOEM int `json:"tesseract_oem,omitempty"`
|
||||||
Foreground bool `json:"foreground,omitempty"`
|
Foreground bool `json:"foreground,omitempty"`
|
||||||
SecureCookie bool `json:"secure_cookie,omitempty"`
|
SecureCookie bool `json:"secure_cookie,omitempty"`
|
||||||
Debug bool `json:"debug,omitempty"`
|
Debug bool `json:"debug,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mode int
|
type Mode int
|
||||||
|
|
@ -34,10 +35,11 @@ func (fs *Filesystem) GetFile(path string) (f *os.File, fi os.FileInfo, err erro
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Mode(path string) (mode Mode, err error) {
|
func (fs *Filesystem) Mode(path string) (mode Mode, enoent bool, err error) {
|
||||||
p := filepath.Join(fs.Base, Clean(path))
|
p := filepath.Join(fs.Base, Clean(path))
|
||||||
fi, err := os.Stat(p)
|
fi, err := os.Stat(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
enoent = err.(*os.PathError).Err == syscall.ENOENT
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m := fi.Mode()
|
m := fi.Mode()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright (C) 2019 Marius Schellenberger
|
|
||||||
|
|
||||||
// +build !openbsd
|
|
||||||
|
|
||||||
package scan
|
|
||||||
|
|
||||||
const tesseract_oem = "2"
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright (C) 2019 Marius Schellenberger
|
|
||||||
|
|
||||||
// +build openbsd
|
|
||||||
|
|
||||||
package scan
|
|
||||||
|
|
||||||
const tesseract_oem = "1"
|
|
||||||
|
|
@ -50,7 +50,7 @@ type Scanner struct {
|
||||||
p PDF
|
p PDF
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(base string, langs []string, threads int, timeout time.Duration) (s *Scanner, err error) {
|
func New(c core.Config) (s *Scanner, err error) {
|
||||||
var (
|
var (
|
||||||
tcmd string
|
tcmd string
|
||||||
pcmd string
|
pcmd string
|
||||||
|
|
@ -72,24 +72,27 @@ func New(base string, langs []string, threads int, timeout time.Duration) (s *Sc
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if langs == nil {
|
llen := len(c.Langs)
|
||||||
|
langs := c.Langs
|
||||||
|
if langs == nil || llen == 0 {
|
||||||
langs = defaultLangs
|
langs = defaultLangs
|
||||||
}
|
}
|
||||||
for i, l := range langs {
|
for i, l := range langs {
|
||||||
lang += l
|
lang += l
|
||||||
if i < len(langs)-1 {
|
if i < llen-1 {
|
||||||
lang += "+"
|
lang += "+"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
threads := c.TesseractThreads
|
||||||
if threads > maxThreads {
|
if threads > maxThreads {
|
||||||
threads = maxThreads
|
threads = maxThreads
|
||||||
} else if threads < minThreads {
|
} else if threads < minThreads {
|
||||||
threads = minThreads
|
threads = minThreads
|
||||||
}
|
}
|
||||||
args := []string{"-l", lang, "--oem", tesseract_oem}
|
args := []string{"-l", lang, "--oem", fmt.Sprintf("%d", c.TesseractOEM)}
|
||||||
s = &Scanner{
|
s = &Scanner{
|
||||||
base,
|
c.DataDir,
|
||||||
timeout,
|
time.Second * time.Duration(c.CMDTimeout),
|
||||||
Tesseract{tcmd, args, []string{fmt.Sprintf("OMP_THREAD_LIMIT=%d", threads)}},
|
Tesseract{tcmd, args, []string{fmt.Sprintf("OMP_THREAD_LIMIT=%d", threads)}},
|
||||||
PDF{pcmd, []string{"-layout", "-nopgbrk", "-eol", "unix"}},
|
PDF{pcmd, []string{"-layout", "-nopgbrk", "-eol", "unix"}},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ type webData struct {
|
||||||
|
|
||||||
Login bool
|
Login bool
|
||||||
|
|
||||||
Key string
|
NotFound string
|
||||||
|
|
||||||
User string
|
User string
|
||||||
Token string
|
Token string
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,7 @@ func newDirHandler(ctx *Context) {
|
||||||
|
|
||||||
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)
|
mode, _, err := ctx.Srv.FS.Mode(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
|
|
@ -323,11 +323,12 @@ func moveHandler(ctx *Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, m := range moved {
|
for _, m := range moved {
|
||||||
log.Debugf("move: %s to %s", m.Old, m.New)
|
|
||||||
err = ctx.Srv.DB.MoveFile(m.Old, m.New)
|
err = ctx.Srv.DB.MoveFile(m.Old, m.New)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("move: %s: %s", m, err)
|
log.Printf("move: %s: %s", m, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
log.Debugf("move: %s to %s", m.Old, m.New)
|
||||||
}
|
}
|
||||||
ctx.Redirect(newfile, http.StatusFound)
|
ctx.Redirect(newfile, http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
@ -335,8 +336,8 @@ func moveHandler(ctx *Context) {
|
||||||
|
|
||||||
func deleteHandler(ctx *Context) {
|
func deleteHandler(ctx *Context) {
|
||||||
path := strings.TrimPrefix(ctx.Path(), core.DeletePrefix)
|
path := strings.TrimPrefix(ctx.Path(), core.DeletePrefix)
|
||||||
mode, err := ctx.Srv.FS.Mode(path)
|
mode, enoent, err := ctx.Srv.FS.Mode(path)
|
||||||
if err != nil {
|
if !enoent && err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -352,6 +353,7 @@ func deleteHandler(ctx *Context) {
|
||||||
BodyTitle: "Delete Directory",
|
BodyTitle: "Delete Directory",
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
if ctx.Method() == "GET" {
|
||||||
ctx.Data = webData{
|
ctx.Data = webData{
|
||||||
Title: "Delete",
|
Title: "Delete",
|
||||||
BodyTitle: "Delete",
|
BodyTitle: "Delete",
|
||||||
|
|
@ -359,6 +361,7 @@ func deleteHandler(ctx *Context) {
|
||||||
ctx.Error(noSuchFile)
|
ctx.Error(noSuchFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ctx.Data.Path = path
|
ctx.Data.Path = path
|
||||||
ctx.Data.Data = mode
|
ctx.Data.Data = mode
|
||||||
ctx.Template("deleteHandler")
|
ctx.Template("deleteHandler")
|
||||||
|
|
@ -369,6 +372,27 @@ func deleteHandler(ctx *Context) {
|
||||||
if !ctx.CheckXsrf() {
|
if !ctx.CheckXsrf() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
p, _ := filepath.Split(path)
|
||||||
|
p = filepath.Dir(p)
|
||||||
|
clean := ctx.Form("clean")
|
||||||
|
if clean == "true" {
|
||||||
|
f := fs.Clean(path)
|
||||||
|
id, err := ctx.Srv.DB.DeleteFile(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("db: delete: %s %s", f, err)
|
||||||
|
}
|
||||||
|
if id == "" {
|
||||||
|
ctx.Redirect(p, http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("delete %s id: %s from index", f, id)
|
||||||
|
err = ctx.Srv.DB.Index.Delete(id)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("index: delete: %s %s", f, err)
|
||||||
|
}
|
||||||
|
ctx.Redirect(p, http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
files, err := ctx.Srv.FS.Delete(path)
|
files, err := ctx.Srv.FS.Delete(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
|
|
@ -379,17 +403,15 @@ func deleteHandler(ctx *Context) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("db: delete: %s %s", f, err)
|
log.Printf("db: delete: %s %s", f, err)
|
||||||
}
|
}
|
||||||
log.Debugf("delete %s id: %s from index", f, id)
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
log.Debugf("delete %s id: %s from index", f, id)
|
||||||
err = ctx.Srv.DB.Index.Delete(id)
|
err = ctx.Srv.DB.Index.Delete(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("index: delete: %s %s", f, err)
|
log.Printf("index: delete: %s %s", f, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p, _ := filepath.Split(path)
|
|
||||||
p = filepath.Dir(p)
|
|
||||||
ctx.Redirect(p, http.StatusFound)
|
ctx.Redirect(p, http.StatusFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -399,8 +421,8 @@ func dirHandler(ctx *Context) {
|
||||||
path := ctx.Path()
|
path := ctx.Path()
|
||||||
switch ctx.Method() {
|
switch ctx.Method() {
|
||||||
case "GET":
|
case "GET":
|
||||||
mode, err := ctx.Srv.FS.Mode(path)
|
mode, enoent, err := ctx.Srv.FS.Mode(path)
|
||||||
if err != nil {
|
if !enoent && err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -429,10 +451,17 @@ func dirHandler(ctx *Context) {
|
||||||
ctx.Data.Paths = fs.Paths(path)
|
ctx.Data.Paths = fs.Paths(path)
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
default:
|
default:
|
||||||
|
p := fs.Clean(path)
|
||||||
|
if enoent && ctx.Srv.DB.IsIndexed(p) {
|
||||||
|
ctx.Template("enoentHandler")
|
||||||
ctx.Data = webData{
|
ctx.Data = webData{
|
||||||
Title: "Viewer",
|
Title: "Stalled File",
|
||||||
}
|
}
|
||||||
ctx.Error(noSuchFile)
|
ctx.Data.Path = p
|
||||||
|
ctx.Exec()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,18 @@ const (
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}`
|
||||||
|
enoent = `{{define "body"}}
|
||||||
|
<div class="alert alert-danger msg">
|
||||||
|
<h4>File missing!</h4>
|
||||||
|
<p>This file is indexed but has been removed from the file system.</p>
|
||||||
|
<p>Do you want to delete this entry from the index?</p>
|
||||||
|
</div>
|
||||||
|
<form class="form-horizontal" action="/delete{{.Path}}" method="post">
|
||||||
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
|
<input type="hidden" name="clean" value="true">
|
||||||
|
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||||
|
</form>
|
||||||
{{end}}`
|
{{end}}`
|
||||||
file = `{{define "body"}}
|
file = `{{define "body"}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -427,7 +439,7 @@ const (
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="name">Name</label>
|
<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}}">
|
<input class="form-control input-sm" type="text" id="name" name="name" autocomplete="off" value="{{.Data}}" placeholder="optional">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
|
|
@ -721,6 +733,7 @@ func (s *HTTPServer) loadTemplates() {
|
||||||
s.templ["uploadHandler"] = parse(index, menu, upload)
|
s.templ["uploadHandler"] = parse(index, menu, upload)
|
||||||
s.templ["newDirHandler"] = parse(index, menu, newDir)
|
s.templ["newDirHandler"] = parse(index, menu, newDir)
|
||||||
s.templ["moveHandler"] = parse(index, menu, move)
|
s.templ["moveHandler"] = parse(index, menu, move)
|
||||||
|
s.templ["enoentHandler"] = parse(index, menu, enoent)
|
||||||
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)
|
||||||
s.templ["forbiddenHandler"] = parse(index, menu, forbidden)
|
s.templ["forbiddenHandler"] = parse(index, menu, forbidden)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ func (s *HTTPServer) loadTemplates() {
|
||||||
s.templ["uploadHandler"] = parse(index, menu, upload)
|
s.templ["uploadHandler"] = parse(index, menu, upload)
|
||||||
s.templ["newDirHandler"] = parse(index, menu, newDir)
|
s.templ["newDirHandler"] = parse(index, menu, newDir)
|
||||||
s.templ["moveHandler"] = parse(index, menu, move)
|
s.templ["moveHandler"] = parse(index, menu, move)
|
||||||
|
s.templ["enoentHandler"] = parse(index, menu, enoent)
|
||||||
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)
|
||||||
s.templ["forbiddenHandler"] = parse(index, menu, forbidden)
|
s.templ["forbiddenHandler"] = parse(index, menu, forbidden)
|
||||||
|
|
|
||||||
12
templates/enoent.html
Normal file
12
templates/enoent.html
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{{define "body"}}
|
||||||
|
<div class="alert alert-danger msg">
|
||||||
|
<h4>File missing!</h4>
|
||||||
|
<p>This file is indexed but has been removed from the file system.</p>
|
||||||
|
<p>Do you want to delete this entry from the index?</p>
|
||||||
|
</div>
|
||||||
|
<form class="form-horizontal" action="/delete{{.Path}}" method="post">
|
||||||
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
|
<input type="hidden" name="clean" value="true">
|
||||||
|
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-form-label" for="name">Name</label>
|
<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}}">
|
<input class="form-control input-sm" type="text" id="name" name="name" autocomplete="off" value="{{.Data}}" placeholder="optional">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue