Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
153 commits
Select commit Hold shift + click to select a range
e429738
feat: generate dynamic proxy dashboard
vitormattos Sep 12, 2026
abf3875
feat: serve proxy dashboard and friendly not found page
vitormattos Sep 12, 2026
dbfcbd0
refactor: retire static localhost virtual host
vitormattos Sep 12, 2026
98734e5
feat: add dynamic proxy dashboard services
vitormattos Sep 12, 2026
2e2a671
fix: reconcile shared proxy services when reused
vitormattos Sep 12, 2026
672d7b5
fix: route unknown localhost hosts to dashboard
vitormattos Sep 12, 2026
06b2b41
fix: use wildcard host for proxy dashboard fallback
vitormattos Sep 12, 2026
f379db0
fix: keep proxy dashboard routes in sync
vitormattos Sep 12, 2026
78b0588
feat: extend nginx-proxy for dynamic dashboard
vitormattos Sep 12, 2026
22a12cc
feat: watch Docker state for dashboard
vitormattos Sep 12, 2026
2f6e956
feat: render dashboard from Docker metadata
vitormattos Sep 12, 2026
73b9272
refactor: serve dashboard inside nginx-proxy
vitormattos Sep 12, 2026
db330e1
refactor: keep dashboard inside proxy service
vitormattos Sep 12, 2026
912d5a5
refactor: let proxy own dashboard assets
vitormattos Sep 12, 2026
a4e1d6d
refactor: remove dashboard updater service script
vitormattos Sep 12, 2026
0c64f90
refactor: remove legacy localhost proxy config
vitormattos Sep 12, 2026
288bdb1
refactor: remove static proxy dashboard
vitormattos Sep 12, 2026
84dba5d
refactor: keep generated proxy files ephemeral
vitormattos Sep 12, 2026
3d724a0
feat: generate proxy config and dashboard together
vitormattos Sep 12, 2026
06ee3cb
feat: serve dashboard from localhost vhost
vitormattos Sep 12, 2026
3e6e45b
feat: render dashboard for unknown localhost hosts
vitormattos Sep 12, 2026
1d2224f
feat: return 404 for unknown localhost hosts
vitormattos Sep 12, 2026
14f796b
refactor: run dashboard generator from stock proxy image
vitormattos Sep 12, 2026
3ae5872
refactor: run shared proxy without local build
vitormattos Sep 12, 2026
f8a49d3
refactor: run proxy coordinator without local build
vitormattos Sep 12, 2026
fb95f2c
feat: release shared proxy with last environment
vitormattos Sep 12, 2026
6e35c04
refactor: remove local proxy image build
vitormattos Sep 12, 2026
bac090a
refactor: remove unused docker-gen config
vitormattos Sep 12, 2026
ed58a8f
refactor: remove separate dashboard server config
vitormattos Sep 12, 2026
245f9da
refactor: remove local coordinator image build
vitormattos Sep 12, 2026
3a0aead
refactor: keep shared proxy network external
vitormattos Sep 12, 2026
2215334
fix: ensure shared proxy network exists
vitormattos Sep 12, 2026
d4fa66f
feat: generate proxy config and dashboard together
vitormattos Sep 12, 2026
fe7fc4f
refactor: use one docker-gen watcher
vitormattos Sep 12, 2026
ffbd368
refactor: generate dashboard nginx config
vitormattos Sep 12, 2026
b13e5ba
refactor: keep dashboard generation in one watcher
vitormattos Sep 12, 2026
b017983
refactor: remove redundant dashboard nginx server
vitormattos Sep 12, 2026
3bc6480
fix: release shared proxy with coordinator leases
vitormattos Sep 12, 2026
1443096
fix: serve dashboard for unknown localhost hosts
vitormattos Sep 12, 2026
eaad008
fix: handle localhost dashboard directly in nginx
vitormattos Sep 12, 2026
4f82dee
refactor: serve dashboard from proxy config
vitormattos Sep 12, 2026
e3ad06e
revert: keep wildcard host managed by nginx-proxy
vitormattos Sep 12, 2026
95d910e
revert: remove direct dashboard server
vitormattos Sep 12, 2026
9403f3d
fix: render friendly wildcard 404 page
vitormattos Sep 12, 2026
aeb6430
fix: preserve 404 status for wildcard dashboard
vitormattos Sep 12, 2026
a29ee72
fix: ignore current proxy lease during shutdown
vitormattos Sep 12, 2026
da11167
refactor: extract proxy coordinator common helpers
vitormattos Sep 12, 2026
e403b5f
refactor: extract shared proxy infrastructure logic
vitormattos Sep 12, 2026
2bf03bf
refactor: extract shared proxy asset installation
vitormattos Sep 12, 2026
dd523b9
refactor: extract proxied service coordination
vitormattos Sep 12, 2026
3dca7da
refactor: extract shared proxy lease lifecycle
vitormattos Sep 12, 2026
11a3f34
refactor: reduce proxy coordinator to orchestration
vitormattos Sep 12, 2026
13fbd34
refactor: mount proxy coordinator modules
vitormattos Sep 12, 2026
b86d87b
refactor: make compose files injectable for tests
vitormattos Sep 12, 2026
2aac791
test: cover shared proxy lease decisions
vitormattos Sep 12, 2026
2754c1a
test: cover shared proxy infrastructure decisions
vitormattos Sep 12, 2026
e7f6532
test: cover proxied service coordination
vitormattos Sep 12, 2026
a734a23
test: add minimal proxy integration fixture
vitormattos Sep 12, 2026
6ab5227
test: cover shared proxy lifecycle and routing
vitormattos Sep 12, 2026
1b4a856
ci: test shared proxy lifecycle with Bats
vitormattos Sep 12, 2026
5be1aa5
fix: make proxy coordinator shellcheck clean
vitormattos Sep 12, 2026
59bf8af
fix: document sourced proxy globals for shellcheck
vitormattos Sep 12, 2026
511c316
fix: mark sourced proxy globals for infrastructure module
vitormattos Sep 12, 2026
4bca3fe
fix: mark sourced proxy globals for asset module
vitormattos Sep 12, 2026
093187e
fix: mark sourced proxy globals for service module
vitormattos Sep 12, 2026
943da4f
fix: mark sourced proxy globals for lease module
vitormattos Sep 12, 2026
db23c2d
fix: keep proxy test fixture valid inside coordinator
vitormattos Sep 12, 2026
9d5c1db
fix: bound shared proxy shutdown time
vitormattos Sep 12, 2026
d1fa931
test: cover Ctrl+C proxy shutdown
vitormattos Sep 12, 2026
eae012f
test: expect bounded proxy shutdown
vitormattos Sep 12, 2026
b55dbba
refactor: make proxy lease release idempotent
vitormattos Sep 12, 2026
a752bcd
fix: support reliable proxy pre-stop cleanup
vitormattos Sep 12, 2026
d751cfe
fix: make local shutdown fast and deterministic
vitormattos Sep 12, 2026
6f4156a
test: exercise coordinator pre-stop cleanup
vitormattos Sep 12, 2026
c63e3ad
test: cover idempotent proxy lease release
vitormattos Sep 12, 2026
96ffa12
test: bound attached compose shutdown time
vitormattos Sep 12, 2026
fa2012d
fix: keep compose lifecycle hooks backward compatible
vitormattos Sep 12, 2026
b53ff0d
test: avoid unsupported compose lifecycle hook
vitormattos Sep 12, 2026
e68db42
fix: process coordinator shutdown signals promptly
vitormattos Sep 12, 2026
18eaf67
fix: run proxy coordinator with init
vitormattos Sep 12, 2026
ff0122c
test: run proxy coordinator with init
vitormattos Sep 12, 2026
1e8ab58
feat: generate proxy runtime diagnostics
vitormattos Sep 12, 2026
00353cb
feat: publish proxy runtime diagnostics
vitormattos Sep 12, 2026
55bee39
feat: serve proxy runtime diagnostics
vitormattos Sep 12, 2026
d1bdf65
feat: show runtime diagnostics on proxy dashboard
vitormattos Sep 12, 2026
49f1374
test: cover proxy runtime diagnostics
vitormattos Sep 12, 2026
357cd02
ci: run proxy runtime diagnostic tests
vitormattos Sep 12, 2026
b6701a5
test: cover proxy runtime diagnostics endpoint
vitormattos Sep 12, 2026
0cfea3e
fix: report host runtime versions only
vitormattos Sep 12, 2026
031a601
test: use host runtime versions in diagnostics
vitormattos Sep 12, 2026
5c5f9a0
fix: show only host runtime versions
vitormattos Sep 12, 2026
d1be7b7
test: assert host runtime diagnostics only
vitormattos Sep 12, 2026
70f0747
fix: publish runtime diagnostics from running proxy
vitormattos Sep 12, 2026
59c22d8
fix: publish diagnostics after proxy startup
vitormattos Sep 12, 2026
36a9bd1
refactor: remove shutdown workarounds and pin default tools
vitormattos Sep 12, 2026
604606f
refactor: remove coordinator shutdown test workarounds
vitormattos Sep 12, 2026
5684952
test: remove proxy fixture shutdown workarounds
vitormattos Sep 12, 2026
8bb49c5
chore: pin proxy helper image
vitormattos Sep 12, 2026
f699735
chore: pin shared proxy images
vitormattos Sep 12, 2026
ba21959
ci: update compose images with dependabot
vitormattos Sep 12, 2026
b051320
test: enforce proxy compose runtime policy
vitormattos Sep 12, 2026
0d1b698
ci: enforce proxy compose runtime policy
vitormattos Sep 12, 2026
5629a09
refactor: avoid shellcheck suppression in diagnostics
vitormattos Sep 12, 2026
cdd9965
refactor: make proxy module dependencies explicit
vitormattos Sep 12, 2026
a175def
refactor: replace shared proxy globals with accessors
vitormattos Sep 12, 2026
68b8416
refactor: export coordinator context for proxy modules
vitormattos Sep 12, 2026
2700db8
refactor: remove shared globals from proxy infrastructure
vitormattos Sep 12, 2026
cfd3bd3
refactor: remove shared globals from proxy services
vitormattos Sep 12, 2026
6d33c65
refactor: remove shared globals from proxy assets
vitormattos Sep 12, 2026
f7ca200
refactor: avoid nested shell expansion for proxy assets
vitormattos Sep 12, 2026
53d8fff
refactor: remove shared globals from proxy lease
vitormattos Sep 12, 2026
f78ce8d
refactor: export coordinator container context
vitormattos Sep 12, 2026
3e89ec8
test: use explicit proxy lease context
vitormattos Sep 12, 2026
5cd9fa5
test: use proxy network accessor in service tests
vitormattos Sep 12, 2026
12d3add
test: use proxy network accessor in infrastructure tests
vitormattos Sep 12, 2026
161bd7f
ci: analyze shell modules together
vitormattos Sep 12, 2026
b6025cc
ci: make shell source paths explicit
vitormattos Sep 12, 2026
2123988
ci: follow sourced shell modules
vitormattos Sep 12, 2026
ce36dce
ci: inspect runtime image digests
vitormattos Sep 12, 2026
7d54755
build: pin default runtime images by digest
vitormattos Sep 12, 2026
a6f6f5d
build: pin shared proxy images by digest
vitormattos Sep 12, 2026
fbcc795
test: pin proxy fixture images by digest
vitormattos Sep 12, 2026
34524f4
build: pin proxy helper image by digest
vitormattos Sep 12, 2026
83f18e2
build: pin database images by digest
vitormattos Sep 12, 2026
8c5a17f
test: require digest-pinned runtime images
vitormattos Sep 12, 2026
9d59c57
ci: remove temporary image digest inspection
vitormattos Sep 12, 2026
a190d67
refactor: expose container image reference
vitormattos Sep 12, 2026
1efade3
refactor: reuse coordinator image for proxy helpers
vitormattos Sep 12, 2026
91ff3dd
test: keep proxy helper image single sourced
vitormattos Sep 12, 2026
d541a86
feat: report long Compose project names
vitormattos Sep 12, 2026
6405003
test: cover Compose project name hints
vitormattos Sep 12, 2026
32dd07a
feat: show Compose project name hints
vitormattos Sep 12, 2026
e21ef44
feat: add contributor links to proxy dashboard
vitormattos Sep 12, 2026
32fe6b3
test: verify contributor links on proxy dashboard
vitormattos Sep 12, 2026
cda5594
refactor: present dashboard as development environment
vitormattos Sep 12, 2026
672306b
test: match development environment branding
vitormattos Sep 12, 2026
c888125
feat: emphasize unknown environment errors
vitormattos Sep 12, 2026
adf2dca
fix: bind development proxy to loopback by default
vitormattos Sep 12, 2026
c88abd9
fix: pass proxy host settings to coordinator
vitormattos Sep 12, 2026
fa1160c
docs: document proxy bind override
vitormattos Sep 12, 2026
23f2036
ci: pin proxy test actions by commit
vitormattos Sep 12, 2026
540eb9e
ci: pin shellcheck action and version
vitormattos Sep 12, 2026
9119500
ci: pin nginx build actions by commit
vitormattos Sep 12, 2026
7f22e34
ci: pin php build actions by commit
vitormattos Sep 12, 2026
b5f32bf
test: enforce proxy security policies
vitormattos Sep 12, 2026
d55f740
test: cover proxy network exposure boundaries
vitormattos Sep 12, 2026
594745d
fix: restore optional development services
vitormattos Sep 12, 2026
e0ce7e8
fix: scope proxy bind address setting
vitormattos Sep 12, 2026
a0a3da7
fix: scope database bind address settings
vitormattos Sep 12, 2026
4075b28
fix: pass scoped proxy bind setting
vitormattos Sep 12, 2026
d9c6f89
docs: document scoped bind settings and Docker trust
vitormattos Sep 12, 2026
df20a41
test: enforce scoped network bind settings
vitormattos Sep 12, 2026
9787574
test: make bind policy assertions order independent
vitormattos Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .docker/Dockerfile.proxy-coordinator

