Skip to content

Commit 936efaf

Browse files
authored
feat: #16 add feature proxy path URL (#43)
1 parent 363aaa4 commit 936efaf

File tree

11 files changed

+57
-39
lines changed

11 files changed

+57
-39
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,4 @@ cython_debug/
164164

165165
# Development environment
166166
run-dev.sh
167+
behind_a_proxy

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Doku can be configured using environment variables. You can set these either dir
8888
| LOG_LEVEL | Logging detail level (debug, info, warning, error, critical) | info |
8989
| SI | Use SI units (base 1000) instead of binary units (base 1024) | true |
9090
| BASIC_HTPASSWD | Path to the htpasswd file for basic authentication | /.htpasswd |
91+
| ROOT_PATH | URL prefix when served behind a proxy (e.g., "/doku") | "" |
9192
| SCAN_INTERVAL | How often to collect basic Docker usage data (in seconds) | 60 |
9293
| SCAN_LOGFILE_INTERVAL | How frequently to check container log sizes (in seconds) | 300 |
9394
| SCAN_BINDMOUNTS_INTERVAL | Time between bind mount scanning operations (in seconds) | 3600 |

app/main.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313
from server.state import lifespan
1414

1515

16-
app = FastAPI(lifespan=lifespan)
16+
app = FastAPI(lifespan=lifespan, root_path=settings.ROOT_PATH)
1717
app.mount('/static', StaticFiles(directory=settings.STATIC_DIR), name='static')
1818
app.include_router(site.router)
1919

2020

2121
@app.get('/', response_class=HTMLResponse, include_in_schema=False)
22-
async def index(_: Request):
23-
return RedirectResponse(url='/site', status_code=status.HTTP_303_SEE_OTHER)
22+
async def index(request: Request):
23+
url = request.url_for('dashboard')
24+
return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)
2425

2526

2627
def main():

app/server/router/site.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,49 +15,49 @@
1515
templates = Jinja2Templates(directory=settings.TEMPLATES_DIR)
1616

1717

18-
@router.get('/', response_class=HTMLResponse, include_in_schema=False)
18+
@router.get('/', name='dashboard', response_class=HTMLResponse, include_in_schema=False)
1919
def dashboard(request: Request, _: AuthRequired):
2020
ctx = context.dashboard()
2121
return templates.TemplateResponse(request=request, name='pages/dashboard.html', context=ctx)
2222

2323

24-
@router.get('/images/', response_class=HTMLResponse, include_in_schema=False)
24+
@router.get('/images/', name='images', response_class=HTMLResponse, include_in_schema=False)
2525
def images(request: Request, _: AuthRequired):
2626
ctx = context.images()
2727
return templates.TemplateResponse(request=request, name='pages/images.html', context=ctx)
2828

2929

30-
@router.get('/containers/', response_class=HTMLResponse, include_in_schema=False)
30+
@router.get('/containers/', name='containers', response_class=HTMLResponse, include_in_schema=False)
3131
def containers(request: Request, _: AuthRequired):
3232
ctx = context.containers()
3333
return templates.TemplateResponse(request=request, name='pages/containers.html', context=ctx)
3434

3535

36-
@router.get('/volumes/', response_class=HTMLResponse, include_in_schema=False)
36+
@router.get('/volumes/', name='volumes', response_class=HTMLResponse, include_in_schema=False)
3737
def volumes(request: Request, _: AuthRequired):
3838
ctx = context.volumes()
3939
return templates.TemplateResponse(request=request, name='pages/volumes.html', context=ctx)
4040

4141

42-
@router.get('/bind-mounts/', response_class=HTMLResponse, include_in_schema=False)
42+
@router.get('/bind-mounts/', name='bind_mounts', response_class=HTMLResponse, include_in_schema=False)
4343
def bind_mounts(request: Request, _: AuthRequired):
4444
ctx = context.bind_mounts()
4545
return templates.TemplateResponse(request=request, name='pages/bind_mounts.html', context=ctx)
4646

4747

48-
@router.get('/logs/', response_class=HTMLResponse, include_in_schema=False)
48+
@router.get('/logs/', name='logs', response_class=HTMLResponse, include_in_schema=False)
4949
def logs(request: Request, _: AuthRequired):
5050
ctx = context.logs()
5151
return templates.TemplateResponse(request=request, name='pages/logs.html', context=ctx)
5252

5353

