mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-31 01:58:11 +08:00
This is to enable static assets to be patched dynamically via a plugin. The idea is to be able to apply things like custom branding, custom banners and change in the behavior of the software via plugins that override the core
260 lines
6.8 KiB
Go
260 lines
6.8 KiB
Go
package common
|
|
|
|
import (
|
|
"io"
|
|
"io/fs"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
type Plugin struct {
|
|
Type string
|
|
Enable bool
|
|
}
|
|
|
|
type Register struct{}
|
|
type Get struct{}
|
|
type All struct{}
|
|
|
|
var Hooks = struct {
|
|
Get Get
|
|
Register Register
|
|
}{
|
|
Get: Get{},
|
|
Register: Register{},
|
|
}
|
|
|
|
/*
|
|
* ProcessFileContentBeforeSend is a processing hooks used in plugins like:
|
|
* 1. pluggable image transcoding service: plg_image_light, plg_image_bimg, plg_image_golang
|
|
* 2. video transcoding service: plg_video_transcode
|
|
* 3. disallow certain type of file: plg_security_svg
|
|
*/
|
|
var process_file_content_before_send []func(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, error)
|
|
|
|
func (this Register) ProcessFileContentBeforeSend(fn func(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, error)) {
|
|
process_file_content_before_send = append(process_file_content_before_send, fn)
|
|
}
|
|
func (this Get) ProcessFileContentBeforeSend() []func(io.ReadCloser, *App, *http.ResponseWriter, *http.Request) (io.ReadCloser, error) {
|
|
return process_file_content_before_send
|
|
}
|
|
|
|
/*
|
|
* HttpEndpoint is a hook that makes it possible to register new endpoint in the application.
|
|
* It is used in plugin like:
|
|
* 1. plg_video_transcoder to server the transcoded video segment via hls
|
|
* 2. plg_editor_onlyoffice to server the content for a custom type in an iframe
|
|
* 3. plg_handler_syncthing to create better integration with syncthing
|
|
* 4. plg_handler_console to server a full blown console for debugging the application
|
|
*/
|
|
var http_endpoint []func(*mux.Router, *App) error
|
|
|
|
func (this Register) HttpEndpoint(fn func(*mux.Router, *App) error) {
|
|
http_endpoint = append(http_endpoint, fn)
|
|
}
|
|
func (this Get) HttpEndpoint() []func(*mux.Router, *App) error {
|
|
return http_endpoint
|
|
}
|
|
|
|
/*
|
|
* Override some urls with static content. The main use case for this is to enable
|
|
* plugins to change the frontend code and overwrite some core components
|
|
*/
|
|
func (this Register) Static(www fs.FS, chroot string) {
|
|
fs.WalkDir(www, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
} else if d.IsDir() {
|
|
return nil
|
|
}
|
|
this.HttpEndpoint(func(r *mux.Router, app *App) error {
|
|
r.PathPrefix("/" + strings.TrimPrefix(path, chroot)).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
f, err := www.Open(path)
|
|
if err != nil {
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Write([]byte("plugin.go::static::err " + err.Error()))
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", GetMimeType(filepath.Ext(path)))
|
|
io.Copy(w, f)
|
|
f.Close()
|
|
})
|
|
return nil
|
|
})
|
|
return nil
|
|
})
|
|
}
|
|
|
|
/*
|
|
* Starter is the meat that let us connect to a wide variety of server like:
|
|
* - plg_starter_http which is the default that server the application under 8334
|
|
* - plg_starter_tor to serve the application via tor
|
|
* - plg_starter_web that create ssl certificate via letsencrypt
|
|
* - plg_started_http2 to create an HTTP2 server
|
|
* - ...
|
|
*/
|
|
var starter_process []func(*mux.Router)
|
|
|
|
func (this Register) Starter(fn func(*mux.Router)) {
|
|
starter_process = append(starter_process, fn)
|
|
}
|
|
func (this Get) Starter() []func(*mux.Router) {
|
|
return starter_process
|
|
}
|
|
|
|
/*
|
|
* AuthenticationMiddleware is what enabled us to authenticate user via different means:
|
|
* - plg_authentication_admin to enable connection to an admin
|
|
* - plg_authentication_saml
|
|
* - plg_authentication_openid
|
|
* - plg_authentication_ldap
|
|
* - ...
|
|
*/
|
|
var authentication_middleware map[string]IAuthentication = make(map[string]IAuthentication, 0)
|
|
|
|
func (this Register) AuthenticationMiddleware(id string, am IAuthentication) {
|
|
authentication_middleware[id] = am
|
|
}
|
|
|
|
func (this Get) AuthenticationMiddleware() map[string]IAuthentication {
|
|
return authentication_middleware
|
|
}
|
|
|
|
/*
|
|
* AuthorisationMiddleware is to enable custom rule for authorisation. eg: anonymous can see, registered
|
|
* user can see/edit some files but not some others, admin can do everything
|
|
*/
|
|
var authorisation_middleware []IAuthorisation
|
|
|
|
func (this Register) AuthorisationMiddleware(a IAuthorisation) {
|
|
authorisation_middleware = append(authorisation_middleware, a)
|
|
}
|
|
|
|
func (this Get) AuthorisationMiddleware() []IAuthorisation {
|
|
return authorisation_middleware
|
|
}
|
|
|
|
/*
|
|
* Search is the pluggable search mechanism. By default, there's 2 options:
|
|
* - plg_search_stateless which does stateless search based on filename only
|
|
* - plg_search_statefull which does full text search with a sqlite data store
|
|
* The idea here is to enable different type of usage like leveraging elastic search or solr
|
|
* with custom stuff around it
|
|
*/
|
|
var search ISearch
|
|
|
|
func (this Register) SearchEngine(s ISearch) {
|
|
search = s
|
|
}
|
|
|
|
func (this Get) SearchEngine() ISearch {
|
|
return search
|
|
}
|
|
|
|
/*
|
|
* The idea here is to enable plugin to register their own thumbnailing process, typically
|
|
* images but could also be videos, pdf, excel documents, ...
|
|
*/
|
|
var thumbnailer map[string]IThumbnailer = make(map[string]IThumbnailer)
|
|
|
|
func (this Register) Thumbnailer(mimeType string, fn IThumbnailer) {
|
|
thumbnailer[mimeType] = fn
|
|
}
|
|
|
|
func (this Get) Thumbnailer() map[string]IThumbnailer {
|
|
return thumbnailer
|
|
}
|
|
|
|
/*
|
|
* Pluggable Audit interface
|
|
*/
|
|
var audit IAuditPlugin
|
|
|
|
func (this Register) AuditEngine(a IAuditPlugin) {
|
|
audit = a
|
|
}
|
|
|
|
func (this Get) AuditEngine() IAuditPlugin {
|
|
return audit
|
|
}
|
|
|
|
/*
|
|
* UI Overrides
|
|
* They are the means by which server plugin change the frontend behaviors.
|
|
*/
|
|
var overrides []string
|
|
|
|
func (this Register) FrontendOverrides(url string) {
|
|
overrides = append(overrides, url)
|
|
}
|
|
func (this Get) FrontendOverrides() []string {
|
|
return overrides
|
|
}
|
|
|
|
var xdg_open []string
|
|
|
|
func (this Register) XDGOpen(jsString string) {
|
|
xdg_open = append(xdg_open, jsString)
|
|
}
|
|
func (this Get) XDGOpen() []string {
|
|
return xdg_open
|
|
}
|
|
|
|
var cssOverride []func() string
|
|
|
|
func (this Register) CSS(stylesheet string) {
|
|
cssOverride = append(cssOverride, func() string {
|
|
return stylesheet
|
|
})
|
|
}
|
|
|
|
func (this Register) CSSFunc(stylesheet func() string) {
|
|
cssOverride = append(cssOverride, stylesheet)
|
|
}
|
|
|
|
func (this Get) CSS() string {
|
|
s := ""
|
|
for i := 0; i < len(cssOverride); i++ {
|
|
s += cssOverride[i]() + "\n"
|
|
}
|
|
return s
|
|
}
|
|
|
|
const OverrideVideoSourceMapper = "/overrides/video-transcoder.js"
|
|
|
|
var afterload []func()
|
|
|
|
func (this Register) Onload(fn func()) {
|
|
afterload = append(afterload, fn)
|
|
}
|
|
func (this Get) Onload() []func() {
|
|
return afterload
|
|
}
|
|
|
|
var middlewares []func(HandlerFunc) HandlerFunc
|
|
|
|
func (this Register) Middleware(m func(HandlerFunc) HandlerFunc) {
|
|
middlewares = append(middlewares, m)
|
|
}
|
|
|
|
func (this Get) Middleware() []func(HandlerFunc) HandlerFunc {
|
|
return middlewares
|
|
}
|
|
|
|
var staticOverrides []fs.FS
|
|
|
|
func (this Register) StaticPatch(folder fs.FS) {
|
|
staticOverrides = append(staticOverrides, folder)
|
|
}
|
|
|
|
func (this Get) StaticPatch() []fs.FS {
|
|
return staticOverrides
|
|
}
|
|
|
|
func init() {
|
|
Hooks.Register.FrontendOverrides(OverrideVideoSourceMapper)
|
|
}
|