mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-28 04:05:21 +08:00
202 lines
9.7 KiB
Go
202 lines
9.7 KiB
Go
package main
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"github.com/gorilla/mux"
|
|
. "github.com/mickael-kerjean/filestash/server/common"
|
|
. "github.com/mickael-kerjean/filestash/server/ctrl"
|
|
. "github.com/mickael-kerjean/filestash/server/middleware"
|
|
_ "github.com/mickael-kerjean/filestash/server/plugin"
|
|
"net/http"
|
|
"net/http/pprof"
|
|
"os"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strconv"
|
|
"sync"
|
|
)
|
|
|
|
//go:embed plugin/index.go
|
|
var EmbedPluginList []byte
|
|
|
|
func main() {
|
|
app := App{}
|
|
Init(app)
|
|
}
|
|
|
|
func Init(a App) {
|
|
var (
|
|
r *mux.Router = mux.NewRouter()
|
|
middlewares []Middleware
|
|
)
|
|
|
|
// API for Session
|
|
session := r.PathPrefix("/api/session").Subrouter()
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart}
|
|
session.HandleFunc("", NewMiddlewareChain(SessionGet, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, RateLimiter, BodyParser}
|
|
session.HandleFunc("", NewMiddlewareChain(SessionAuthenticate, middlewares, a)).Methods("POST")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax}
|
|
session.HandleFunc("", NewMiddlewareChain(SessionLogout, middlewares, a)).Methods("DELETE")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders}
|
|
session.HandleFunc("/auth/{service}", NewMiddlewareChain(SessionOAuthBackend, middlewares, a)).Methods("GET")
|
|
session.HandleFunc("/auth/", NewMiddlewareChain(SessionAuthMiddleware, middlewares, a)).Methods("GET", "POST")
|
|
middlewares = []Middleware{ApiHeaders, RateLimiter, BodyParser}
|
|
r.HandleFunc("/api/token", NewMiddlewareChain(SessionAuthenticateExternal, middlewares, a)).Methods("POST")
|
|
|
|
// API for Admin Console
|
|
admin := r.PathPrefix("/admin/api").Subrouter()
|
|
middlewares = []Middleware{ApiHeaders, SecureAjax}
|
|
admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionGet, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{ApiHeaders, SecureAjax, RateLimiter}
|
|
admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionAuthenticate, middlewares, a)).Methods("POST")
|
|
middlewares = []Middleware{ApiHeaders, AdminOnly, SecureAjax}
|
|
admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigHandler, middlewares, a)).Methods("GET")
|
|
admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigUpdateHandler, middlewares, a)).Methods("POST")
|
|
admin.HandleFunc("/audit", NewMiddlewareChain(FetchAuditHandler, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{IndexHeaders, AdminOnly}
|
|
admin.HandleFunc("/logs", NewMiddlewareChain(FetchLogHandler, middlewares, a)).Methods("GET")
|
|
|
|
// API for File management
|
|
files := r.PathPrefix("/api/files").Subrouter()
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly}
|
|
files.HandleFunc("/cat", NewMiddlewareChain(FileCat, middlewares, a)).Methods("GET", "HEAD")
|
|
files.HandleFunc("/zip", NewMiddlewareChain(FileDownloader, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly}
|
|
files.HandleFunc("/cat", NewMiddlewareChain(FileAccess, middlewares, a)).Methods("OPTIONS")
|
|
files.HandleFunc("/cat", NewMiddlewareChain(FileSave, middlewares, a)).Methods("POST", "PUT")
|
|
files.HandleFunc("/ls", NewMiddlewareChain(FileLs, middlewares, a)).Methods("GET")
|
|
files.HandleFunc("/mv", NewMiddlewareChain(FileMv, middlewares, a)).Methods("GET")
|
|
files.HandleFunc("/rm", NewMiddlewareChain(FileRm, middlewares, a)).Methods("GET")
|
|
files.HandleFunc("/mkdir", NewMiddlewareChain(FileMkdir, middlewares, a)).Methods("GET")
|
|
files.HandleFunc("/touch", NewMiddlewareChain(FileTouch, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{ApiHeaders, SessionStart, LoggedInOnly}
|
|
files.HandleFunc("/search", NewMiddlewareChain(FileSearch, middlewares, a)).Methods("GET")
|
|
|
|
// API for Shared link
|
|
share := r.PathPrefix("/api/share").Subrouter()
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly}
|
|
share.HandleFunc("", NewMiddlewareChain(ShareList, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser}
|
|
share.HandleFunc("/{share}/proof", NewMiddlewareChain(ShareVerifyProof, middlewares, a)).Methods("POST")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, CanManageShare}
|
|
share.HandleFunc("/{share}", NewMiddlewareChain(ShareDelete, middlewares, a)).Methods("DELETE")
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, SecureAjax, BodyParser, CanManageShare}
|
|
share.HandleFunc("/{share}", NewMiddlewareChain(ShareUpsert, middlewares, a)).Methods("POST")
|
|
|
|
// Webdav server / Shared Link
|
|
middlewares = []Middleware{IndexHeaders, SecureHeaders}
|
|
r.HandleFunc("/s/{share}", NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{WebdavBlacklist, SessionStart}
|
|
r.PathPrefix("/s/{share}").Handler(NewMiddlewareChain(WebdavHandler, middlewares, a))
|
|
middlewares = []Middleware{ApiHeaders, SecureHeaders, RedirectSharedLoginIfNeeded, SessionStart, LoggedInOnly}
|
|
r.PathPrefix("/api/export/{share}/{mtype0}/{mtype1}").Handler(NewMiddlewareChain(FileExport, middlewares, a))
|
|
|
|
// Application Resources
|
|
middlewares = []Middleware{ApiHeaders}
|
|
r.HandleFunc("/api/config", NewMiddlewareChain(PublicConfigHandler, middlewares, a)).Methods("GET")
|
|
r.HandleFunc("/api/backend", NewMiddlewareChain(AdminBackend, middlewares, a)).Methods("GET")
|
|
r.HandleFunc("/api/middlewares/authentication", NewMiddlewareChain(AdminAuthenticationMiddleware, middlewares, a)).Methods("GET")
|
|
middlewares = []Middleware{StaticHeaders}
|
|
r.PathPrefix("/assets").Handler(http.HandlerFunc(NewMiddlewareChain(StaticHandler(FILE_ASSETS), middlewares, a))).Methods("GET")
|
|
r.HandleFunc("/favicon.ico", NewMiddlewareChain(StaticHandler(FILE_ASSETS+"/assets/logo/"), middlewares, a)).Methods("GET")
|
|
r.HandleFunc("/sw_cache.js", NewMiddlewareChain(StaticHandler(FILE_ASSETS+"/assets/worker/"), middlewares, a)).Methods("GET")
|
|
|
|
// Other endpoints
|
|
middlewares = []Middleware{ApiHeaders}
|
|
r.HandleFunc("/report", NewMiddlewareChain(ReportHandler, middlewares, a)).Methods("POST")
|
|
middlewares = []Middleware{IndexHeaders}
|
|
r.HandleFunc("/about", NewMiddlewareChain(AboutHandler, middlewares, a)).Methods("GET")
|
|
r.HandleFunc("/robots.txt", NewMiddlewareChain(RobotsHandler, []Middleware{}, a))
|
|
r.HandleFunc("/manifest.json", NewMiddlewareChain(ManifestHandler, []Middleware{}, a)).Methods("GET")
|
|
r.HandleFunc("/.well-known/security.txt", NewMiddlewareChain(WellKnownSecurityHandler, []Middleware{}, a)).Methods("GET")
|
|
r.HandleFunc("/healthz", NewMiddlewareChain(HealthHandler, []Middleware{}, a)).Methods("GET")
|
|
r.HandleFunc("/custom.css", NewMiddlewareChain(CustomCssHandler, []Middleware{}, a)).Methods("GET")
|
|
|
|
if os.Getenv("DEBUG") == "true" {
|
|
initDebugRoutes(r)
|
|
}
|
|
initPluginsRoutes(r, &a)
|
|
|
|
r.PathPrefix("/admin").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, a))).Methods("GET")
|
|
r.PathPrefix("/").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, a))).Methods("GET", "POST")
|
|
|
|
// Routes are served via plugins to avoid getting stuck with plain HTTP. The idea is to
|
|
// support many more protocols in the future: HTTPS, HTTP2, TOR or whatever that sounds
|
|
// fancy I don't know much when this got written: IPFS, solid, ...
|
|
Log.Info("Filestash %s starting", APP_VERSION)
|
|
if len(Hooks.Get.Starter()) == 0 {
|
|
Log.Warning("No starter plugin available")
|
|
os.Exit(1)
|
|
return
|
|
}
|
|
var wg sync.WaitGroup
|
|
for _, obj := range Hooks.Get.Starter() {
|
|
wg.Add(1)
|
|
go func() {
|
|
obj(r)
|
|
wg.Done()
|
|
}()
|
|
}
|
|
go func() {
|
|
InitPluginList(EmbedPluginList)
|
|
}()
|
|
wg.Wait()
|
|
}
|
|
|
|
func initDebugRoutes(r *mux.Router) {
|
|
r.HandleFunc("/debug/pprof/", pprof.Index)
|
|
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
r.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
|
r.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
|
|
r.Handle("/debug/pprof/heap", pprof.Handler("heap"))
|
|
r.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
|
|
r.Handle("/debug/pprof/block", pprof.Handler("block"))
|
|
r.Handle("/debug/pprof/allocs", pprof.Handler("allocs"))
|
|
r.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
|
|
r.HandleFunc("/debug/free", func(w http.ResponseWriter, r *http.Request) {
|
|
debug.FreeOSMemory()
|
|
w.Write([]byte("DONE"))
|
|
})
|
|
bToMb := func(b uint64) string {
|
|
return strconv.Itoa(int(b / 1024 / 1024))
|
|
}
|
|
r.HandleFunc("/debug/memory", func(w http.ResponseWriter, r *http.Request) {
|
|
var m runtime.MemStats
|
|
runtime.ReadMemStats(&m)
|
|
w.Write([]byte("<p style='font-family:monospace'>"))
|
|
w.Write([]byte("Alloc = " + bToMb(m.Alloc) + "MiB <br>"))
|
|
w.Write([]byte("TotalAlloc = " + bToMb(m.TotalAlloc) + "MiB <br>"))
|
|
w.Write([]byte("Sys = " + bToMb(m.Sys) + "MiB <br>"))
|
|
w.Write([]byte("NumGC = " + strconv.Itoa(int(m.NumGC))))
|
|
w.Write([]byte("</p>"))
|
|
})
|
|
}
|
|
|
|
func initPluginsRoutes(r *mux.Router, a *App) {
|
|
// Endpoints handle by plugins
|
|
for _, obj := range Hooks.Get.HttpEndpoint() {
|
|
obj(r, a)
|
|
}
|
|
// frontoffice overrides: it is the mean by which plugin can interact with the frontoffice
|
|
for _, obj := range Hooks.Get.FrontendOverrides() {
|
|
r.HandleFunc(obj, func(res http.ResponseWriter, req *http.Request) {
|
|
res.Header().Set("Content-Type", GetMimeType(req.URL.String()))
|
|
res.Write([]byte(fmt.Sprintf("/* Default '%s' */", obj)))
|
|
})
|
|
}
|
|
// map file types to application handler
|
|
r.HandleFunc("/overrides/xdg-open.js", func(res http.ResponseWriter, req *http.Request) {
|
|
res.Header().Set("Content-Type", GetMimeType(req.URL.String()))
|
|
res.Write([]byte(`window.overrides["xdg-open"] = function(mime){`))
|
|
openers := Hooks.Get.XDGOpen()
|
|
for i := 0; i < len(openers); i++ {
|
|
res.Write([]byte(openers[i]))
|
|
}
|
|
res.Write([]byte(`return null;}`))
|
|
})
|
|
}
|