diff --git a/README.md b/README.md
index e943b33..1c2a3df 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,42 @@
# Opengist
+
+
+Opengist is a **self-hosted** pastebin **powered by Git**. All snippets are stored in a Git repository and can be
+read and/or modified using standard Git commands, or with the web interface.
+It is similiar to [GitHub Gist](https://gist.github.com/), but open-source and could be self-hosted.
+
+[Documentation](/docs) • [Demo](https://opengist.thomice.li)
+
+
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/thomiceli/opengist?sort=semver)
![License](https://img.shields.io/github/license/thomiceli/opengist?color=blue)
-![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/thomiceli/opengist/go.yml)
+[![Go CI](https://github.com/thomiceli/opengist/actions/workflows/go.yml/badge.svg)](https://github.com/thomiceli/opengist/actions/workflows/go.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/thomiceli/opengist)](https://goreportcard.com/report/github.com/thomiceli/opengist)
-A self-hosted pastebin **powered by Git**. [Try it here](https://opengist.thomice.li).
-
-* [Features](#features)
-* [Install](#install)
- * [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)
-* [Configure OAuth](#configure-oauth)
-* [License](#license)
## Features
* Create public, unlisted or private snippets
-* Clone / Pull / Push snippets **via Git** over HTTP or SSH
+* [Init](/docs/usage/init-via-git.md) / Clone / Pull / Push snippets **via Git** over HTTP or SSH
* Revisions history
* Syntax highlighting ; markdown & CSV support
* Like / Fork snippets
* Search for snippets ; browse users snippets, likes and forks
-* Editor with indentation mode & size ; drag and drop files
* Download raw files or as a ZIP archive
* OAuth2 login with GitHub, Gitea, and OpenID Connect
-* Avatars via Gravatar or OAuth2 providers
-* Light/Dark mode
-* Responsive UI
-* Enable or disable signups
* Restrict or unrestrict snippets visibility to anonymous users
-* Admin panel : delete users/gists; clean database/filesystem by syncing gists
-* SQLite database
-* Logging
* Docker support
+* [More...](/docs/index.md#features)
-#### Todo
-
-- [ ] Translation
-- [ ] Code/text search
-- [ ] Embed snippets
-- [ ] Tests
-- [ ] Filesystem/Redis support for user sessions
-- [ ] Have a cool logo
-
-## Install
+## Quick start
### With Docker
Docker [images](https://github.com/thomiceli/opengist/pkgs/container/opengist) are available for each release :
```shell
-docker pull ghcr.io/thomiceli/opengist:1.4
+docker pull ghcr.io/thomiceli/opengist:1
```
It can be used in a `docker-compose.yml` file :
@@ -105,167 +84,12 @@ make
Opengist is now running on port 6157, you can browse http://localhost:6157
-## Configuration
-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.
+## Documentation
-
-Configuration option list
+The documentation is available in [/docs](/docs) directory.
-| 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. |
-| sqlite.journal-mode | OG_SQLITE_JOURNAL_MODE | `WAL` | Set the journal mode for SQLite. More info [here](https://www.sqlite.org/pragma.html#pragma_journal_mode) |
-| 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`) |
-| 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. |
-| oidc.client-key | OG_OIDC_CLIENT_KEY | none | The client key for the OpenID application. |
-| oidc.secret | OG_OIDC_SECRET | none | The secret for the OpenID application. |
-| oidc.discovery-url | OG_OIDC_DISCOVERY_URL | none | Discovery endpoint of the OpenID provider. |
-
-
-
-### Configuration via YAML file
-
-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
-
-### Use Nginx as a reverse proxy
-
-Configure Nginx to proxy requests to Opengist. Here is an example configuration file :
-```
-server {
- listen 80;
- server_name opengist.example.com;
-
- location / {
- proxy_pass http://127.0.0.1:6157;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-}
-```
-
-Then run :
-```shell
-service nginx restart
-```
-
-### Use Fail2ban
-
-Fail2ban can be used to ban IPs that try to bruteforce the login page.
-Log level must be set at least to `warn`.
-
-Add this filter in `etc/fail2ban/filter.d/opengist.conf` :
-```ini
-[Definition]
-failregex = Invalid .* authentication attempt from
-ignoreregex =
-```
-
-Add this jail in `etc/fail2ban/jail.d/opengist.conf` :
-```ini
-[opengist]
-enabled = true
-filter = opengist
-logpath = /home/*/.opengist/log/opengist.log
-maxretry = 10
-findtime = 3600
-bantime = 600
-banaction = iptables-allports
-port = anyport
-```
-
-Then run
-```shell
-service fail2ban restart
-```
-
-## Configure OAuth
-
-Opengist can be configured to use OAuth to authenticate users, with GitHub, Gitea, or OpenID Connect.
-
-
-Integrate Github
-
-* Add a new OAuth app in your [Github account settings](https://github.com/settings/applications/new)
-* Set 'Authorization callback URL' to `http://opengist.domain/oauth/github/callback`
-* Copy the 'Client ID' and 'Client Secret' and add them to the configuration :
- ```yaml
- github.client-key:
- github.secret:
- ```
-
-
-
-Integrate Gitea
-
-* Add a new OAuth app in Application settings from the [Gitea instance](https://gitea.com/user/settings/applications)
-* Set 'Redirect URI' to `http://opengist.domain/oauth/gitea/callback`
-* Copy the 'Client ID' and 'Client Secret' and add them to the configuration :
- ```yaml
- gitea.client-key:
- gitea.secret:
- # URL of the Gitea instance. Default: https://gitea.com/
- gitea.url: http://localhost:3000
- ```
-
-
-
-Integrate OpenID
-
-* Add a new OAuth app in Application settings of your OIDC provider
-* Set 'Redirect URI' to `http://opengist.domain/oauth/openid-connect/callback`
-* Copy the 'Client ID', 'Client Secret', and the discovery endpoint, and add them to the configuration :
- ```yaml
- oidc.client-key:
- oidc.secret:
- # Discovery endpoint of the OpenID provider. Generally something like http://auth.example.com/.well-known/openid-configuration
- oidc.discovery-url: http://auth.example.com/.well-known/openid-configuration
- ```
-
## License
-Opengist is licensed under the [AGPL-3.0 license](LICENSE).
+Opengist is licensed under the [AGPL-3.0 license](/LICENSE).
diff --git a/docs/administration/fail2ban-setup.md b/docs/administration/fail2ban-setup.md
new file mode 100644
index 0000000..cb76ec3
--- /dev/null
+++ b/docs/administration/fail2ban-setup.md
@@ -0,0 +1,29 @@
+# Fail2ban setup
+
+Fail2ban can be used to ban IPs that try to bruteforce the login page.
+Log level must be set at least to `warn`.
+
+Add this filter in `etc/fail2ban/filter.d/opengist.conf` :
+```ini
+[Definition]
+failregex = Invalid .* authentication attempt from
+ignoreregex =
+```
+
+Add this jail in `etc/fail2ban/jail.d/opengist.conf` :
+```ini
+[opengist]
+enabled = true
+filter = opengist
+logpath = /home/*/.opengist/log/opengist.log
+maxretry = 10
+findtime = 3600
+bantime = 600
+banaction = iptables-allports
+port = anyport
+```
+
+Then run
+```shell
+service fail2ban restart
+```
diff --git a/docs/administration/nginx-reverse-proxy.md b/docs/administration/nginx-reverse-proxy.md
new file mode 100644
index 0000000..9accdb6
--- /dev/null
+++ b/docs/administration/nginx-reverse-proxy.md
@@ -0,0 +1,22 @@
+# Use Nginx as a reverse proxy
+
+Configure Nginx to proxy requests to Opengist. Here is an example configuration file :
+```
+server {
+ listen 80;
+ server_name opengist.example.com;
+
+ location / {
+ proxy_pass http://127.0.0.1:6157;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+}
+```
+
+Then run :
+```shell
+service nginx restart
+```
diff --git a/docs/administration/oauth-providers.md b/docs/administration/oauth-providers.md
new file mode 100644
index 0000000..9b1e73e
--- /dev/null
+++ b/docs/administration/oauth-providers.md
@@ -0,0 +1,39 @@
+# Use OAuth providers
+
+Opengist can be configured to use OAuth to authenticate users, with GitHub, Gitea, or OpenID Connect.
+
+## Github
+
+* Add a new OAuth app in your [Github account settings](https://github.com/settings/applications/new)
+* Set 'Authorization callback URL' to `http://opengist.domain/oauth/github/callback`
+* Copy the 'Client ID' and 'Client Secret' and add them to the [configuration](/docs/configuration/cheat-sheet.md) :
+ ```yaml
+ github.client-key:
+ github.secret:
+ ```
+
+
+## Gitea
+
+* Add a new OAuth app in Application settings from the [Gitea instance](https://gitea.com/user/settings/applications)
+* Set 'Redirect URI' to `http://opengist.domain/oauth/gitea/callback`
+* Copy the 'Client ID' and 'Client Secret' and add them to the [configuration](/docs/configuration/cheat-sheet.md) :
+ ```yaml
+ gitea.client-key:
+ gitea.secret:
+ # URL of the Gitea instance. Default: https://gitea.com/
+ gitea.url: http://localhost:3000
+ ```
+
+
+## OpenID Connect
+
+* Add a new OAuth app in Application settings of your OIDC provider
+* Set 'Redirect URI' to `http://opengist.domain/oauth/openid-connect/callback`
+* Copy the 'Client ID', 'Client Secret', and the discovery endpoint, and add them to the [configuration](/docs/configuration/cheat-sheet.md) :
+ ```yaml
+ oidc.client-key:
+ oidc.secret:
+ # Discovery endpoint of the OpenID provider. Generally something like http://auth.example.com/.well-known/openid-configuration
+ oidc.discovery-url: http://auth.example.com/.well-known/openid-configuration
+ ```
diff --git a/docs/configuration/cheat-sheet.md b/docs/configuration/cheat-sheet.md
new file mode 100644
index 0000000..5400570
--- /dev/null
+++ b/docs/configuration/cheat-sheet.md
@@ -0,0 +1,25 @@
+# Configuration Cheat Sheet
+
+| 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. |
+| sqlite.journal-mode | OG_SQLITE_JOURNAL_MODE | `WAL` | Set the journal mode for SQLite. More info [here](https://www.sqlite.org/pragma.html#pragma_journal_mode) |
+| 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`) |
+| 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. |
+| oidc.client-key | OG_OIDC_CLIENT_KEY | none | The client key for the OpenID application. |
+| oidc.secret | OG_OIDC_SECRET | none | The secret for the OpenID application. |
+| oidc.discovery-url | OG_OIDC_DISCOVERY_URL | none | Discovery endpoint of the OpenID provider. |
diff --git a/docs/configuration/index.md b/docs/configuration/index.md
new file mode 100644
index 0000000..1327aa4
--- /dev/null
+++ b/docs/configuration/index.md
@@ -0,0 +1,38 @@
+# Configuration
+
+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.
+
+The [configuration cheat sheet](cheat-sheet.md) lists all available configuration options.
+
+
+## Configuration via YAML file
+
+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
+```
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..09a0c88
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,52 @@
+# Opengist
+
+Opengist is a **self-hosted** pastebin **powered by Git**. All snippets are stored in a Git repository and can be
+read and/or modified using standard Git commands, or with the web interface.
+It is similiar to [GitHub Gist](https://gist.github.com/), but open-source and could be self-hosted.
+
+Written in [Go](https://go.dev), Opengist aims to be fast and easy to deploy.
+
+
+## Features
+
+* Create public, unlisted or private snippets
+* [Init](/docs/usage/init-via-git.md) / Clone / Pull / Push snippets **via Git** over HTTP or SSH
+* Revisions history
+* Syntax highlighting ; markdown & CSV support
+* Like / Fork snippets
+* Search for snippets ; browse users snippets, likes and forks
+* Editor with indentation mode & size ; drag and drop files
+* Download raw files or as a ZIP archive
+* OAuth2 login with GitHub, Gitea, and OpenID Connect
+* Avatars via Gravatar or OAuth2 providers
+* Light/Dark mode
+* Responsive UI
+* Enable or disable signups
+* Restrict or unrestrict snippets visibility to anonymous users
+* Admin panel :
+ * delete users/gists;
+ * clean database/filesystem by syncing gists
+ * run `git gc` for all repositories
+* SQLite database
+* Logging
+* Docker support
+
+
+## System requirements
+
+[Git](https://git-scm.com/download) is obviously required to run Opengist, as it's the main feature of the app.
+Version **2.20** or later is recommended as the app has not been tested with older Git versions.
+
+[OpenSSH](https://www.openssh.com/) suite if you wish to use Git over SSH
+
+
+## Components
+
+* Backend Web Framework: [Echo](https://echo.labstack.com/)
+* ORM: [GORM](https://gorm.io/)
+* Frontend libraries:
+ * [Tailwind CSS](https://tailwindcss.com/)
+ * [CodeMirror](https://codemirror.net/)
+ * [Day.js](https://day.js.org/)
+ * [highlight.js](https://highlightjs.org/)
+ * and [others](/package.json)
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000..3def026
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,59 @@
+# Installation
+
+## With Docker
+
+Docker [images](https://github.com/thomiceli/opengist/pkgs/container/opengist) are available for each release :
+
+```shell
+docker pull ghcr.io/thomiceli/opengist:1
+```
+
+It can be used in a `docker-compose.yml` file :
+
+1. Create a `docker-compose.yml` file with the following content
+2. Run `docker compose up -d`
+3. Opengist is now running on port 6157, you can browse http://localhost:6157
+
+```yml
+version: "3"
+
+services:
+ opengist:
+ image: ghcr.io/thomiceli/opengist:1
+ container_name: opengist
+ restart: unless-stopped
+ ports:
+ - "6157:6157" # HTTP port
+ - "2222:2222" # SSH port, can be removed if you don't use SSH
+ volumes:
+ - "$HOME/.opengist:/opengist"
+```
+
+You can define which user/group should run the container and own the files by setting the `UID` and `GID` environment
+variables :
+
+```yml
+services:
+ opengist:
+ # ...
+ environment:
+ UID: 1001
+ GID: 1001
+```
+
+
+## From source
+
+Requirements :
+* [Git](https://git-scm.com/downloads) (2.20+)
+* [Go](https://go.dev/doc/install) (1.20+)
+* [Node.js](https://nodejs.org/en/download/) (16+)
+
+```shell
+git clone https://github.com/thomiceli/opengist
+cd opengist
+make
+./opengist
+```
+
+Opengist is now running on port 6157, you can browse http://localhost:6157
diff --git a/docs/usage/init-via-git.md b/docs/usage/init-via-git.md
new file mode 100644
index 0000000..231e698
--- /dev/null
+++ b/docs/usage/init-via-git.md
@@ -0,0 +1,42 @@
+# Init Gists via Git
+
+Opengist allows you to create new snippets via Git over HTTP.
+
+Simply init a new Git repository where your file(s) is/are located:
+
+```shell
+git init
+git add .
+git commit -m "My cool snippet"
+```
+
+Then add this Opengist special remote URL and push your changes:
+
+```shell
+git remote add origin http://localhost:6157/init
+
+git push -u origin master
+```
+
+Log in with your Opengist account credentials, and your snippet will be created at the specified URL:
+
+```shell
+Username for 'http://localhost:6157': thomas
+Password for 'http://thomas@localhost:6157':
+Enumerating objects: 3, done.
+Counting objects: 100% (3/3), done.
+Delta compression using up to 8 threads
+Compressing objects: 100% (2/2), done.
+Writing objects: 100% (3/3), 416 bytes | 416.00 KiB/s, done.
+Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
+remote:
+remote: Your new repository has been created here: http://localhost:6157/thomas/6051e930f140429f9a2f3bb1fa101066
+remote:
+remote: If you want to keep working with your gist, you could set the remote URL via:
+remote: git remote set-url origin http://localhost:6157/thomas/6051e930f140429f9a2f3bb1fa101066
+remote:
+To http://localhost:6157/init
+ * [new branch] master -> master
+```
+
+https://github.com/thomiceli/opengist/assets/27960254/3fe1a0ba-b638-4928-83a1-f38e46fea066
diff --git a/templates/pages/admin_config.html b/templates/pages/admin_config.html
index 0c98a87..5ea3af6 100644
--- a/templates/pages/admin_config.html
+++ b/templates/pages/admin_config.html
@@ -3,7 +3,7 @@