diff --git a/README.md b/README.md
index db1b02b..b4eced4 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,8 @@ A self-hosted pastebin **powered by Git**. [Try it here](https://opengist.thomic
* [With Docker](#with-docker)
* [From source](#from-source)
* [Configuration](#configuration)
+ * [Via YAML file](#configuration-via-yaml-file)
+ * [Via Environment Variables](#configuration-via-environment-variables)
* [Administration](#administration)
* [Use Nginx as a reverse proxy](#use-nginx-as-a-reverse-proxy)
* [Use Fail2ban](#use-fail2ban)
@@ -53,7 +55,7 @@ A self-hosted pastebin **powered by Git**. [Try it here](https://opengist.thomic
A Docker [image](https://github.com/users/thomiceli/packages/container/package/opengist), available for each release, can be pulled
-```
+```shell
docker pull ghcr.io/thomiceli/opengist:1
```
@@ -76,9 +78,6 @@ services:
- "2222:2222" # SSH port, can be removed if you don't use SSH
volumes:
- "$HOME/.opengist:/root/.opengist"
- environment:
- CONFIG: |
- log-level: info
```
### From source
@@ -96,29 +95,64 @@ Opengist is now running on port 6157, you can browse http://localhost:6157
## Configuration
-Opengist can be configured using YAML. The full configuration file is [config.yml](config.yml), each default key/value
-pair can be overridden.
+Opengist provides flexible configuration options through either a YAML file and/or environment variables.
+You would only need to specify the configuration options you want to change — for any config option left untouched, Opengist will simply apply the default values.
-### With docker
+
+Configuration option list
-Add a `CONFIG` environment variable in the `docker-compose.yml` file to the `opengist` service :
+| YAML Config Key | Environment Variable | Default value | Description |
+|-----------------------|--------------------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------|
+| log-level | OG_LOG_LEVEL | `warn` | Set the log level to one of the following: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `panic`. |
+| external-url | OG_EXTERNAL_URL | none | Public URL for the Git HTTP/SSH connection. If not set, uses the URL from the request. |
+| opengist-home | OG_OPENGIST_HOME | home directory | Path to the directory where Opengist stores its data. |
+| db-filename | OG_DB_FILENAME | `opengist.db` | Name of the SQLite database file. |
+| http.host | OG_HTTP_HOST | `0.0.0.0` | The host on which the HTTP server should bind. |
+| http.port | OG_HTTP_PORT | `6157` | The port on which the HTTP server should listen. |
+| http.git-enabled | OG_HTTP_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via HTTP. (`true` or `false`) |
+| http.tls-enabled | OG_HTTP_TLS_ENABLED | `false` | Enable or disable TLS for the HTTP server. (`true` or `false`) |
+| http.cert-file | OG_HTTP_CERT_FILE | none | Path to the TLS certificate file if TLS is enabled. |
+| http.key-file | OG_HTTP_KEY_FILE | none | Path to the TLS key file if TLS is enabled. |
+| ssh.git-enabled | OG_SSH_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via SSH. (`true` or `false`) |
+| ssh.host | OG_SSH_HOST | `0.0.0.0` | The host on which the SSH server should bind. |
+| ssh.port | OG_SSH_PORT | `2222` | The port on which the SSH server should listen. |
+| ssh.external-domain | OG_SSH_EXTERNAL_DOMAIN | none | Public domain for the Git SSH connection, if it has to be different from the HTTP one. If not set, uses the URL from the request. |
+| ssh.keygen-executable | OG_SSH_KEYGEN_EXECUTABLE | `ssh-keygen` | Path to the SSH key generation executable. |
+| github.client-key | OG_GITHUB_CLIENT_KEY | none | The client key for the GitHub OAuth application. |
+| github.secret | OG_GITHUB_SECRET | none | The secret for the GitHub OAuth application. |
+| gitea.client-key | OG_GITEA_CLIENT_KEY | none | The client key for the Gitea OAuth application. |
+| gitea.secret | OG_GITEA_SECRET | none | The secret for the Gitea OAuth application. |
+| gitea.url | OG_GITEA_URL | `https://gitea.com/` | The URL of the Gitea instance. |
-```diff
-environment:
- CONFIG: |
- log-level: info
- ssh.git-enabled: false
- # ...
-```
+
-### With binary
+### Configuration via YAML file
-Create a `config.yml` file (you can reuse this [one](config.yml)) and run Opengist binary with the `--config` flag :
+The configuration file must be specified when launching the application, using the `--config` flag followed by the path to your YAML file.
```shell
./opengist --config /path/to/config.yml
```
+You can start by copying and/or modifying the provided [config.yml](config.yml) file.
+
+### Configuration via Environment Variables
+
+Usage with Docker Compose :
+
+```yml
+services:
+ opengist:
+ # ...
+ environment:
+ OG_LOG_LEVEL: "info"
+ # etc.
+```
+Usage via command line :
+
+```shell
+OG_LOG_LEVEL=info ./opengist
+```
## Administration
@@ -142,7 +176,7 @@ server {
Then run :
```shell
-service nginx restart
+service nginx restart
```
### Use Fail2ban
@@ -172,7 +206,7 @@ port = anyport
Then run
```shell
-service fail2ban restart
+service fail2ban restart
```
## Configure OAuth
diff --git a/internal/config/config.go b/internal/config/config.go
index 6d3d205..bfd78d6 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -7,6 +7,7 @@ import (
"gopkg.in/yaml.v3"
"os"
"path/filepath"
+ "reflect"
"strconv"
"strings"
)
@@ -18,30 +19,30 @@ var C *config
// Not using nested structs because the library
// doesn't support dot notation in this case sadly
type config struct {
- LogLevel string `yaml:"log-level"`
- ExternalUrl string `yaml:"external-url"`
- OpengistHome string `yaml:"opengist-home"`
- DBFilename string `yaml:"db-filename"`
+ LogLevel string `yaml:"log-level" env:"OG_LOG_LEVEL"`
+ ExternalUrl string `yaml:"external-url" env:"OG_EXTERNAL_URL"`
+ OpengistHome string `yaml:"opengist-home" env:"OG_OPENGIST_HOME"`
+ DBFilename string `yaml:"db-filename" env:"OG_DB_FILENAME"`
- HttpHost string `yaml:"http.host"`
- HttpPort string `yaml:"http.port"`
- HttpGit bool `yaml:"http.git-enabled"`
- HttpTLSEnabled bool `yaml:"http.tls-enabled"`
- HttpCertFile string `yaml:"http.cert-file"`
- HttpKeyFile string `yaml:"http.key-file"`
+ HttpHost string `yaml:"http.host" env:"OG_HTTP_HOST"`
+ HttpPort string `yaml:"http.port" env:"OG_HTTP_PORT"`
+ HttpGit bool `yaml:"http.git-enabled" env:"OG_HTTP_GIT_ENABLED"`
+ HttpTLSEnabled bool `yaml:"http.tls-enabled" env:"OG_HTTP_TLS_ENABLED"`
+ HttpCertFile string `yaml:"http.cert-file" env:"OG_HTTP_CERT_FILE"`
+ HttpKeyFile string `yaml:"http.key-file" env:"OG_HTTP_KEY_FILE"`
- SshGit bool `yaml:"ssh.git-enabled"`
- SshHost string `yaml:"ssh.host"`
- SshPort string `yaml:"ssh.port"`
- SshExternalDomain string `yaml:"ssh.external-domain"`
- SshKeygen string `yaml:"ssh.keygen-executable"`
+ SshGit bool `yaml:"ssh.git-enabled" env:"OG_SSH_GIT_ENABLED"`
+ SshHost string `yaml:"ssh.host" env:"OG_SSH_HOST"`
+ SshPort string `yaml:"ssh.port" env:"OG_SSH_PORT"`
+ SshExternalDomain string `yaml:"ssh.external-domain" env:"OG_SSH_EXTERNAL_DOMAIN"`
+ SshKeygen string `yaml:"ssh.keygen-executable" env:"OG_SSH_KEYGEN_EXECUTABLE"`
- GithubClientKey string `yaml:"github.client-key"`
- GithubSecret string `yaml:"github.secret"`
+ GithubClientKey string `yaml:"github.client-key" env:"OG_GITHUB_CLIENT_KEY"`
+ GithubSecret string `yaml:"github.secret" env:"OG_GITHUB_SECRET"`
- GiteaClientKey string `yaml:"gitea.client-key"`
- GiteaSecret string `yaml:"gitea.secret"`
- GiteaUrl string `yaml:"gitea.url"`
+ GiteaClientKey string `yaml:"gitea.client-key" env:"OG_GITEA_CLIENT_KEY"`
+ GiteaSecret string `yaml:"gitea.secret" env:"OG_GITEA_SECRET"`
+ GiteaUrl string `yaml:"gitea.url" env:"OG_GITEA_URL"`
}
func configWithDefaults() (*config, error) {
@@ -77,37 +78,12 @@ func InitConfig(configPath string) error {
return err
}
- if configPath != "" {
- absolutePath, _ := filepath.Abs(configPath)
- absolutePath = filepath.Clean(absolutePath)
- file, err := os.Open(absolutePath)
- if err != nil {
- if !os.IsNotExist(err) {
- return err
- }
- fmt.Println("No YML config file found at " + absolutePath)
- } else {
- fmt.Println("Using config file: " + absolutePath)
-
- // Override default values with values from config.yml
- d := yaml.NewDecoder(file)
- if err = d.Decode(&c); err != nil {
- return err
- }
- defer file.Close()
- }
- } else {
- fmt.Println("No config file specified. Using default values.")
+ if err = loadConfigFromYaml(c, configPath); err != nil {
+ return err
}
- // Override default values with environment variables (as yaml)
- configEnv := os.Getenv("CONFIG")
- if configEnv != "" {
- fmt.Println("Using config from environment variable: CONFIG")
- d := yaml.NewDecoder(strings.NewReader(configEnv))
- if err = d.Decode(&c); err != nil {
- return err
- }
+ if err = loadConfigFromEnv(c); err != nil {
+ return err
}
C = c
@@ -159,3 +135,79 @@ func GetHomeDir() string {
absolutePath, _ := filepath.Abs(C.OpengistHome)
return filepath.Clean(absolutePath)
}
+
+func loadConfigFromYaml(c *config, configPath string) error {
+ if configPath != "" {
+ absolutePath, _ := filepath.Abs(configPath)
+ absolutePath = filepath.Clean(absolutePath)
+ file, err := os.Open(absolutePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return err
+ }
+ fmt.Println("No YAML config file found at " + absolutePath)
+ } else {
+ fmt.Println("Using YAML config file: " + absolutePath)
+
+ // Override default values with values from config.yml
+ d := yaml.NewDecoder(file)
+ if err = d.Decode(&c); err != nil {
+ return err
+ }
+ defer file.Close()
+ }
+ } else {
+ fmt.Println("No YAML config file specified.")
+ }
+
+ // Override default values with environment variables (as yaml)
+ configEnv := os.Getenv("CONFIG")
+ if configEnv != "" {
+ fmt.Println("Using config from environment variable: CONFIG")
+ d := yaml.NewDecoder(strings.NewReader(configEnv))
+ if err := d.Decode(&c); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func loadConfigFromEnv(c *config) error {
+ v := reflect.ValueOf(c).Elem()
+ var envVars []string
+
+ for i := 0; i < v.NumField(); i++ {
+ tag := v.Type().Field(i).Tag.Get("env")
+
+ if tag == "" {
+ continue
+ }
+
+ envValue := os.Getenv(strings.ToUpper(tag))
+ if envValue == "" {
+ continue
+ }
+
+ switch v.Field(i).Kind() {
+ case reflect.String:
+ v.Field(i).SetString(envValue)
+ case reflect.Bool:
+ boolVal, err := strconv.ParseBool(envValue)
+ if err != nil {
+ return err
+ }
+ v.Field(i).SetBool(boolVal)
+ }
+
+ envVars = append(envVars, tag)
+ }
+
+ if len(envVars) > 0 {
+ fmt.Println("Using environment variables config: " + strings.Join(envVars, ", "))
+ } else {
+ fmt.Println("No environment variables config specified.")
+ }
+
+ return nil
+}
diff --git a/internal/web/admin.go b/internal/web/admin.go
index 2992565..9111f74 100644
--- a/internal/web/admin.go
+++ b/internal/web/admin.go
@@ -185,15 +185,16 @@ func adminSyncReposFromDB(ctx echo.Context) error {
return redirect(ctx, "/admin-panel")
}
-func adminSettings(ctx echo.Context) error {
- setData(ctx, "title", "Admin Settings")
- setData(ctx, "htmlTitle", "Admin Settings - Admin panel")
- setData(ctx, "adminHeaderPage", "settings")
+func adminConfig(ctx echo.Context) error {
+ setData(ctx, "title", "Configuration")
+ setData(ctx, "htmlTitle", "Configuration - Admin panel")
+ setData(ctx, "adminHeaderPage", "config")
+ setData(ctx, "c", config.C)
- return html(ctx, "admin_settings.html")
+ return html(ctx, "admin_config.html")
}
-func adminSetSetting(ctx echo.Context) error {
+func adminSetConfig(ctx echo.Context) error {
key := ctx.FormValue("key")
value := ctx.FormValue("value")
diff --git a/internal/web/run.go b/internal/web/run.go
index dc5d0cc..00802c8 100644
--- a/internal/web/run.go
+++ b/internal/web/run.go
@@ -193,8 +193,8 @@ func Start() {
g2.POST("/gists/:gist/delete", adminGistDelete)
g2.POST("/sync-fs", adminSyncReposFromFS)
g2.POST("/sync-db", adminSyncReposFromDB)
- g2.GET("/settings", adminSettings)
- g2.PUT("/set-setting", adminSetSetting)
+ g2.GET("/configuration", adminConfig)
+ g2.PUT("/set-config", adminSetConfig)
}
g1.GET("/all", allGists, checkRequireLogin)
diff --git a/public/admin.ts b/public/admin.ts
index dfdf21a..cb7a69e 100644
--- a/public/admin.ts
+++ b/public/admin.ts
@@ -12,7 +12,7 @@ const setSetting = (key: string, value: string) => {
data.append('key', key);
data.append('value', value);
data.append('_csrf', ((document.getElementsByName('_csrf')[0] as HTMLInputElement).value));
- return fetch('/admin-panel/set-setting', {
+ return fetch('/admin-panel/set-config', {
method: 'PUT',
credentials: 'same-origin',
body: data,
diff --git a/public/style.css b/public/style.css
index 1c222b6..93c98af 100644
--- a/public/style.css
+++ b/public/style.css
@@ -140,3 +140,15 @@ table.csv-table thead tr th {
table.csv-table tbody td {
@apply border py-1.5 px-1 border-slate-200 dark:border-slate-800;
}
+
+dl.dl-config {
+ @apply grid grid-cols-3 text-sm;
+}
+
+dl.dl-config dt {
+ @apply col-span-1 text-gray-700 dark:text-slate-300 font-bold;
+}
+
+dl.dl-config dd {
+ @apply ml-1 col-span-2 break-words;
+}
diff --git a/templates/base/admin_header.html b/templates/base/admin_header.html
index 0082d67..cecf911 100644
--- a/templates/base/admin_header.html
+++ b/templates/base/admin_header.html
@@ -15,8 +15,8 @@
{{ else }} text-gray-600 dark:text-gray-400 hover:text-gray-400 dark:hover:text-slate-300 px-3 py-2 font-medium text-sm rounded-md {{ end }}" aria-current="page">Users
-
+
diff --git a/templates/pages/admin_config.html b/templates/pages/admin_config.html
new file mode 100644
index 0000000..0cdb077
--- /dev/null
+++ b/templates/pages/admin_config.html
@@ -0,0 +1,116 @@
+{{ template "header" .}}
+{{ template "admin_header" .}}
+
+
+
+
This configuration can be overridden by a YAML config file and/or environment variables.
+
+
+ - Log level
- {{ .c.LogLevel }}
+ - External URL
- {{ .c.ExternalUrl }}
+ - Opengist home
- {{ .c.OpengistHome }}
+ - DB filename
- {{ .c.DBFilename }}
+
+ - HTTP host
- {{ .c.HttpHost }}
+ - HTTP port
- {{ .c.HttpPort }}
+ - HTTP Git enabled
- {{ .c.HttpGit }}
+ - HTTP TLS enabled
- {{ .c.HttpTLSEnabled }}
+ - HTTP Cert file
- {{ .c.HttpCertFile }}
+ - HTTP Key file
- {{ .c.HttpKeyFile }}
+
+ - SSH Git enabled
- {{ .c.SshGit }}
+ - SSH host
- {{ .c.SshHost }}
+ - SSH port
- {{ .c.SshPort }}
+ - SSH external domain
- {{ .c.SshExternalDomain }}
+ - SSH Keygen
- {{ .c.SshKeygen }}
+
+ - Github Client key
- {{ .c.GithubClientKey }}
+ - Github Secret
- {{ .c.GithubSecret }}
+ - Gitea client Key
- {{ .c.GiteaClientKey }}
+ - Gitea Secret
- {{ .c.GiteaSecret }}
+ - Gitea URL
- {{ .c.GiteaUrl }}
+
+
+
+
+ -
+
+
+ Disable signup
+ Forbid the creation of new accounts.
+
+
+
+
+ -
+
+
+ Require login
+ Enforce users to be logged in to see gists.
+
+
+
+
+ -
+
+
+ Disable login form
+ Forbid logging in via the login form to force using OAuth providers instead.
+
+
+
+
+ -
+
+
+ Disable Gravatar
+ Disable the usage of Gravatar as an avatar provider.
+
+
+
+
+
+ {{ .csrfHtml }}
+
+
+
+
+
+{{ template "admin_footer" .}}
+{{ template "footer" .}}
diff --git a/templates/pages/admin_settings.html b/templates/pages/admin_settings.html
deleted file mode 100644
index ba2831d..0000000
--- a/templates/pages/admin_settings.html
+++ /dev/null
@@ -1,58 +0,0 @@
-{{ template "header" .}}
-{{ template "admin_header" .}}
-
-
-
-
- -
-
-
- Disable signup
- Forbid the creation of new accounts.
-
-
-
-
- -
-
-
- Require login
- Enforce users to be logged in to see gists.
-
-
-
-
- -
-
-
- Disable login form
- Forbid logging in via the login form to force using OAuth providers instead.
-
-
-
-
- -
-
-
- Disable Gravatar
- Disable the usage of Gravatar as an avatar provider.
-
-
-
-
-
- {{ .csrfHtml }}
-
-
-
-
-{{ template "admin_footer" .}}
-{{ template "footer" .}}