Fix reverse proxy subpath support (#192)

This commit is contained in:
Thomas Miceli 2024-01-02 05:11:49 +01:00
parent f52310a841
commit 4cb7dc2d30
14 changed files with 69 additions and 32 deletions

View file

@ -8,8 +8,7 @@ log-level: warn
# Set the log output to one or more of the following: `stdout`, `file`. Default: stdout,file
log-output: stdout,file
# Public URL for the Git HTTP/SSH connection.
# If not set, uses the URL from the request
# Public URL to access to Opengist
external-url:
# Directory where Opengist will store its data. Default: ~/.opengist/

View file

@ -1,6 +1,10 @@
# Use Nginx as a reverse proxy
Configure Nginx to proxy requests to Opengist. Here is an example configuration file :
Configure Nginx to proxy requests to Opengist. Here are example configuration file to use Opengist on a subdomain or on a subpath.
Make sure you set the base url for Opengist via the [configuration](/docs/configuration/cheat-sheet.md).
### Subdomain
```
server {
listen 80;
@ -16,7 +20,27 @@ server {
}
```
Then run :
```shell
service nginx restart
### Subpath
```
server {
listen 80;
server_name example.com;
location /opengist/ {
rewrite ^/opengist(/.*)$ $1 break;
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;
proxy_set_header X-Forwarded-Prefix /opengist;
}
}
```
---
To apply changes:
```shell
sudo systemctl restart nginx
```

View file

@ -4,7 +4,7 @@
|-----------------------|--------------------------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| log-level | OG_LOG_LEVEL | `warn` | Set the log level to one of the following: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, `panic`. |
| log-output | OG_LOG_OUTPUT | `stdout,file` | Set the log output to one or more of the following: `stdout`, `file`. |
| external-url | OG_EXTERNAL_URL | none | Public URL for the Git HTTP/SSH connection. If not set, uses the URL from the request. |
| external-url | OG_EXTERNAL_URL | none | Public URL to access to Opengist. |
| 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. |
| git.default-branch | OG_GIT_DEFAULT_BRANCH | none | Default branch name used by Opengist when initializing Git repositories. If not set, uses the Git default branch name. More info [here](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup#_new_default_branch) |

View file

@ -323,7 +323,12 @@ func gistJson(ctx echo.Context) error {
jsUrl, err := url.JoinPath(getData(ctx, "baseHttpUrl").(string), gist.User.Username, gist.Identifier()+".js")
if err != nil {
return errorRes(500, "Error joining url", err)
return errorRes(500, "Error joining js url", err)
}
cssUrl, err := url.JoinPath(getData(ctx, "baseHttpUrl").(string), manifestEntries["embed.css"].File)
if err != nil {
return errorRes(500, "Error joining css url", err)
}
return ctx.JSON(200, map[string]interface{}{
@ -337,7 +342,7 @@ func gistJson(ctx echo.Context) error {
"files": renderedFiles,
"embed": map[string]string{
"html": htmlbuf.String(),
"css": getData(ctx, "baseHttpUrl").(string) + asset("embed.css"),
"css": cssUrl,
"js": jsUrl,
"js_dark": jsUrl + "?dark",
},
@ -365,10 +370,15 @@ func gistJs(ctx echo.Context) error {
}
_ = w.Flush()
cssUrl, err := url.JoinPath(getData(ctx, "baseHttpUrl").(string), manifestEntries["embed.css"].File)
if err != nil {
return errorRes(500, "Error joining css url", err)
}
js := `document.write('<link rel="stylesheet" href="%s">')
document.write('%s')
`
js = fmt.Sprintf(js, getData(ctx, "baseHttpUrl").(string)+asset("embed.css"),
js = fmt.Sprintf(js, cssUrl,
strings.Replace(htmlbuf.String(), "\n", `\n`, -1))
ctx.Response().Header().Set("Content-Type", "application/javascript")
return plainText(ctx, 200, js)

View file

@ -8,13 +8,14 @@ document.addEventListener('DOMContentLoaded', () => {
});
const setSetting = (key: string, value: string) => {
const baseUrl = window.opengist_base_url || '';
const data = new URLSearchParams();
data.append('key', key);
data.append('value', value);
if (document.getElementsByName('_csrf').length !== 0) {
data.append('_csrf', ((document.getElementsByName('_csrf')[0] as HTMLInputElement).value));
}
return fetch('/admin-panel/set-config', {
return fetch(`${baseUrl}/admin-panel/set-config`, {
method: 'PUT',
credentials: 'same-origin',
body: data,

View file

@ -7,7 +7,10 @@
<meta name="robots" content="noindex, follow">
{{ end }}
<base href="{{ $.c.ExternalUrl }}" />
<script>
window.opengist_base_url = "{{ $.c.ExternalUrl }}";
const checkTheme = () => {
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
@ -85,7 +88,7 @@
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
</svg>
</div>
<form action="/search" method="GET">
<form action="{{ $.c.ExternalUrl }}/search" method="GET">
<input id="search" name="q" class="bg-white dark:bg-gray-900 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-200 dark:border-gray-700 rounded-md pl-10" placeholder="Search" type="search" value="{{ .searchQuery }}">
<input type="submit" hidden="hidden">
</form>
@ -211,7 +214,7 @@
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
</svg>
</div>
<form action="/search" method="GET">
<form action="{{ $.c.ExternalUrl }}/search" method="GET">
<input id="searchmobile" name="q" class="bg-white dark:bg-gray-900 shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-gray-200 dark:border-gray-700 rounded-md pl-10" placeholder="Search" type="search" value="{{.searchQuery}}">
<input type="submit" hidden="hidden">
</form>

View file

@ -9,7 +9,7 @@
</div>
<div class="lg:flex-row flex py-2 lg:py-0 lg:ml-auto">
{{ if .userLogged }}
<form id="like" class="flex items-center" method="post" action="/{{ .gist.User.Username }}/{{ .gist.Identifier }}/like?redirecturl={{ .currentUrl }}">
<form id="like" class="flex items-center" method="post" action="{{ $.c.ExternalUrl }}/{{ .gist.User.Username }}/{{ .gist.Identifier }}/like?redirecturl={{ .currentUrl }}">
{{ .csrfHtml }}
<button type="submit" class="focus-within:z-10 text-slate-700 dark:text-slate-300 relative inline-flex items-center space-x-2 rounded-l-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2 py-1.5 text-xs font-medium text-slate-700 dark:text-slate-300 hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 leading-3">
{{ if not .hasLiked }}
@ -29,7 +29,7 @@
</a>
</form>
{{ if ne .userLogged.ID .gist.User.ID }}
<form id="fork" class="ml-2 flex items-center " method="post" action="/{{ .gist.User.Username }}/{{ .gist.Identifier }}/fork">
<form id="fork" class="ml-2 flex items-center " method="post" action="{{ $.c.ExternalUrl }}/{{ .gist.User.Username }}/{{ .gist.Identifier }}/fork">
{{ .csrfHtml }}
<button type="submit" class="ml-auto focus-within:z-10 text-slate-700 dark:text-slate-300 relative inline-flex items-center space-x-2 rounded-l-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2 py-1.5 text-xs font-medium text-slate-700 dark:text-slate-300 hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 leading-3">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2">
@ -75,7 +75,7 @@
{{ .locale.Tr "gist.header.edit" }}
</a>
</div>
<form id="delete" onsubmit="return confirm('Are you sure you want to delete this gist ?')" class="ml-2 flex items-center" method="post" action="/{{ .gist.User.Username }}/{{ .gist.Identifier }}/delete">
<form id="delete" onsubmit="return confirm('Are you sure you want to delete this gist ?')" class="ml-2 flex items-center" method="post" action="{{ $.c.ExternalUrl }}/{{ .gist.User.Username }}/{{ .gist.Identifier }}/delete">
{{ .csrfHtml }}
<button type="submit" class="relative inline-flex items-center space-x-2 rounded-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2 py-1.5 text-xs font-medium text-rose-600 dark:text-rose-400 hover:bg-rose-500 hover:text-white dark:hover:bg-rose-600 hover:border-rose-600 dark:hover:border-rose-700 dark:hover:text-white focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">

View file

@ -28,7 +28,7 @@
<td class="whitespace-nowrap px-2 py-2 text-sm text-slate-700 dark:text-slate-300">{{ $gist.NbLikes }}</td>
<td class="whitespace-nowrap px-2 py-2 text-sm text-slate-700 dark:text-slate-300"><span class="moment-timestamp-date">{{ $gist.CreatedAt }}</span></td>
<td class="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
<form action="/admin-panel/gists/{{ $gist.ID }}/delete" method="POST" onsubmit="return confirm('{{ $.locale.Tr "admin.gists.delete_confirm" }}')">
<form action="{{ $.c.ExternalUrl }}/admin-panel/gists/{{ $gist.ID }}/delete" method="POST" onsubmit="return confirm('{{ $.locale.Tr "admin.gists.delete_confirm" }}')">
{{ $.csrfHtml }}
<button type="submit" class="text-rose-500 hover:text-rose-600">{{ $.locale.Tr "admin.delete" }}</button>
</form>

View file

@ -56,19 +56,19 @@
<span class="text-base font-bold leading-6 text-slate-700 dark:text-slate-300">{{ .locale.Tr "admin.actions" }}</span>
</div>
<div class="space-y-2">
<form action="/admin-panel/sync-fs" method="POST">
<form action="{{ $.c.ExternalUrl }}/admin-panel/sync-fs" method="POST">
{{ .csrfHtml }}
<button type="submit" {{ if .syncReposFromFS }}disabled="disabled"{{ end }} class="whitespace-nowrap text-slate-700 dark:text-slate-300{{ if .syncReposFromFS }} text-slate-500 cursor-not-allowed {{ end }}rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2.5 py-2 text-xs font-medium text-gray-700 dark:text-white shadow-sm hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:outline-none focus:ring-1 focus:border-primary-500 focus:ring-primary-500 leading-3">
{{ .locale.Tr "admin.actions.sync-fs" }}
</button>
</form>
<form action="/admin-panel/sync-db" method="POST">
<form action="{{ $.c.ExternalUrl }}/admin-panel/sync-db" method="POST">
{{ .csrfHtml }}
<button type="submit" {{ if .syncReposFromDB }}disabled="disabled"{{ end }} class="whitespace-nowrap text-slate-700 dark:text-slate-300{{ if .syncReposFromDB }} text-slate-500 cursor-not-allowed {{ end }}rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2.5 py-2 text-xs font-medium text-gray-700 dark:text-white shadow-sm hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:outline-none focus:ring-1 focus:border-primary-500 focus:ring-primary-500 leading-3">
{{ .locale.Tr "admin.actions.sync-db" }}
</button>
</form>
<form action="/admin-panel/gc-repos" method="POST">
<form action="{{ $.c.ExternalUrl }}/admin-panel/gc-repos" method="POST">
{{ .csrfHtml }}
<button type="submit" {{ if .gitGcRepos }}disabled="disabled"{{ end }} class="whitespace-nowrap text-slate-700 dark:text-slate-300{{ if .gitGcRepos }} text-slate-500 cursor-not-allowed {{ end }}rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2.5 py-2 text-xs font-medium text-gray-700 dark:text-white shadow-sm hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:outline-none focus:ring-1 focus:border-primary-500 focus:ring-primary-500 leading-3">
{{ .locale.Tr "admin.actions.git-gc" }}

View file

@ -20,7 +20,7 @@
<td class="whitespace-nowrap px-2 py-2 text-sm text-slate-700 dark:text-slate-300"><a href="{{ $.c.ExternalUrl }}/{{ $user.Username }}">{{ $user.Username }}</a></td>
<td class="whitespace-nowrap px-2 py-2 text-sm text-slate-700 dark:text-slate-300"><span class="moment-timestamp-date">{{ $user.CreatedAt }}</span></td>
<td class="relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
<form action="/admin-panel/users/{{ $user.ID }}/delete" method="POST" onsubmit="return confirm('{{ $.locale.Tr "admin.users.delete_confirm" }}')">
<form action="{{ $.c.ExternalUrl }}/admin-panel/users/{{ $user.ID }}/delete" method="POST" onsubmit="return confirm('{{ $.locale.Tr "admin.users.delete_confirm" }}')">
{{ $.csrfHtml }}
<button type="submit" class="text-rose-500 hover:text-rose-600">{{ $.locale.Tr "admin.delete" }}</button>
</form>

View file

@ -16,7 +16,7 @@
<div class="bg-white dark:bg-gray-900 rounded-md border border-1 border-gray-200 dark:border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
{{ if not .disableForm }}
<form class="space-y-6" action="#" method="post">
<form class="space-y-6" method="post">
<div>
<label for="username" class="block text-sm font-medium text-slate-700 dark:text-slate-300"> {{ .locale.Tr "auth.username" }} </label>
<div class="mt-1">

View file

@ -8,7 +8,7 @@
</header>
<main class="mt-4">
<form id="create" class="space-y-4" method="post" action="/">
<form id="create" class="space-y-4" method="post" action="{{ $.c.ExternalUrl }}/">
<div>
<p class="cursor-pointer select-none" id="gist-metadata-btn">Metadata ▼</p>
<div class="grid grid-cols-12 gap-x-4 mt-1 hidden" id="gist-metadata">

View file

@ -8,7 +8,7 @@
</h1>
</div>
<div class="lg:flex-row flex py-2 lg:py-0 lg:ml-auto">
<form id="visibility" class="flex items-center whitespace-nowrap" method="post" action="/{{ .gist.User.Username }}/{{ .gist.Identifier }}/visibility">
<form id="visibility" class="flex items-center whitespace-nowrap" method="post" action="{{ $.c.ExternalUrl }}/{{ .gist.User.Username }}/{{ .gist.Identifier }}/visibility">
{{ .csrfHtml }}
<button type="submit" class="ml-auto relative inline-flex items-center space-x-2 rounded-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2 py-1.5 text-xs font-medium text-slate-700 dark:text-slate-300 hover:bg-gray-100 dark:hover:bg-gray-700 hover:border-gray-500 hover:text-slate-700 dark:hover:text-slate-300 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 leading-3">
{{ if eq .gist.Private 2 }}
@ -24,7 +24,7 @@
{{ .locale.Tr "gist.edit.change-visibility" }} {{ visibilityStr .gist.Private.Next true }}
</button>
</form>
<form id="delete" onsubmit="return confirm('Are you sure you want to delete this gist ?')" class="ml-2 flex items-center" method="post" action="/{{ .gist.User.Username }}/{{ .gist.Identifier }}/delete">
<form id="delete" onsubmit="return confirm('Are you sure you want to delete this gist ?')" class="ml-2 flex items-center" method="post" action="{{ $.c.ExternalUrl }}/{{ .gist.User.Username }}/{{ .gist.Identifier }}/delete">
{{ .csrfHtml }}
<button type="submit" class="relative inline-flex items-center space-x-2 rounded-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 px-2 py-1.5 text-xs font-medium text-rose-600 dark:text-rose-400 hover:bg-rose-500 hover:text-white dark:hover:bg-rose-600 hover:border-rose-600 dark:hover:border-rose-700 dark:hover:text-white focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
@ -37,7 +37,7 @@
</div>
</header>
<main class="mt-4">
<form id="create" class="space-y-4" method="post" action="/{{ .gist.User.Username }}/{{ .gist.Identifier }}/edit">
<form id="create" class="space-y-4" method="post" action="{{ $.c.ExternalUrl }}/{{ .gist.User.Username }}/{{ .gist.Identifier }}/edit">
<div>
<p class="cursor-pointer select-none" id="gist-metadata-btn">Metadata ▼</p>
<div class="grid grid-cols-12 gap-x-4 mt-1 hidden" id="gist-metadata">

View file

@ -13,7 +13,7 @@
<h2 class="text-md font-bold text-slate-700 dark:text-slate-300">
{{ .locale.Tr "settings.change-username" }}
</h2>
<form class="space-y-6" action="/settings/username" method="post">
<form class="space-y-6" action="{{ $.c.ExternalUrl }}/settings/username" method="post">
<div>
<div class="mt-1">
<input id="username-change" name="username" type="text" required autocomplete="off" class="dark:bg-gray-800 appearance-none block w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md shadow-sm placeholder-gray-600 dark:placeholder-gray-400 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm">
@ -44,7 +44,7 @@
{{ .locale.Tr "settings.create-password-help" }}
{{end}}
</h3>
<form class="space-y-6" action="/settings/password" method="post">
<form class="space-y-6" action="{{ $.c.ExternalUrl }}/settings/password" method="post">
<div>
<label for="password-change" class="block text-sm font-medium text-slate-700 dark:text-slate-300"> {{ .locale.Tr "settings.password-label-title" }} </label>
<div class="mt-1">
@ -73,7 +73,7 @@
<h3 class="text-sm text-gray-600 dark:text-gray-400 italic mb-4">
{{ .locale.Tr "settings.email-help" }}
</h3>
<form class="space-y-6" action="/settings/email" method="post">
<form class="space-y-6" action="{{ $.c.ExternalUrl }}/settings/email" method="post">
<div>
<div class="mt-1">
<input id="email" name="email" value="{{ .userLogged.Email }}" type="email" required autocomplete="off" class="dark:bg-gray-800 appearance-none block w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md shadow-sm placeholder-gray-600 dark:placeholder-gray-400 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm">
@ -155,7 +155,7 @@
<h3 class="text-sm text-gray-600 dark:text-gray-400 italic mb-4">
{{ .locale.Tr "settings.add-ssh-key-help" }}
</h3>
<form class="space-y-6" action="/settings/ssh-keys" method="post">
<form class="space-y-6" action="{{ $.c.ExternalUrl }}/settings/ssh-keys" method="post">
<div>
<label for="title" class="block text-sm font-medium text-slate-700 dark:text-slate-300"> {{ .locale.Tr "settings.add-ssh-key-title" }} </label>
<div class="mt-1">
@ -194,7 +194,7 @@
<p class="text-xs text-gray-500 line-clamp-2">{{ $.locale.Tr "settings.ssh-key-last-used" }} <span class="moment-timestamp">{{ .LastUsedAt }}</span></p>
{{ end }}
</div>
<form action="/settings/ssh-keys/{{.ID}}" method="post" class="inline-block" onsubmit="return confirm('{{ $.locale.Tr "settings.delete-ssh-key-confirm" }}')">
<form action="{{ $.c.ExternalUrl }}/settings/ssh-keys/{{.ID}}" method="post" class="inline-block" onsubmit="return confirm('{{ $.locale.Tr "settings.delete-ssh-key-confirm" }}')">
<input type="hidden" name="_method" value="DELETE">
{{ $.csrfHtml }}
@ -213,7 +213,7 @@
<h2 class="text-md font-bold text-slate-700 dark:text-slate-300">
{{ .locale.Tr "settings.delete-account" }}
</h2>
<form class="space-y-6" action="/settings/account" method="post" onsubmit="return confirm('{{ .locale.Tr "settings.delete-account-confirm" }}')">
<form class="space-y-6" action="{{ $.c.ExternalUrl }}/settings/account" method="post" onsubmit="return confirm('{{ .locale.Tr "settings.delete-account-confirm" }}')">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent border-gray-200 dark:border-gray-700 text-sm font-medium rounded-md shadow-sm text-white dark:text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-rose-500 mt-2">{{ .locale.Tr "settings.delete-account" }}</button>
{{ .csrfHtml }}