54-
@router.get('/build-cache/', response_class=HTMLResponse, include_in_schema=False)
54+
@router.get('/build-cache/', name='build_cache', response_class=HTMLResponse, include_in_schema=False)
5555
def build_cache(request: Request, _: AuthRequired):
5656
ctx = context.build_cache()
5757
return templates.TemplateResponse(request=request, name='pages/build_cache.html', context=ctx)
5858

5959

60-
@router.get('/overlay2/', response_class=HTMLResponse, include_in_schema=False)
60+
@router.get('/overlay2/', name='overlay2', response_class=HTMLResponse, include_in_schema=False)
6161
def overlay2(request: Request, _: AuthRequired):
6262
ctx = context.overlay2()
6363
return templates.TemplateResponse(request=request, name='pages/overlay2.html', context=ctx)

app/settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class Settings(BaseSettings):
4040
basic_htpasswd: str = Field(
4141
alias='BASIC_HTPASSWD', default='/.htpasswd', description='Path to the htpasswd file for basic authentication'
4242
)
43+
root_path: str = Field(
44+
alias='ROOT_PATH', default='', description='URL prefix when served behind a proxy (e.g., "/doku")'
45+
)
4346

4447
# ssl settings
4548
ssl_keyfile: str = Field(alias='SSL_KEYFILE', default='/.ssl/key.pem')
@@ -153,6 +156,7 @@ def log_level_num(self) -> int:
153156
SI = _settings.si
154157
BASIC_HTPASSWD = _settings.basic_htpasswd
155158
AUTH_ENABLED = Path(BASIC_HTPASSWD).exists()
159+
ROOT_PATH = _settings.root_path
156160

