chore (about): revamp about page

This commit is contained in:
MickaelK 2025-06-28 02:25:18 +10:00
parent a96a192f2e
commit e2f3475bfc
7 changed files with 155 additions and 133 deletions

View file

@ -7,9 +7,10 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mickael-kerjean/filestash" "github.com/mickael-kerjean/filestash"
. "github.com/mickael-kerjean/filestash/server" "github.com/mickael-kerjean/filestash/server"
. "github.com/mickael-kerjean/filestash/server/common" . "github.com/mickael-kerjean/filestash/server/common"
. "github.com/mickael-kerjean/filestash/server/ctrl" "github.com/mickael-kerjean/filestash/server/ctrl"
"github.com/mickael-kerjean/filestash/server/model"
_ "github.com/mickael-kerjean/filestash/server/plugin" _ "github.com/mickael-kerjean/filestash/server/plugin"
) )
@ -18,7 +19,7 @@ func main() {
router *mux.Router = mux.NewRouter() router *mux.Router = mux.NewRouter()
app = App{} app = App{}
) )
Build(router, app) server.Build(router, app)
Run(router, app) Run(router, app)
} }
@ -27,21 +28,20 @@ func Run(routes *mux.Router, app App) {
// support many more protocols in the future: HTTPS, HTTP2, TOR or whatever that sounds // 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, ... // fancy I don't know much when this got written: IPFS, solid, ...
Log.Info("Filestash %s starting", APP_VERSION) Log.Info("Filestash %s starting", APP_VERSION)
check(InitLogger(), "Logger init failed. err=%s")
check(InitConfig(), "Config init failed. err=%s")
check(model.PluginDiscovery(), "Plugin Discovery failed. err=%s")
ctrl.InitPluginList(embed.EmbedPluginList, model.PLUGINS)
if len(Hooks.Get.Starter()) == 0 { if len(Hooks.Get.Starter()) == 0 {
Log.Warning("No starter plugin available") check(ErrNotFound, "Missing starter plugin. err=%s")
os.Exit(1)
return
} }
InitLogger()
InitConfig()
InitPluginList(embed.EmbedPluginList)
for _, obj := range Hooks.Get.HttpEndpoint() { for _, obj := range Hooks.Get.HttpEndpoint() {
obj(routes, &app) obj(routes, &app)
} }
for _, fn := range Hooks.Get.Onload() { for _, fn := range Hooks.Get.Onload() {
fn() fn()
} }
CatchAll(routes, app) server.CatchAll(routes, app)
var wg sync.WaitGroup var wg sync.WaitGroup
for _, obj := range Hooks.Get.Starter() { for _, obj := range Hooks.Get.Starter() {
wg.Add(1) wg.Add(1)
@ -52,3 +52,11 @@ func Run(routes *mux.Router, app App) {
} }
wg.Wait() wg.Wait()
} }
func check(err error, msg string) {
if err == nil {
return
}
Log.Error(msg, err.Error())
os.Exit(1)
}

View file