This file was deleted.

8 changes: 4 additions & 4 deletions .docker/database-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ services:
mysql:
# Pin to 8.4: mysql:latest currently resolves to 9.x, and Nextcloud autoinstall
# runs with the application user, which cannot read mysql.component there.
image: mysql:8.4
image: mysql:8.4@sha256:b3b90af2a6552ae30c266fdb7d5dd55f3afb72404bb78d37fe8a23eb857fd3fb
volumes:
- ../volumes/mysql/init:/docker-entrypoint-initdb.d
- ../volumes/mysql/data:/var/lib/mysql
ports:
- target: 3306
published: "3306-3325"
host_ip: ${IP_BIND:-127.0.0.1}
host_ip: ${MYSQL_IP_BIND:-127.0.0.1}
command: ["mysqld"]
environment:
- TZ=${TZ:-CET}
Expand All @@ -18,13 +18,13 @@ services:
- MYSQL_USER=${MYSQL_USER:-nextcloud}
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-nextcloud}
pgsql:
image: postgres:13-alpine
image: postgres:13-alpine@sha256:fb9065b6e3e213bdc07edd372a5b2a26245840b7fb65d1fd8b6700106d51805c
volumes:
- ../volumes/postgres/data:/var/lib/postgresql/data
ports:
- target: 5432
published: "5432-5451"
host_ip: ${IP_BIND:-127.0.0.1}
host_ip: ${POSTGRES_IP_BIND:-127.0.0.1}
command: ["postgres", "-c", "log_statement=all"]
environment:
- POSTGRES_DB=${POSTGRES_DB:-nextcloud}
Expand Down
25 changes: 13 additions & 12 deletions .docker/docker-compose.proxy.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
services:
nginx-proxy:
image: nginxproxy/nginx-proxy:1.11-alpine
image: nginxproxy/nginx-proxy:1.11.6-alpine@sha256:99376e95c8754547213d411222a2c4dca046d974e753ae2a1c9aef09b2c03364
container_name: librecode-dev-proxy
restart: unless-stopped
labels:
coop.librecode.dev-proxy: "true"
working_dir: /dashboard
command: ["forego", "start", "-r"]
ports:
- "80:80"
- "443:443"
- "${PROXY_IP_BIND:-127.0.0.1}:80:80"
- "${PROXY_IP_BIND:-127.0.0.1}:443:443"
volumes:
- ${DOCKER_SOCKET:-/var/run/docker.sock}:/tmp/docker.sock:ro
- librecode-dev-proxy-vhost:/etc/nginx/vhost.d
- librecode-dev-proxy-conf:/etc/nginx/conf.d
- librecode-dev-proxy-assets:/dashboard:ro
- librecode-dev-proxy-log:/etc/nginx/log
- librecode-dev-proxy-certs:/etc/nginx/certs:ro
- librecode-dev-proxy-html:/usr/share/nginx/html
environment:
- ENABLE_IPV6=true
- VIRTUAL_HOST=localhost,*.localhost
- VIRTUAL_PORT=80
- SELF_SIGNED_HOST=localhost,*.localhost
networks:
- proxy