157161
# ssl settings
158162
SSL_KEYFILE = _settings.ssl_keyfile
@@ -231,6 +235,7 @@ def to_string() -> str:
231235
'my_hostname',
232236
'si',
233237
'basic_htpasswd',
238+
'root_path',
234239
],
235240
'SSL settings': ['ssl_keyfile', 'ssl_keyfile_password', 'ssl_certfile', 'ssl_ciphers'],
236241
'Scan settings': [

app/static/site.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
/* Font declaration */
1818
@font-face {
1919
font-family: 'Lato';
20-
src: url('/static/assets/fonts/Lato-Regular.ttf') format('truetype');
20+
src: url('assets/fonts/Lato-Regular.ttf') format('truetype');
2121
font-weight: normal;
2222
font-style: normal;
2323
}

app/static/site.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function setActiveMenuItem() {
1515
// Find and activate current menu item
1616
document.querySelectorAll('.uk-navbar-nav li a').forEach(link => {
1717
const href = link.getAttribute('href').replace(/\/$/, '');
18-
if (href === currentPath) {
18+
if (href.endsWith(currentPath)) {
1919
link.parentElement.classList.add('uk-active');
2020
}
2121
});
@@ -78,16 +78,24 @@ document.addEventListener('DOMContentLoaded', function() {
7878
html.classList.add('uk-light', 'dark');
7979
localStorage.setItem('theme', 'dark');
8080
themeSwitcher.querySelector('#theme-switcher>i').setAttribute('class', 'bi-sun');
81-
changeChartTextColor(duChart, darkTextColor);
82-
changeChartTextColor(overlay2Chart, darkTextColor);
81+
if (duChart !== null) {
82+
changeChartTextColor(duChart, darkTextColor);
83+
}
84+
if (overlay2Chart !== null) {
85+
changeChartTextColor(overlay2Chart, darkTextColor);
86+
}
8387
}
8488

8589
function disableDarkMode() {
8690
html.classList.remove('uk-light', 'dark');
8791
localStorage.setItem('theme', 'light');
8892
themeSwitcher.querySelector('#theme-switcher>i').setAttribute('class', 'bi-moon');
89-
changeChartTextColor(duChart, lightTextColor);
90-
changeChartTextColor(overlay2Chart, lightTextColor);
93+
if (duChart !== null) {
94+
changeChartTextColor(duChart, lightTextColor);
95+
}
96+
if (overlay2Chart !== null) {
97+
changeChartTextColor(overlay2Chart, lightTextColor);
98+
}
9199
}
92100
});
93101

app/templates/base.html

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@
88
<meta charset="utf-8" />
99
<meta name="viewport" content="width=device-width, initial-scale=1" />
1010

11-
<link rel="icon" href="/static/assets/images/docker-mark-blue.svg" sizes="any" type="image/svg+xml">
12-
<link rel="stylesheet" href="/static/[email protected]/uikit.min.css" />
13-
<link rel="stylesheet" href="/static/[email protected]/datatables.min.css" />
14-
<link rel="stylesheet" href="/static/[email protected]/bootstrap-icons.min.css">
15-
<link rel="stylesheet" href="/static/site.css" />
16-
<script src="/static/[email protected]/jquery.min.js"></script>
17-
<script src="/static/[email protected]/uikit.min.js"></script>
18-
<script src="/static/[email protected]/uikit-icons.min.js"></script>
19-
<script src="/static/[email protected]/datatables.min.js"></script>
11+
<link rel="icon" href="{{ url_for('static', path='assets/images/docker-mark-blue.svg') }}" sizes="any" type="image/svg+xml">
12+
<link rel="stylesheet" href="{{ url_for('static', path='[email protected]/uikit.min.css') }}" />
13+
<link rel="stylesheet" href="{{ url_for('static', path='[email protected]/datatables.min.css') }}" />
14+
<link rel="stylesheet" href="{{ url_for('static', path='[email protected]/bootstrap-icons.min.css') }}" />
15+
<link rel="stylesheet" href="{{ url_for('static', path='site.css') }}" />
16+
<script src="{{ url_for('static', path='[email protected]/jquery.min.js') }}"></script>
17+
<script src="{{ url_for('static', path='[email protected]/uikit.min.js') }}"></script>
18+
<script src="{{ url_for('static', path='[email protected]/uikit-icons.min.js') }}"></script>
19+
<script src="{{ url_for('static', path='[email protected]/datatables.min.js') }}"></script>
2020
{% block extra_head %}
2121
{% endblock %}
22-
<script src="/static/site.js"></script>
22+
<script src="{{ url_for('static', path='site.js') }}"></script>
2323
{% endblock %}
2424
</head>
2525
<body data-si="{% if si %}true{% else %}false{% endif %}">

app/templates/navbar.html

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,34 @@
44

55
<!-- Navbar left side -->
66
<div class="uk-navbar-left">
7-
<a class="uk-navbar-item uk-logo" href="/" aria-label="Back to Home">
8-
<img src="/static/assets/images/docker-mark-white.svg" width="25" height="25" alt="Docker Logo" />
7+
<a class="uk-navbar-item uk-logo" href="{{ url_for('index') }}" aria-label="Back to Home">
8+
<img src="{{ url_for('static', path='assets/images/docker-mark-white.svg') }}" width="25" height="25" alt="Docker Logo" />
99
</a>
1010
<ul class="uk-navbar-nav">
1111
<li>
12-
<a href="/site/">Dashboard</a>
12+
<a href="{{ url_for('dashboard') }}">Dashboard</a>
1313
</li>
1414
<li>
15-
<a href="/site/images/">Images</a>
15+
<a href="{{ url_for('images') }}">Images</a>
1616
</li>
1717
<li>
18-
<a href="/site/containers/">Containers</a>
18+
<a href="{{ url_for('containers') }}">Containers</a>
1919
</li>
2020
<li>
21-
<a href="/site/volumes/">Volumes</a>
21+
<a href="{{ url_for('volumes') }}">Volumes</a>
2222
</li>
2323
<li>
24-
<a href="/site/bind-mounts/">Bind Mounts</a>
24+
<a href="{{ url_for('bind_mounts') }}">Bind Mounts</a>
2525
</li>
2626
<li>
27-
<a href="/site/logs/">Logs</a>
27+
<a href="{{ url_for('logs') }}">Logs</a>
2828
</li>
2929
<li>
30-
<a href="/site/build-cache/">Build Cache</a>
30+
<a href="{{ url_for('build_cache') }}">Build Cache</a>
3131
</li>
3232
<li>
33-
<a href="/site/overlay2/">Overlay2</a>
34-
</li>
33+
<a href="{{ url_for('overlay2') }}">Overlay2</a>
34+
</li>
3535
</ul>
3636
</div>
3737

app/templates/pages/dashboard.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends "base.html" %}
22

33
{% block extra_head %}
4-
<script src="/static/[email protected]/echarts.min.js"></script>
4+
<script src="{{ url_for('static', path='[email protected]/echarts.min.js') }}"></script>
55
{% endblock %}
66

77
{% block content %}

0 commit comments

Comments
 (0)