Skip to content

Commit

Permalink
Add custom static links (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomiceli committed Apr 2, 2024
1 parent c185cb8 commit 3f5f4e0
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 51 deletions.
10 changes: 9 additions & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ oidc.discovery-url:


# Custom assets
# Add your own custom assets to $opengist-home/custom/
# Add your own custom assets, that are files relatives to $opengist-home/custom/
custom.logo:
custom.favicon:

# Static pages in footer (like legal notices, privacy policy, etc.)
# The path can be a URL or a relative path to a file in the $opengist-home/custom/ directory
custom.static-links:
# - name: Gitea
# path: https://gitea.com
# - name: Legal notices
# path: legal.html
69 changes: 35 additions & 34 deletions docs/configuration/cheat-sheet.md

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions docs/configuration/custom-assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Custom assets

To add custom assets to your Opengist instance, you can use the `$opengist-home/custom` directory (where `$opengist-home` is the directory where Opengist stores its data).

### Logo / Favicon

To add a custom logo or favicon, you can add your own image file to the `$opengist-home/custom` directory, then define the relative path in the config.

For example, if you have a logo file `logo.png` in the `$opengist-home/custom` directory, you can set the logo path in the config as follows:

#### YAML
```yaml
custom.logo: logo.png
```

#### Environment variable
```sh
export OG_CUSTOM_LOGO=logo.png
```

Same as the favicon:

#### YAML
```yaml
custom.favicon: favicon.png
```

#### Environment variable
```sh
export OG_CUSTOM_FAVICON=favicon.png
```
38 changes: 38 additions & 0 deletions docs/configuration/custom-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Custom links

On the footer of your Opengist instance, you can add links to custom static templates or any other website you want to link to.
This can be useful for legal information, privacy policy, or any other information you want to provide to your users.

To add one or more links, you can add your own file to the `$opengist-home/custom` directory or set a URL, then define the relative path and its name in the config.

For example, if you have a legal information file `legal.html` in the `$opengist-home/custom` directory, and also wish to add a link to a Gitea instance, you can set the link in the config as follows:

#### YAML
```yaml
custom.static-links:
- name: Legal notices
path: legal.html
- name: Gitea
path: https://gitea.com
```

#### Environment variable
```sh
OG_CUSTOM_STATIC_LINK_0_NAME="Legal Notices" \
OG_CUSTOM_STATIC_LINK_0_PATH=legal.html \
OG_CUSTOM_STATIC_LINK_1_NAME=Gitea \
OG_CUSTOM_STATIC_LINK_1_PATH=https://gitea.com \
./opengist
```

## Templating custom HTML pages

In the start and end of the custom HTML files, you can use the syntax to include the header and footer of the Opengist instance:

```html
{{ template "header" . }}

<!-- my content -->

{{ template "footer" . }}
```
56 changes: 51 additions & 5 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,14 @@ type config struct {
OIDCSecret string `yaml:"oidc.secret" env:"OG_OIDC_SECRET"`
OIDCDiscoveryUrl string `yaml:"oidc.discovery-url" env:"OG_OIDC_DISCOVERY_URL"`

CustomLogo string `yaml:"custom.logo" env:"OG_CUSTOM_LOGO"`
CustomFavicon string `yaml:"custom.favicon" env:"OG_CUSTOM_FAVICON"`
CustomLogo string `yaml:"custom.logo" env:"OG_CUSTOM_LOGO"`
CustomFavicon string `yaml:"custom.favicon" env:"OG_CUSTOM_FAVICON"`
StaticLinks []StaticLink `yaml:"custom.static-links" env:"OG_CUSTOM_STATIC_LINK"`
}

type StaticLink struct {
Name string `yaml:"name" env:"OG_CUSTOM_STATIC_LINK_#_NAME"`
Path string `yaml:"path" env:"OG_CUSTOM_STATIC_LINK_#_PATH"`
}

func configWithDefaults() (*config, error) {
Expand Down Expand Up @@ -129,7 +135,6 @@ func InitConfig(configPath string, out io.Writer) error {
if err = os.Setenv("OG_OPENGIST_HOME_INTERNAL", GetHomeDir()); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -246,22 +251,63 @@ func loadConfigFromEnv(c *config, out io.Writer) error {
}

envValue := os.Getenv(strings.ToUpper(tag))
if envValue == "" {
if envValue == "" && v.Field(i).Kind() != reflect.Slice {
continue
}

switch v.Field(i).Kind() {
case reflect.String:
v.Field(i).SetString(envValue)
envVars = append(envVars, tag)
case reflect.Bool:
boolVal, err := strconv.ParseBool(envValue)
if err != nil {
return err
}
v.Field(i).SetBool(boolVal)
envVars = append(envVars, tag)
case reflect.Slice:
if v.Type().Field(i).Type.Elem().Kind() == reflect.Struct {
prefix := strings.ToUpper(tag) + "_"
var sliceValue reflect.Value
elemType := v.Type().Field(i).Type.Elem()

for index := 0; ; index++ {
allFieldsPresent := true
elemValue := reflect.New(elemType).Elem()

for j := 0; j < elemValue.NumField() && allFieldsPresent; j++ {
elemField := elemValue.Type().Field(j)
envName := fmt.Sprintf("%s%d_%s", prefix, index, strings.ToUpper(elemField.Name))
envValue, present := os.LookupEnv(envName)

if !present {
allFieldsPresent = false
break
}

envVars = append(envVars, envName)
elemValue.Field(j).SetString(envValue)
}

if !allFieldsPresent {
break
}

if sliceValue.Kind() != reflect.Slice {
sliceValue = reflect.MakeSlice(v.Type().Field(i).Type, 0, index+1)
}
sliceValue = reflect.Append(sliceValue, elemValue)
}

if sliceValue.IsValid() {
v.Field(i).Set(sliceValue)
}
}
default:
return fmt.Errorf("unsupported type: %s", v.Field(i).Kind())
}

envVars = append(envVars, tag)
}

if len(envVars) > 0 {
Expand Down
46 changes: 36 additions & 10 deletions internal/web/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"github.com/thomiceli/opengist/internal/index"
"github.com/thomiceli/opengist/internal/utils"
"github.com/thomiceli/opengist/templates"
htmlpkg "html"
"html/template"
"io"
Expand All @@ -30,7 +31,6 @@ import (
"github.com/thomiceli/opengist/internal/git"
"github.com/thomiceli/opengist/internal/i18n"
"github.com/thomiceli/opengist/public"
"github.com/thomiceli/opengist/templates"
"golang.org/x/text/language"
)

Expand Down Expand Up @@ -138,6 +138,10 @@ var (
},
"addMetadataToSearchQuery": addMetadataToSearchQuery,
"indexEnabled": index.Enabled,
"isUrl": func(s string) bool {
_, err := url.ParseRequestURI(s)
return err == nil
},
}
)

Expand Down Expand Up @@ -186,9 +190,22 @@ func NewServer(isDev bool) *Server {
e.Use(middleware.Recover())
e.Use(middleware.Secure())

t := template.Must(template.New("t").Funcs(fm).ParseFS(templates.Files, "*/*.html"))
customPattern := filepath.Join(config.GetHomeDir(), "custom", "*.html")
matches, err := filepath.Glob(customPattern)
if err != nil {
log.Fatal().Err(err).Msg("Failed to check for custom templates")
}
if len(matches) > 0 {
t, err = t.ParseGlob(customPattern)
if err != nil {
log.Fatal().Err(err).Msg("Failed to parse custom templates")
}
}
e.Renderer = &Template{
templates: template.Must(template.New("t").Funcs(fm).ParseFS(templates.Files, "*/*.html")),
templates: t,
}

e.HTTPErrorHandler = func(er error, ctx echo.Context) {
if err, ok := er.(*echo.HTTPError); ok {
if err.Code >= 500 {
Expand All @@ -211,14 +228,6 @@ func NewServer(isDev bool) *Server {
if !dev {
parseManifestEntries()
}
customFs := os.DirFS(filepath.Join(config.GetHomeDir(), "custom"))
e.GET("/assets/*", func(c echo.Context) error {
if _, err := public.Files.Open(path.Join("assets", c.Param("*"))); !dev && err == nil {
return echo.WrapHandler(http.FileServer(http.FS(public.Files)))(c)
}

return echo.WrapHandler(http.StripPrefix("/assets/", http.FileServer(http.FS(customFs))))(c)
})

// Web based routes
g1 := e.Group("")
Expand Down Expand Up @@ -309,6 +318,23 @@ func NewServer(isDev bool) *Server {
}
}

customFs := os.DirFS(filepath.Join(config.GetHomeDir(), "custom"))
e.GET("/assets/*", func(ctx echo.Context) error {
if _, err := public.Files.Open(path.Join("assets", ctx.Param("*"))); !dev && err == nil {
return echo.WrapHandler(http.FileServer(http.FS(public.Files)))(ctx)
}

// if the custom file is an .html template, render it
if strings.HasSuffix(ctx.Param("*"), ".html") {
if err := html(ctx, ctx.Param("*")); err != nil {
return notFound("Page not found")
}
return nil
}

return echo.WrapHandler(http.StripPrefix("/assets/", http.FileServer(http.FS(customFs))))(ctx)
})

// Git HTTP routes
if config.C.HttpGit {
e.Any("/:user/:gistname/*", gitHttp, gistSoftInit)
Expand Down
9 changes: 8 additions & 1 deletion templates/base/base_footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

{{ define "footer" }}
<div class="inline-flex py-8">
<p class="text-slate-600 dark:text-slate-400 [&>*]:mx-1.5 flex">
<p class="text-slate-600 dark:text-slate-400 [&>*]:mx-1.5 -ml-1.5 flex">
<span>
<a target="_blank" style="margin-left: 0 !important;" class="text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 inline-flex" href="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/thomiceli/opengist">
<span class="mr-1">{{ .locale.Tr "footer.powered-by" "<span class=\"font-bold dark:text-slate-300\">Opengist</span>" }} </span>
Expand All @@ -28,6 +28,13 @@
</div>
</div>
</div>
{{ if ne (len .c.StaticLinks) 0 }}
<div class="ml-1.5">
{{ range $index, $value := .c.StaticLinks }}
<a href="{{ if isUrl .Path }}{{ .Path }}{{ else }}{{ $.c.ExternalUrl }}/assets/{{ .Path }}{{ end }}" class="text-slate-600 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-200 inline-flex">{{ .Name }}</a>
{{ end }}
</div>
{{ end }}
</div>
</div>
</div>
Expand Down

0 comments on commit 3f5f4e0

Please sign in to comment.