ssl-companion:
image: sebastienheyd/self-signed-proxy-companion:latest
image: sebastienheyd/self-signed-proxy-companion:1.1.0@sha256:dd698341f804aaffe2974931a185aa088561d76094e040e374847f3a64004d43
container_name: librecode-dev-proxy-ssl-companion
restart: unless-stopped
labels:
coop.librecode.dev-proxy-companion: "true"
volumes:
Expand All @@ -37,15 +39,14 @@ services:
networks:
proxy:
name: librecode-dev-proxy
external: true

volumes:
librecode-dev-proxy-vhost:
name: librecode-dev-proxy-vhost
librecode-dev-proxy-conf:
name: librecode-dev-proxy-conf
librecode-dev-proxy-assets:
name: librecode-dev-proxy-assets
librecode-dev-proxy-log:
name: librecode-dev-proxy-log
librecode-dev-proxy-certs:
name: librecode-dev-proxy-certs
librecode-dev-proxy-html:
name: librecode-dev-proxy-html
6 changes: 6 additions & 0 deletions .docker/nginx-proxy/*.localhost
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
root /usr/share/nginx/html;
error_page 404 =404 @librecode_dashboard_404;

location @librecode_dashboard_404 {
try_files /index.html =404;
}
1 change: 1 addition & 0 deletions .docker/nginx-proxy/*.localhost_location_override
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return 404;
2 changes: 2 additions & 0 deletions .docker/nginx-proxy/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dockergen: docker-gen -config /dashboard/docker-gen.cfg -event-filter event=connect -event-filter event=disconnect
nginx: nginx -g "daemon off;"
250 changes: 250 additions & 0 deletions .docker/nginx-proxy/dashboard.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
{{- $routes := list -}}
{{- range $hostname, $containers := groupByMulti . "Env.VIRTUAL_HOST" "," -}}
{{- $hostname = trim $hostname -}}
{{- if or (eq $hostname "") (eq $hostname "localhost") (eq $hostname "*.localhost") -}}
{{- continue -}}
{{- end -}}

{{- range $container := $containers -}}
{{- $onProxyNetwork := false -}}
{{- range $network := $container.Networks -}}
{{- if eq $network.Name "librecode-dev-proxy" -}}
{{- $onProxyNetwork = true -}}
{{- end -}}
{{- end -}}
{{- if not $onProxyNetwork -}}
{{- continue -}}
{{- end -}}

{{- $project := index $container.Labels "com.docker.compose.project" -}}
{{- $service := index $container.Labels "com.docker.compose.service" -}}
{{- if not $project -}}
{{- $project = "Docker" -}}
{{- end -}}
{{- if not $service -}}
{{- $service = "Service" -}}
{{- end -}}

{{- $routes = append $routes (dict "host" $hostname "project" $project "service" $service) -}}
{{- break -}}
{{- end -}}
{{- end -}}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LibreCode Nextcloud Development Environment</title>
<style>
:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
body { margin: 0; min-height: 100vh; display: grid; place-items: start center; background: Canvas; color: CanvasText; }
main { width: min(760px, calc(100% - 48px)); margin: 26px 0; padding: 32px; border: 1px solid color-mix(in srgb, CanvasText 20%, transparent); border-radius: 12px; }
h1 { margin-top: 0; font-size: 1.5rem; }
h2 { margin-top: 2rem; font-size: 1.15rem; }
h3 { margin: 1.5rem 0 .5rem; font-size: 1rem; }
ul { padding-left: 1.25rem; }
li { margin: .45rem 0; }
a { color: LinkText; }
code { padding: .15em .4em; border-radius: 4px; background: color-mix(in srgb, CanvasText 10%, transparent); }
.not-found, .runtime-warning, .runtime-hint { padding: 1rem; border: 1px solid color-mix(in srgb, CanvasText 24%, transparent); border-radius: 8px; }
.not-found { border-color: #c9362b; background: color-mix(in srgb, #c9362b 8%, Canvas 92%); }
.not-found h2 { margin: 0 0 .65rem; color: color-mix(in srgb, #c9362b 82%, CanvasText 18%); }
.not-found p { margin: .45rem 0 0; }
.not-found #suggestion { margin-top: .8rem; font-weight: 600; }
.runtime-warning, .runtime-hint { margin-top: 1rem; }
.runtime-warning { border-color: #c47f00; }
.runtime-hint { border-color: color-mix(in srgb, LinkText 55%, Canvas 45%); }
.runtime-warning strong, .runtime-hint strong { display: block; margin-bottom: .4rem; }
.runtime-hint p { margin: .45rem 0 0; }
.runtime-list { list-style: none; padding: 0; }
.runtime-list li { margin: .35rem 0; }
.project-links { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid color-mix(in srgb, CanvasText 16%, transparent); font-size: .95rem; }
.project-links p { margin: .45rem 0; }
.project-links nav { display: flex; flex-wrap: wrap; gap: .4rem 1rem; }
.muted { opacity: .7; }
</style>
</head>
<body>
<main>
<h1>💙 LibreCode Nextcloud Development Environment</h1>
<section id="not-found" class="not-found" role="alert" hidden>
<h2>⚠ Environment not found</h2>
<p>There is no active development service for <code id="requested-host"></code>.</p>
<p id="suggestion" hidden>Did you mean <a id="suggestion-link"></a>?</p>
</section>

<h2>Active environments</h2>
<div id="environments"></div>

<section id="runtime-section" hidden>
<h2>Environment checks</h2>
<div id="runtime-checks"><p class="muted">Loading runtime information…</p></div>
</section>

<p class="muted">This page is generated from the currently active development routes.</p>

<footer class="project-links">
<strong>Help improve this development environment</strong>
<p class="muted">Contributions, bug reports, and stars help the project grow.</p>
<nav aria-label="Project links">
<a href="https://github.com/LibreCodeCoop/nextcloud-docker-development" target="_blank" rel="noopener noreferrer">Contribute on GitHub</a>
<a href="https://github.com/LibreCodeCoop/nextcloud-docker-development/issues/new/choose" target="_blank" rel="noopener noreferrer">Report an issue</a>
<a href="https://github.com/LibreCodeCoop/nextcloud-docker-development" target="_blank" rel="noopener noreferrer">Star on GitHub</a>
</nav>
</footer>
</main>

<script type="application/json" id="routes">{{ toJson $routes }}</script>
<script>
const serviceNames = {
nginx: 'Nextcloud',
mailpit: 'Mailpit',
playwright: 'Playwright',
eurooffice: 'EuroOffice',
'signal-gateway': 'Signal Gateway',
};

const routes = JSON.parse(document.getElementById('routes').textContent)
.filter(({ host }) => !host.includes('*') && !host.startsWith('~'))
.sort((a, b) => a.project.localeCompare(b.project) || a.host.localeCompare(b.host));

const environments = document.getElementById('environments');
if (routes.length === 0) {
const empty = document.createElement('p');
empty.className = 'muted';
empty.textContent = 'No development environments are active.';
environments.appendChild(empty);
} else {
let currentProject = null;
let list = null;

for (const route of routes) {
if (route.project !== currentProject) {
currentProject = route.project;
const heading = document.createElement('h3');
heading.textContent = currentProject;
environments.appendChild(heading);

list = document.createElement('ul');
environments.appendChild(list);
}

const item = document.createElement('li');
const label = document.createElement('strong');
label.textContent = `${serviceNames[route.service] || route.service}: `;
const link = document.createElement('a');
link.href = `https://${route.host}`;
link.dataset.host = route.host;
link.textContent = link.href;
item.append(label, link);
list.appendChild(item);
}
}

const requestedHost = window.location.hostname;
if (requestedHost !== 'localhost') {
const panel = document.getElementById('not-found');
document.getElementById('requested-host').textContent = requestedHost;
panel.hidden = false;

const hosts = routes.map(({ host }) => host);
const distance = (a, b) => {
const row = Array.from({ length: b.length + 1 }, (_, index) => index);
for (let i = 1; i <= a.length; i++) {
let previous = row[0];
row[0] = i;
for (let j = 1; j <= b.length; j++) {
const current = row[j];
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
previous = current;
}
}
return row[b.length];
};

if (hosts.length > 0) {
const closest = hosts.reduce((best, host) => distance(requestedHost, host) < distance(requestedHost, best) ? host : best);
const suggestion = document.getElementById('suggestion');
const link = document.getElementById('suggestion-link');
link.href = `https://${closest}`;
link.textContent = closest;
suggestion.hidden = false;
}
} else {
const runtimeSection = document.getElementById('runtime-section');
runtimeSection.hidden = false;

fetch('/runtime.json', { cache: 'no-store' })
.then((response) => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then((runtime) => {
const checks = document.getElementById('runtime-checks');
checks.replaceChildren();

const list = document.createElement('ul');
list.className = 'runtime-list';
for (const [label, version] of [
['Docker Engine', runtime.docker],
['runc', runtime.runc],
]) {
const item = document.createElement('li');
item.textContent = `✓ ${label} ${version || 'unknown'}`;
list.appendChild(item);
}
checks.appendChild(list);

for (const warning of runtime.warnings || []) {
const panel = document.createElement('div');
panel.className = 'runtime-warning';
const title = document.createElement('strong');
title.textContent = '⚠ Docker runtime needs attention';
const message = document.createElement('span');
message.textContent = warning.message;
panel.append(title, message);
checks.appendChild(panel);
}

for (const hint of runtime.hints || []) {
if (hint.code !== 'long-project-name') continue;

const panel = document.createElement('div');
panel.className = 'runtime-hint';

const title = document.createElement('strong');
title.textContent = 'ℹ Shorter local URLs are available';

const explanation = document.createElement('p');
explanation.append(
'This environment uses the Compose project name ',
Object.assign(document.createElement('code'), { textContent: hint.projectName }),
', which makes local URLs longer.'
);

const example = document.createElement('p');
example.append(
'For example: ',
Object.assign(document.createElement('code'), { textContent: `https://${hint.exampleHost}/` })
);

const suggestion = document.createElement('p');
suggestion.append(
'You can keep the current directory and use a shorter project name, for example ',
Object.assign(document.createElement('code'), { textContent: 'COMPOSE_PROJECT_NAME=dev docker compose up' }),
'.'
);

panel.append(title, explanation, example, suggestion);
checks.appendChild(panel);
}
})
.catch(() => {
const checks = document.getElementById('runtime-checks');
checks.textContent = 'Runtime information is not available.';
checks.className = 'muted';
});
}
</script>
</body>
</html>
12 changes: 12 additions & 0 deletions .docker/nginx-proxy/docker-gen.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[[config]]
template = "/app/nginx.tmpl"
dest = "/etc/nginx/conf.d/default.conf"
watch = true
wait = "100ms:500ms"
notifycmd = "nginx -s reload"

[[config]]
template = "/dashboard/dashboard.tmpl"
dest = "/usr/share/nginx/html/index.html"
watch = true
wait = "100ms:500ms"
Loading