mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 16:32:31 +01:00
feature (plg_handler_site): discoverability by admin
This commit is contained in:
parent
32526f8bde
commit
6f2404d69a
5 changed files with 192 additions and 9 deletions
|
|
@ -31,6 +31,7 @@ import (
|
|||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_editor_wopi"
|
||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_handler_console"
|
||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp"
|
||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_handler_site"
|
||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_image_ascii"
|
||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_image_c"
|
||||
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_license"
|
||||
|
|
|
|||
56
server/plugin/plg_handler_site/config.go
Normal file
56
server/plugin/plg_handler_site/config.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package plg_handler_site
|
||||
|
||||
import (
|
||||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Hooks.Register.Onload(func() {
|
||||
PluginEnable()
|
||||
PluginParamAutoindex()
|
||||
PluginParamCORSAllowOrigins()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
var PluginEnable = func() bool {
|
||||
return Config.Get("features.site.enable").Schema(func(f *FormElement) *FormElement {
|
||||
if f == nil {
|
||||
f = &FormElement{}
|
||||
}
|
||||
f.Name = "enable"
|
||||
f.Type = "enable"
|
||||
f.Target = []string{"site_autoindex", "site_cors_allow_origins"}
|
||||
f.Description = "Enable/Disable the creation of site via shared links. Sites will be made available under /public/{shareID}/"
|
||||
f.Default = false
|
||||
return f
|
||||
}).Bool()
|
||||
}
|
||||
|
||||
var PluginParamAutoindex = func() bool {
|
||||
return Config.Get("features.site.autoindex").Schema(func(f *FormElement) *FormElement {
|
||||
if f == nil {
|
||||
f = &FormElement{}
|
||||
}
|
||||
f.Id = "site_autoindex"
|
||||
f.Name = "autoindex"
|
||||
f.Type = "boolean"
|
||||
f.Description = "Enables or disables automatic directory listing when no index file is present."
|
||||
f.Default = false
|
||||
return f
|
||||
}).Bool()
|
||||
}
|
||||
|
||||
var PluginParamCORSAllowOrigins = func() string {
|
||||
return Config.Get("features.site.cors_allow_origins").Schema(func(f *FormElement) *FormElement {
|
||||
if f == nil {
|
||||
f = &FormElement{}
|
||||
}
|
||||
f.Id = "site_cors_allow_origins"
|
||||
f.Name = "cors_allow_origins"
|
||||
f.Type = "text"
|
||||
f.Placeholder = "* or https://example.com, https://app.example.com"
|
||||
f.Description = "List of allowed origins for CORS. Use '*' to allow all origins, or provide a comma-separated list."
|
||||
return f
|
||||
}).String()
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package plg_handler_site
|
|||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
|
|
@ -15,17 +16,29 @@ import (
|
|||
|
||||
func init() {
|
||||
Hooks.Register.HttpEndpoint(func(r *mux.Router, app *App) error {
|
||||
if PluginEnable() == false {
|
||||
return nil
|
||||
}
|
||||
r.PathPrefix("/public/{share}/").HandlerFunc(NewMiddlewareChain(
|
||||
publicHandler,
|
||||
[]Middleware{SessionStart, SecureHeaders},
|
||||
SiteHandler,
|
||||
[]Middleware{SessionStart, SecureHeaders, cors},
|
||||
*app,
|
||||
)).Methods("GET", "HEAD")
|
||||
|
||||
r.HandleFunc("/public/", NewMiddlewareChain(
|
||||
SharesListHandler,
|
||||
[]Middleware{SecureHeaders, basicAdmin},
|
||||
*app,
|
||||
)).Methods("GET")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func publicHandler(app *App, w http.ResponseWriter, r *http.Request) {
|
||||
if app.Backend == nil {
|
||||
func SiteHandler(app *App, w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
} else if app.Backend == nil {
|
||||
SendErrorResult(w, ErrNotFound)
|
||||
return
|
||||
} else if model.CanRead(app) == false {
|
||||
|
|
@ -47,12 +60,53 @@ func publicHandler(app *App, w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
f, err := app.Backend.Cat(path)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if f, err := app.Backend.Cat(path); err == nil {
|
||||
w.Header().Set("Content-Type", GetMimeType(path))
|
||||
io.Copy(w, f)
|
||||
f.Close()
|
||||
return
|
||||
} else if err == ErrNotFound && PluginParamAutoindex() {
|
||||
if files, err := app.Backend.Ls(strings.TrimSuffix(path, "index.html")); err == nil {
|
||||
if strings.HasSuffix(r.URL.Path, "/") == false {
|
||||
http.Redirect(w, r, r.URL.Path+"/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := TmplAutoindex.Execute(w, map[string]any{
|
||||
"Base": r.URL.Path,
|
||||
"Files": files,
|
||||
}); err != nil {
|
||||
SendErrorResult(w, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
SendErrorResult(w, ErrNotFound)
|
||||
}
|
||||
|
||||
func SharesListHandler(app *App, w http.ResponseWriter, r *http.Request) {
|
||||
shares, err := model.ShareAll()
|
||||
if err != nil {
|
||||
SendErrorResult(w, err)
|
||||
return
|
||||
}
|
||||
files := make([]os.FileInfo, len(shares))
|
||||
for i, share := range shares {
|
||||
t := int64(-1)
|
||||
if share.Expire != nil {
|
||||
t = *share.Expire
|
||||
}
|
||||
files[i] = File{
|
||||
FName: share.Id,
|
||||
FType: "directory",
|
||||
FTime: t,
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := TmplAutoindex.Execute(w, map[string]any{
|
||||
"Base": r.URL.Path,
|
||||
"Files": files,
|
||||
}); err != nil {
|
||||
SendErrorResult(w, err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
48
server/plugin/plg_handler_site/middleware.go
Normal file
48
server/plugin/plg_handler_site/middleware.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package plg_handler_site
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func cors(fn HandlerFunc) HandlerFunc {
|
||||
return HandlerFunc(func(ctx *App, w http.ResponseWriter, r *http.Request) {
|
||||
if allowed := PluginParamCORSAllowOrigins(); allowed != "" {
|
||||
w.Header().Add("Vary", "Origin")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS")
|
||||
switch allowed {
|
||||
case "*":
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
default:
|
||||
origin := r.Header.Get("Origin")
|
||||
for _, o := range strings.Split(allowed, ",") {
|
||||
if strings.TrimSpace(o) == origin {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn(ctx, w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func basicAdmin(fn HandlerFunc) HandlerFunc {
|
||||
return HandlerFunc(func(ctx *App, w http.ResponseWriter, r *http.Request) {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
if !ok || user != "admin" {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
} else if err := bcrypt.CompareHashAndPassword([]byte(Config.Get("auth.admin").String()), []byte(pass)); err != nil {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
fn(ctx, w, r)
|
||||
})
|
||||
}
|
||||
24
server/plugin/plg_handler_site/template.go
Normal file
24
server/plugin/plg_handler_site/template.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package plg_handler_site
|
||||
|
||||
import "html/template"
|
||||
|
||||
var TmplAutoindex = template.Must(template.New("autoindex").Parse(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Index of {{ .Base }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index of {{ .Base }}</h1><pre><a href="../">../</a><br>
|
||||
{{- range .Files -}}
|
||||
<a href="{{if .IsDir}}{{printf "./%s/" .Name}}{{else}}{{printf "./%s" .Name}}{{end}}">
|
||||
{{- if .IsDir -}}
|
||||
{{ printf "%-40.40s" (printf "%s/" .Name) }}
|
||||
{{- else -}}
|
||||
{{ printf "%-40.40s" .Name }}
|
||||
{{- end -}}
|
||||
</a> {{ (.ModTime).Format "2006-01-02 15:04:05" }} {{ printf "%8d" .Size }}<br>
|
||||
{{- end }}
|
||||
<hr>
|
||||
</pre>
|
||||
</body>
|
||||
</html>`))
|
||||
Loading…
Reference in a new issue