@ -50,10 +50,13 @@ type FormElement struct {
Required bool `json:"required"` Required bool `json:"required"`
} }
func InitConfig() { func InitConfig() error {
Config = NewConfiguration() Config = NewConfiguration()
Config.Load() if err := Config.Load(); err != nil {
return err
}
Config.Initialise() Config.Initialise()
return nil
} }
func NewConfiguration() Configuration { func NewConfiguration() Configuration {
@ -218,11 +221,11 @@ func (this *Form) Iterator() []FormIterator {
return slice return slice
} }
func (this *Configuration) Load() { func (this *Configuration) Load() error {
cFile, err := LoadConfig() cFile, err := LoadConfig()
if err != nil { if err != nil {
Log.Error("config::load %s", err) Log.Error("config::load %s", err)
return return err
} }
// Extract enabled backends // Extract enabled backends
@ -251,7 +254,7 @@ func (this *Configuration) Load() {
this.onChange[i].Listener <- nil this.onChange[i].Listener <- nil
} }
}() }()
return return nil
} }
type JSONIterator struct { type JSONIterator struct {

View file

@ -13,14 +13,14 @@ var (
logfile *os.File logfile *os.File
) )
func InitLogger() { func InitLogger() error {
var err error logfile, err := os.OpenFile(GetAbsolutePath(LOG_PATH, "access.log"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.ModePerm)
logfile, err = os.OpenFile(GetAbsolutePath(LOG_PATH, "access.log"), os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.ModePerm)
if err != nil { if err != nil {
slog.Printf("ERROR log file: %+v", err) slog.Printf("ERROR log file: %+v", err)
return return err
} }
logfile.WriteString("") logfile.WriteString("")
return nil
} }
type log struct { type log struct {

124
server/ctrl/about.go Normal file
View file

@ -0,0 +1,124 @@
package ctrl
import (
"fmt"
"html"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
. "github.com/mickael-kerjean/filestash/server/common"
"github.com/mickael-kerjean/filestash/server/model"
)
var listOfPlugins = struct {
oss []string
enterprise []string
custom []string
apps []string
}{}
func InitPluginList(code []byte, plgs map[string]model.PluginImpl) {
listOfPackages := regexp.MustCompile(`\t_?\s*\"(github.com/[^\"]+)`).FindAllStringSubmatch(string(code), -1)
for _, packageNameMatch := range listOfPackages {
if len(packageNameMatch) != 2 {
Log.Error("ctrl::static error=assertion_failed msg=invalid_match_size arg=%d", len(packageNameMatch))
}
packageName := packageNameMatch[1]
packageShortName := filepath.Base(packageName)
if strings.HasPrefix(packageName, "github.com/mickael-kerjean/filestash/server/plugin/") {
listOfPlugins.oss = append(listOfPlugins.oss, packageShortName)
} else if strings.HasPrefix(packageName, "github.com/mickael-kerjean/filestash/filestash-enterprise/plugins/") {
listOfPlugins.enterprise = append(listOfPlugins.enterprise, packageShortName)
} else if strings.HasPrefix(packageName, "github.com/mickael-kerjean/filestash/filestash-enterprise/customers/") {
listOfPlugins.custom = append(listOfPlugins.custom, packageShortName)
} else {
listOfPlugins.custom = append(listOfPlugins.custom, packageShortName)
}
}
for name, _ := range plgs {
listOfPlugins.apps = append(listOfPlugins.apps, name)
}
}
func AboutHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
t, _ := template.
New("about").
Funcs(map[string]interface{}{
"renderPlugin": func(lstr string, commit string) string {
if len(lstr) == 0 {
return "N/A"
} else if commit == "" {
return html.EscapeString(lstr)
}
list := strings.Split(lstr, " ")
for i, _ := range list {
list[i] = `<a href="https://github.com/mickael-kerjean/filestash/tree/` + commit +
`/server/plugin/` + html.EscapeString(list[i]) + `" target="_blank">` + html.EscapeString(list[i]) + `</a>`
}
return strings.Join(list, " ")
},
}).
Parse(Page(`
<h1> {{ .Version }} </h1>
<table>
<tr> <td style="width:150px;"> Commit hash </td> <td> <a href="https://github.com/mickael-kerjean/filestash/tree/{{ .CommitHash }}">{{ .CommitHash }}</a> </td> </tr>
<tr> <td> Binary hash </td> <td> {{ index .Checksum 0}} </td> </tr>
<tr> <td> Config hash </td> <td> {{ index .Checksum 1}} </td> </tr>
<tr> <td> License </td> <td> {{ .License }} </td> </tr>
<tr>
<td> Plugins </td>
<td>
STANDARD[<span class="small">{{ renderPlugin (index .Plugins 0) .CommitHash }}</span>]
<br/>
APPS[<span class="small">{{ renderPlugin (index .Plugins 3) "" }}</span>]
<br/>
ENTERPRISE[<span class="small">{{ renderPlugin (index .Plugins 1) "" }}</span>]
<br/>
CUSTOM[<span class="small">{{ renderPlugin (index .Plugins 2) "" }}</span>]
</td>
</tr>
</table>
<style>
body.common_response_page { background: var(--bg-color); }
table { margin: 0 auto; font-family: monospace; opacity: 0.8; max-width: 1000px; width: 95%;}
table td { text-align: right; padding-left: 10px; vertical-align: top; }
table td span.small { font-size:0.8rem; }
table a { color: inherit; text-decoration: none; }
</style>
`))
hashFileContent := func(path string, n int) string {
f, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
if err != nil {
return ""
}
defer f.Close()
return HashStream(f, n)
}
t.Execute(res, struct {
Version string
CommitHash string
Checksum []string
License string
Plugins []string
}{
Version: fmt.Sprintf("Filestash %s.%s", APP_VERSION, BUILD_DATE),
CommitHash: BUILD_REF,
Checksum: []string{
hashFileContent(GetAbsolutePath("filestash"), 0),
hashFileContent(GetAbsolutePath(CONFIG_PATH, "config.json"), 0),
},
License: strings.ToUpper(LICENSE),
Plugins: []string{
strings.Join(listOfPlugins.oss, " "),
strings.Join(listOfPlugins.enterprise, " "),
strings.Join(listOfPlugins.custom, " "),
strings.Join(listOfPlugins.apps, " "),
},
})
}

View file

@ -13,15 +13,6 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func init() {
Hooks.Register.Onload(func() {
if err := model.PluginDiscovery(); err != nil {
Log.Error("Plugin Discovery failed. err=%s", err.Error())
os.Exit(1)
}
})
}
func PluginExportHandler(ctx *App, res http.ResponseWriter, req *http.Request) { func PluginExportHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
plgExports := map[string][]string{} plgExports := map[string][]string{}
for name, plg := range model.PLUGINS { for name, plg := range model.PLUGINS {

View file

@ -11,7 +11,6 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"text/template" "text/template"
@ -105,87 +104,6 @@ func NotFoundHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
SendErrorResult(res, ErrNotFound) SendErrorResult(res, ErrNotFound)
} }
var listOfPlugins map[string][]string = map[string][]string{
"oss": []string{},
"enterprise": []string{},
"custom": []string{},
}
func AboutHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
t, _ := template.
New("about").
Funcs(map[string]interface{}{
"renderPlugin": func(lstr string, commit string) string {
if len(lstr) == 0 {
return "N/A"
} else if commit == "" {
return lstr
}
list := strings.Split(lstr, " ")
for i, _ := range list {
list[i] = `<a href="https://github.com/mickael-kerjean/filestash/tree/` + commit +
`/server/plugin/` + list[i] + `" target="_blank">` + list[i] + `</a>`
}
return strings.Join(list, " ")
},
}).
Parse(Page(`
<h1> {{ .Version }} </h1>
<table>
<tr> <td style="width:150px;"> Commit hash </td> <td> <a href="https://github.com/mickael-kerjean/filestash/tree/{{ .CommitHash }}">{{ .CommitHash }}</a> </td> </tr>
<tr> <td> Binary hash </td> <td> {{ index .Checksum 0}} </td> </tr>
<tr> <td> Config hash </td> <td> {{ index .Checksum 1}} </td> </tr>
<tr> <td> License </td> <td> {{ .License }} </td> </tr>
<tr>
<td> Plugins </td>
<td>
STANDARD[<span class="small">{{ renderPlugin (index .Plugins 0) .CommitHash }}</span>]
<br/>
ENTERPRISE[<span class="small">{{ renderPlugin (index .Plugins 1) "" }}</span>]
<br/>
CUSTOM[<span class="small">{{ renderPlugin (index .Plugins 2) "" }}</span>]
</td>
</tr>
</table>
<style>
body.common_response_page { background: var(--bg-color); }
table { margin: 0 auto; font-family: monospace; opacity: 0.8; max-width: 1000px; width: 95%;}
table td { text-align: right; padding-left: 10px; vertical-align: top; }
table td span.small { font-size:0.8rem; }
table a { color: inherit; text-decoration: none; }
</style>
`))
hashFileContent := func(path string, n int) string {
f, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
if err != nil {
return ""
}
defer f.Close()
return HashStream(f, n)
}
t.Execute(res, struct {
Version string
CommitHash string
Checksum []string
License string
Plugins []string
}{
Version: fmt.Sprintf("Filestash %s.%s", APP_VERSION, BUILD_DATE),
CommitHash: BUILD_REF,
Checksum: []string{
hashFileContent(GetAbsolutePath("filestash"), 0),
hashFileContent(GetAbsolutePath(CONFIG_PATH, "config.json"), 0),
},
License: strings.ToUpper(LICENSE),
Plugins: []string{
strings.Join(listOfPlugins["oss"], " "),
strings.Join(listOfPlugins["enterprise"], " "),
strings.Join(listOfPlugins["custom"], " "),
},
})
}
func ManifestHandler(ctx *App, res http.ResponseWriter, req *http.Request) { func ManifestHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
res.WriteHeader(http.StatusOK) res.WriteHeader(http.StatusOK)
res.Write([]byte(fmt.Sprintf(`{ res.Write([]byte(fmt.Sprintf(`{
@ -234,7 +152,6 @@ func ServeFile(chroot string) func(*App, http.ResponseWriter, *http.Request) {
) )
head := res.Header() head := res.Header()
// case: patch must be apply because of a "StaticPatch" plugin
if f := applyPatch(filePath); f != nil { if f := applyPatch(filePath); f != nil {
head.Set("Content-Type", GetMimeType(filepath.Ext(filePath))) head.Set("Content-Type", GetMimeType(filepath.Ext(filePath)))
head.Set("Cache-Control", "no-cache") head.Set("Cache-Control", "no-cache")
@ -405,27 +322,6 @@ func applyPatch(filePath string) (file *bytes.Buffer) {
return nil return nil
} }
func InitPluginList(code []byte) {
listOfPackages := regexp.MustCompile(`\t_?\s*\"(github.com/[^\"]+)`).FindAllStringSubmatch(string(code), -1)
for _, packageNameMatch := range listOfPackages {
if len(packageNameMatch) != 2 {
Log.Error("ctrl::static error=assertion_failed msg=invalid_match_size arg=%d", len(packageNameMatch))
}
packageName := packageNameMatch[1]
packageShortName := filepath.Base(packageName)
if strings.HasPrefix(packageName, "github.com/mickael-kerjean/filestash/server/plugin/") {
listOfPlugins["oss"] = append(listOfPlugins["oss"], packageShortName)
} else if strings.HasPrefix(packageName, "github.com/mickael-kerjean/filestash/filestash-enterprise/plugins/") {
listOfPlugins["enterprise"] = append(listOfPlugins["enterprise"], packageShortName)
} else if strings.HasPrefix(packageName, "github.com/mickael-kerjean/filestash/filestash-enterprise/customers/") {
listOfPlugins["custom"] = append(listOfPlugins["custom"], packageShortName)
} else {
listOfPlugins["custom"] = append(listOfPlugins["custom"], packageShortName)
}
}
}
func preload() string { func preload() string {
out, _ := json.Marshal([][]string{ out, _ := json.Marshal([][]string{
{ {

View file

@ -1,4 +1,4 @@
package routes package server
import ( import (
"fmt" "fmt"