13 Commits

Author SHA1 Message Date
Andreas Dueren
1189ec810d Override email template footers 2025-11-23 08:45:21 -06:00
Andreas Dueren
a64c28761b Customize email footer for self-hosted instance 2025-11-23 08:21:12 -06:00
Andreas Dueren
00433e664f Redirect base domain to photos host 2025-11-20 23:56:54 -06:00
Andreas Dueren
e50abcb9a2 Limit museum API matchers to photos host 2025-11-20 23:40:08 -06:00
Andreas Dueren
1d4c57c737 Point public-locker to embed host 2025-11-20 23:24:36 -06:00
Andreas Dueren
58e40897a5 Add share/embed/payments frontends and hosts 2025-11-20 23:15:37 -06:00
Andreas Dueren
95758de781 Restore start.sh from 0.5.5 2025-11-20 14:22:13 -06:00
Andreas Dueren
f984184b39 Revert /api matcher nesting 2025-11-20 13:22:24 -06:00
Andreas Dueren
37ce044181 Use handle wrapper for /api matcher 2025-11-20 13:07:26 -06:00
Andreas Dueren
12537896f2 Fix Caddy matcher syntax 2025-11-20 12:43:00 -06:00
Andreas Dueren
ef00dde487 Restrict museum routes to primary host 2025-11-20 12:36:02 -06:00
Andreas Dueren
6ff0b1756d Bump version to 0.5.7 2025-11-20 12:24:21 -06:00
Andreas Dueren
d39a1d86a9 Fix accounts passkey routing 2025-11-20 12:15:01 -06:00
4 changed files with 293 additions and 20 deletions

View File

@@ -1,23 +1,23 @@
# Changelog # Changelog
## 0.5.10 (2025-11-20) ## 0.5.7 (2025-11-20)
* Bundle the Ente Families web app so `family.<domain>` serves the correct invite/management UI instead of the placeholder photos build. * Bundle the Ente Families web app so `family.<domain>` serves the correct invite/management UI instead of the placeholder photos build.
* Ship built-in billing plan JSON so Museum can resolve subscriptions (`family/add-member`, invite acceptance) on self-hosted installs without manual DB edits. * Ship built-in billing plan JSON so Museum can resolve subscriptions (`family/add-member`, invite acceptance) on self-hosted installs without manual DB edits.
* Fix passkey enrollment on the accounts host by ensuring only the photos domain matches the `/api` proxy block.
## 0.5.7 (2025-11-18)
* Inject the API origin into all served HTML so the Next.js bundles (including `accounts/passkeys`) read the self-hosted endpoint instead of defaulting to `https://api.ente.io`
* Document the working Backblaze B2 CORS JSON that whitelists the wildcard origin + upload operations for desktop casts
## 0.5.6 (2025-11-18) ## 0.5.6 (2025-11-18)
* Allow the accounts frontend origin in Museums `webauthn.rporigins` when subdomain routing is enabled so passkey enrollment via the desktop flow succeeds * Allow the accounts frontend origin in Museums `webauthn.rporigins` when subdomain routing is enabled so passkey enrollment via the desktop flow succeeds
* Document the Ente desktop scheme (`ente://app`) in the recommended S3 CORS rules to keep signed URL fetches working for the desktop client * Document the Ente desktop scheme (`ente://app`) in the recommended S3 CORS rules to keep signed URL fetches working for the desktop client
* Add full three-bucket replication support (hot primary, hot secondary, cold tier) and test the workflow with Backblaze (primary hot), Hetzner (secondary hot), and Scaleway Glacier (cold)
* Note that the cold bucket must accept the GLACIER storage class—point the `S3_COLD_*` variables at a provider that supports it, or enable `are_local_buckets`/`use_path_style_urls` so the start script switches Museum into local-bucket mode and skips the Glacier storage class entirely
## 0.5.5 (2025-11-18) ## 0.5.5 (2025-11-18)
* Validate S3 data-center identifiers so replication only uses the canonical `b2-eu-cen`/`wasabi-eu-central-2-v3`/`scw-eu-fr-v3` keys and update the docs to reflect the upstream requirements * Validate S3 data-center identifiers so replication only uses the canonical `b2-eu-cen`/`wasabi-eu-central-2-v3`/`scw-eu-fr-v3` keys and update the docs to reflect the upstream requirements
* Inject the API origin into all served HTML so the Next.js bundles (including `accounts/passkeys`) read the self-hosted endpoint instead of defaulting to `https://api.ente.io`
* Document the working Backblaze B2 CORS JSON that whitelists the wildcard origin + upload operations for desktop casts
## 0.5.4 (2025-11-18) ## 0.5.4 (2025-11-18)

View File

@@ -7,7 +7,7 @@
"contactEmail": "contact@ente.io", "contactEmail": "contact@ente.io",
"website": "https://ente.io", "website": "https://ente.io",
"tagline": "Open source, end-to-end encrypted photo backup", "tagline": "Open source, end-to-end encrypted photo backup",
"version": "0.5.10", "version": "0.5.6",
"upstreamVersion": "git-main", "upstreamVersion": "git-main",
"healthCheckPath": "/health", "healthCheckPath": "/health",
"httpPort": 3080, "httpPort": 3080,
@@ -40,6 +40,27 @@
"defaultValue": "albums", "defaultValue": "albums",
"aliasableDomain": true "aliasableDomain": true
}, },
"SHARE_DOMAIN": {
"title": "Public locker hostname",
"description": "Hostname for the Ente share/collaboration frontend (e.g. share)",
"containerPort": 3080,
"defaultValue": "share",
"aliasableDomain": true
},
"EMBED_DOMAIN": {
"title": "Embed hostname",
"description": "Hostname for the Ente embed frontend (e.g. embed)",
"containerPort": 3080,
"defaultValue": "embed",
"aliasableDomain": true
},
"PAYMENTS_DOMAIN": {
"title": "Payments hostname",
"description": "Hostname for the Ente payments frontend (e.g. payments)",
"containerPort": 3080,
"defaultValue": "payments",
"aliasableDomain": true
},
"FAMILY_DOMAIN": { "FAMILY_DOMAIN": {
"title": "Family hostname", "title": "Family hostname",
"description": "Hostname for the Ente family web app (e.g. family)", "description": "Hostname for the Ente family web app (e.g. family)",

View File

@@ -57,14 +57,17 @@ RUN apt-get update && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
RUN corepack enable RUN corepack enable
RUN yarn install --network-timeout 1000000 RUN yarn install --network-timeout 1000000
RUN mkdir -p /build/web/photos /build/web/accounts /build/web/auth /build/web/cast /build/web/albums /build/web/family RUN mkdir -p /build/web/photos /build/web/accounts /build/web/auth /build/web/cast /build/web/albums /build/web/family /build/web/share /build/web/embed /build/web/payments
RUN set -e; \ RUN set -e; \
yarn build:photos; \ yarn build:photos; \
yarn build:accounts; \ yarn build:accounts; \
yarn build:auth; \ yarn build:auth; \
yarn build:cast yarn build:cast; \
yarn build:share; \
yarn build:embed; \
yarn build:payments
RUN if [ -d "apps" ]; then \ RUN if [ -d "apps" ]; then \
for app in photos accounts auth cast; do \ for app in photos accounts auth cast share embed payments; do \
if [ -d "apps/${app}/out" ]; then \ if [ -d "apps/${app}/out" ]; then \
rm -rf "/build/web/${app}"; \ rm -rf "/build/web/${app}"; \
mkdir -p "/build/web/${app}"; \ mkdir -p "/build/web/${app}"; \
@@ -132,6 +135,9 @@ COPY --from=web-builder /build/web/accounts /app/web/accounts
COPY --from=web-builder /build/web/auth /app/web/auth COPY --from=web-builder /build/web/auth /app/web/auth
COPY --from=web-builder /build/web/cast /app/web/cast COPY --from=web-builder /build/web/cast /app/web/cast
COPY --from=web-builder /build/web/albums /app/web/albums COPY --from=web-builder /build/web/albums /app/web/albums
COPY --from=web-builder /build/web/share /app/web/share
COPY --from=web-builder /build/web/embed /app/web/embed
COPY --from=web-builder /build/web/payments /app/web/payments
COPY --from=families-builder /build/family /app/web/family COPY --from=families-builder /build/family /app/web/family
COPY start.sh /app/pkg/start.sh COPY start.sh /app/pkg/start.sh

266
start.sh
View File

@@ -107,6 +107,9 @@ AUTH_HOST="$(resolve_http_hostname "AUTH_DOMAIN" "auth.${APP_FQDN}")"
CAST_HOST="$(resolve_http_hostname "CAST_DOMAIN" "cast.${APP_FQDN}")" CAST_HOST="$(resolve_http_hostname "CAST_DOMAIN" "cast.${APP_FQDN}")"
ALBUMS_HOST="$(resolve_http_hostname "ALBUMS_DOMAIN" "albums.${APP_FQDN}")" ALBUMS_HOST="$(resolve_http_hostname "ALBUMS_DOMAIN" "albums.${APP_FQDN}")"
FAMILY_HOST="$(resolve_http_hostname "FAMILY_DOMAIN" "family.${APP_FQDN}")" FAMILY_HOST="$(resolve_http_hostname "FAMILY_DOMAIN" "family.${APP_FQDN}")"
SHARE_HOST="$(resolve_http_hostname "SHARE_DOMAIN" "share.${APP_FQDN}")"
EMBED_HOST="$(resolve_http_hostname "EMBED_DOMAIN" "embed.${APP_FQDN}")"
PAYMENTS_HOST="$(resolve_http_hostname "PAYMENTS_DOMAIN" "payments.${APP_FQDN}")"
normalize_host() { normalize_host() {
local host="$1" local host="$1"
@@ -126,10 +129,13 @@ AUTH_HOST="$(normalize_host "$AUTH_HOST")"
CAST_HOST="$(normalize_host "$CAST_HOST")" CAST_HOST="$(normalize_host "$CAST_HOST")"
ALBUMS_HOST="$(normalize_host "$ALBUMS_HOST")" ALBUMS_HOST="$(normalize_host "$ALBUMS_HOST")"
FAMILY_HOST="$(normalize_host "$FAMILY_HOST")" FAMILY_HOST="$(normalize_host "$FAMILY_HOST")"
SHARE_HOST="$(normalize_host "$SHARE_HOST")"
EMBED_HOST="$(normalize_host "$EMBED_HOST")"
PAYMENTS_HOST="$(normalize_host "$PAYMENTS_HOST")"
USE_SUBDOMAIN_ROUTING=false USE_SUBDOMAIN_ROUTING=false
if [ "$APP_FQDN" != "localhost" ]; then if [ "$APP_FQDN" != "localhost" ]; then
if [ "$PHOTOS_HOST" != "$APP_FQDN" ] || [ "$ACCOUNTS_HOST" != "$APP_FQDN" ] || [ "$AUTH_HOST" != "$APP_FQDN" ] || [ "$CAST_HOST" != "$APP_FQDN" ] || [ "$ALBUMS_HOST" != "$APP_FQDN" ] || [ "$FAMILY_HOST" != "$APP_FQDN" ]; then if [ "$PHOTOS_HOST" != "$APP_FQDN" ] || [ "$ACCOUNTS_HOST" != "$APP_FQDN" ] || [ "$AUTH_HOST" != "$APP_FQDN" ] || [ "$CAST_HOST" != "$APP_FQDN" ] || [ "$ALBUMS_HOST" != "$APP_FQDN" ] || [ "$FAMILY_HOST" != "$APP_FQDN" ] || [ "$SHARE_HOST" != "$APP_FQDN" ] || [ "$EMBED_HOST" != "$APP_FQDN" ] || [ "$PAYMENTS_HOST" != "$APP_FQDN" ]; then
USE_SUBDOMAIN_ROUTING=true USE_SUBDOMAIN_ROUTING=true
fi fi
fi fi
@@ -141,12 +147,18 @@ if [ "$USE_SUBDOMAIN_ROUTING" = true ]; then
CAST_URL="https://${CAST_HOST}" CAST_URL="https://${CAST_HOST}"
FAMILY_URL="https://${FAMILY_HOST}" FAMILY_URL="https://${FAMILY_HOST}"
ALBUMS_URL="https://${ALBUMS_HOST}" ALBUMS_URL="https://${ALBUMS_HOST}"
SHARE_URL="https://${SHARE_HOST}"
EMBED_URL="https://${EMBED_HOST}"
PAYMENTS_URL="https://${PAYMENTS_HOST}"
else else
ACCOUNTS_URL="${BASE_URL}/accounts" ACCOUNTS_URL="${BASE_URL}/accounts"
AUTH_URL="${BASE_URL}/auth" AUTH_URL="${BASE_URL}/auth"
CAST_URL="${BASE_URL}/cast" CAST_URL="${BASE_URL}/cast"
FAMILY_URL="${BASE_URL}/family" FAMILY_URL="${BASE_URL}/family"
ALBUMS_URL="${BASE_URL}/albums" ALBUMS_URL="${BASE_URL}/albums"
SHARE_URL="${BASE_URL}/share"
EMBED_URL="${BASE_URL}/embed"
PAYMENTS_URL="${BASE_URL}/payments"
fi fi
if [ "$APP_FQDN" != "localhost" ]; then if [ "$APP_FQDN" != "localhost" ]; then
@@ -155,13 +167,17 @@ else
API_BASE="$BASE_URL" API_BASE="$BASE_URL"
fi fi
API_ORIGIN="${API_BASE}/api" API_ORIGIN="${API_BASE}/api"
RP_ID="$PHOTOS_HOST" if [[ "$PHOTOS_HOST" == *.*.* ]]; then
RP_ID="${PHOTOS_HOST#*.}"
else
RP_ID="$PHOTOS_HOST"
fi
log INFO "Application base URL: $BASE_URL" log INFO "Application base URL: $BASE_URL"
log INFO "Relying party ID: $RP_ID" log INFO "Relying party ID: $RP_ID"
log INFO "API origin: $API_ORIGIN" log INFO "API origin: $API_ORIGIN"
if [ "$USE_SUBDOMAIN_ROUTING" = true ]; then if [ "$USE_SUBDOMAIN_ROUTING" = true ]; then
log INFO "Serving frontend hosts: photos=${PHOTOS_HOST}, accounts=${ACCOUNTS_HOST}, auth=${AUTH_HOST}, cast=${CAST_HOST}" log INFO "Serving frontend hosts: photos=${PHOTOS_HOST}, accounts=${ACCOUNTS_HOST}, auth=${AUTH_HOST}, cast=${CAST_HOST}, share=${SHARE_HOST}, embed=${EMBED_HOST}, payments=${PAYMENTS_HOST}"
fi fi
S3_CONFIG_FILE="$CONFIG_DIR/s3.env" S3_CONFIG_FILE="$CONFIG_DIR/s3.env"
@@ -668,6 +684,151 @@ sync_dir "$APP_DIR/server/mail-templates" "$MUSEUM_RUNTIME_DIR/mail-templates"
sync_dir "$APP_DIR/server/assets" "$MUSEUM_RUNTIME_DIR/assets" sync_dir "$APP_DIR/server/assets" "$MUSEUM_RUNTIME_DIR/assets"
sync_dir "$APP_DIR/data" "$MUSEUM_RUNTIME_DIR/data" sync_dir "$APP_DIR/data" "$MUSEUM_RUNTIME_DIR/data"
customize_mail_templates() {
local base_template="$MUSEUM_RUNTIME_DIR/mail-templates/base.html"
if [ ! -f "$base_template" ]; then
return
fi
cat > "$base_template" <<EOF_BASE_TEMPLATE
{{define "base"}}
<!DOCTYPE html>
<html>
<meta content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<style>
body {
background-color: #f0f1f3;
font-family: "Helvetica Neue", "Segoe UI", Helvetica, sans-serif;
font-size: 16px;
line-height: 27px;
margin: 0;
color: #444;
}
pre {
background: #f4f4f4f4;
padding: 2px;
}
table {
width: 100%;
border: 1px solid #ddd;
}
table td {
border-color: #ddd;
padding: 5px;
}
.wrap {
background-color: #fff;
padding: 30px;
max-width: 525px;
margin: 0 auto;
border-radius: 5px;
}
.button {
background: #0055d4;
border-radius: 3px;
text-decoration: none !important;
color: #fff !important;
font-weight: bold;
padding: 10px 30px;
display: inline-block;
}
.button:hover {
background: #111;
}
.footer {
text-align: center;
font-size: 12px;
color: #888;
padding: 16px;
}
.gutter {
padding: 30px;
}
img {
max-width: 100%;
height: auto;
}
a {
color: #0055d4;
}
a:hover {
color: #111;
}
@media screen and (max-width: 600px) {
.wrap {
max-width: auto;
}
.gutter {
padding: 10px;
}
}
</style>
<body>
<div class="gutter" style="padding: 4px">&nbsp;</div>
<div class="wrap" style="background-color: #ffffff; padding: 2px 30px 30px 30px; max-width: 525px; margin: 0 auto; border-radius: 5px; font-size: 16px;">
<main>
{{block "content" .}} Default Content {{end}}
</main>
</div>
<br />
<div class="footer">
<p style="margin-bottom: 4px;">
<strong><a href="$BASE_URL" target="_blank" style="color: #555;">$BASE_URL</a></strong>
</p>
<p style="margin: 0;">
This email was sent by a self-hosted instance of the open source Ente application.
</p>
</div>
</body>
</html>
{{end}}
EOF_BASE_TEMPLATE
chown cloudron:cloudron "$base_template"
python3 - "$MUSEUM_RUNTIME_DIR/mail-templates" "$BASE_URL" <<'PY'
import re
import sys
from pathlib import Path
template_dir = Path(sys.argv[1])
base_url = sys.argv[2]
footer_html = f'''
<div class="footer" style="text-align: center; font-size: 12px; color: #888; padding: 16px;">
<p style="margin-bottom: 4px;">
<strong><a href="{base_url}" target="_blank" style="color: #555;">{base_url}</a></strong>
</p>
<p style="margin: 0;">This email was sent by a self-hosted instance of the open source Ente application.</p>
</div>
</body>'''.lstrip()
pattern = re.compile(r'<div class="footer".*?</div>\s*</body>', re.DOTALL | re.IGNORECASE)
for html_path in template_dir.rglob("*.html"):
original = html_path.read_text()
updated = pattern.sub(footer_html, original)
if updated != original:
html_path.write_text(updated)
# Ownership will be fixed later by the caller
PY
find "$MUSEUM_RUNTIME_DIR/mail-templates" -type f -name "*.html" -exec chown cloudron:cloudron {} +
}
customize_mail_templates
if [ ! -x "$MUSEUM_BIN" ]; then if [ ! -x "$MUSEUM_BIN" ]; then
log ERROR "Museum binary not found at $MUSEUM_BIN" log ERROR "Museum binary not found at $MUSEUM_BIN"
exit 1 exit 1
@@ -687,7 +848,8 @@ http:
apps: apps:
public-albums: "$ALBUMS_URL" public-albums: "$ALBUMS_URL"
public-locker: "$PHOTOS_URL" embed-albums: "$EMBED_URL"
public-locker: "$EMBED_URL"
accounts: "$ACCOUNTS_URL" accounts: "$ACCOUNTS_URL"
cast: "$CAST_URL" cast: "$CAST_URL"
family: "$FAMILY_URL" family: "$FAMILY_URL"
@@ -802,7 +964,7 @@ chmod 600 "$MUSEUM_CONFIG"
log INFO "Preparing frontend assets" log INFO "Preparing frontend assets"
if [ -d "$WEB_SOURCE_DIR" ]; then if [ -d "$WEB_SOURCE_DIR" ]; then
for app in photos accounts auth cast albums family; do for app in photos accounts auth cast albums family share embed payments; do
if [ -d "$WEB_SOURCE_DIR/$app" ]; then if [ -d "$WEB_SOURCE_DIR/$app" ]; then
log INFO "Updating $app frontend assets" log INFO "Updating $app frontend assets"
rm -rf "$WEB_RUNTIME_DIR/$app" rm -rf "$WEB_RUNTIME_DIR/$app"
@@ -904,6 +1066,9 @@ if [ -d "$WEB_RUNTIME_DIR" ]; then
"https://web.ente.io|$PHOTOS_URL" "https://web.ente.io|$PHOTOS_URL"
"https://albums.ente.io|$ALBUMS_URL" "https://albums.ente.io|$ALBUMS_URL"
"https://family.ente.io|$FAMILY_URL" "https://family.ente.io|$FAMILY_URL"
"https://share.ente.io|$SHARE_URL"
"https://embed.ente.io|$EMBED_URL"
"https://payments.ente.io|$PAYMENTS_URL"
"https://ente.io|$PHOTOS_URL" "https://ente.io|$PHOTOS_URL"
) )
OLD_IFS="$IFS" OLD_IFS="$IFS"
@@ -954,6 +1119,20 @@ chmod 700 "$DATA_DIR/home"
log INFO "Rendering Caddy configuration" log INFO "Rendering Caddy configuration"
if [ "$USE_SUBDOMAIN_ROUTING" = true ]; then if [ "$USE_SUBDOMAIN_ROUTING" = true ]; then
PHOTOS_HOST_MATCHERS="$PHOTOS_HOST"
if [ "$APP_FQDN" != "$PHOTOS_HOST" ]; then
PHOTOS_HOST_MATCHERS="$PHOTOS_HOST_MATCHERS $APP_FQDN"
fi
if [ "$APP_FQDN" != "$PHOTOS_HOST" ]; then
BASE_DOMAIN_REDIRECT="
@base_app_host host ${APP_FQDN}
handle @base_app_host {
redir https://${PHOTOS_HOST}{uri}
}
"
else
BASE_DOMAIN_REDIRECT=""
fi
cat > "$CADDY_CONFIG" <<EOF_CADDY cat > "$CADDY_CONFIG" <<EOF_CADDY
{ {
admin off admin off
@@ -989,6 +1168,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
respond 204 respond 204
} }
${BASE_DOMAIN_REDIRECT}\
handle_path /api/* { handle_path /api/* {
@api_cors_subdomain header Origin * @api_cors_subdomain header Origin *
header @api_cors_subdomain { header @api_cors_subdomain {
@@ -1011,6 +1191,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
handle /health { handle /health {
rewrite * /ping rewrite * /ping
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1020,6 +1201,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
handle /ping { handle /ping {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1028,12 +1210,22 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
handle /public/* { handle /public/* {
reverse_proxy localhost:8080 reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
header_up X-Forwarded-Proto {http.request.header.X-Forwarded-Proto}
}
} }
@user_api path_regexp user_api ^/users($|/.*) @user_api {
host ${PHOTOS_HOST_MATCHERS}
path_regexp user_api ^/users($|/.*)
}
handle @user_api { handle @user_api {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1042,11 +1234,13 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
@museum_api_get { @museum_api_get {
host ${PHOTOS_HOST_MATCHERS}
method GET HEAD method GET HEAD
path_regexp museum_api_get ^/(admin|authenticator|billing|cast|collections|custom-domain|diff|discount|email-hash|emails-from-hashes|emergency-contacts|family|file|file-link|files|fire|info|job|mail|metrics|multipart-upload-urls|offers|options|pass-info|passkeys|public-collection|push|queue|remote-store|storage-bonus|thumbnail|trash|unknown-api|upload-urls|user|user-entity|verify-password)(/|$) path_regexp museum_api_get ^/(admin|authenticator|billing|cast|collections|custom-domain|diff|discount|email-hash|emails-from-hashes|emergency-contacts|family|file|file-link|files|fire|info|job|mail|metrics|multipart-upload-urls|offers|options|pass-info|passkeys|public-collection|push|queue|remote-store|storage-bonus|thumbnail|trash|unknown-api|upload-urls|user|user-entity|verify-password)(/|$)
} }
handle @museum_api_get { handle @museum_api_get {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1055,11 +1249,13 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
@write_methods { @write_methods {
host ${PHOTOS_HOST_MATCHERS}
not method GET not method GET
not method HEAD not method HEAD
} }
handle @write_methods { handle @write_methods {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1067,7 +1263,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
} }
@photos_host host ${PHOTOS_HOST} @photos_host host ${PHOTOS_HOST_MATCHERS}
handle @photos_host { handle @photos_host {
root * $WEB_RUNTIME_DIR/photos root * $WEB_RUNTIME_DIR/photos
try_files {path} {path}/index.html {path}.html index.html try_files {path} {path}/index.html {path}.html index.html
@@ -1109,6 +1305,27 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
file_server file_server
} }
@share_host host ${SHARE_HOST}
handle @share_host {
root * $WEB_RUNTIME_DIR/share
try_files {path} {path}/index.html {path}.html index.html
file_server
}
@embed_host host ${EMBED_HOST}
handle @embed_host {
root * $WEB_RUNTIME_DIR/embed
try_files {path} {path}/index.html {path}.html index.html
file_server
}
@payments_host host ${PAYMENTS_HOST}
handle @payments_host {
root * $WEB_RUNTIME_DIR/payments
try_files {path} {path}/index.html {path}.html index.html
file_server
}
handle { handle {
respond "Not Found" 404 respond "Not Found" 404
} }
@@ -1172,6 +1389,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
handle /health { handle /health {
rewrite * /ping rewrite * /ping
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1181,6 +1399,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
handle /ping { handle /ping {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1189,12 +1408,19 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
handle /public/* { handle /public/* {
reverse_proxy localhost:8080 reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
header_up X-Forwarded-Proto {http.request.header.X-Forwarded-Proto}
}
} }
@user_api_path path_regexp user_api_path ^/users($|/.*) @user_api_path path_regexp user_api_path ^/users($|/.*)
handle @user_api_path { handle @user_api_path {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1208,6 +1434,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
handle @museum_api_get_path { handle @museum_api_get_path {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1221,6 +1448,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
} }
handle @write_methods_path { handle @write_methods_path {
reverse_proxy localhost:8080 { reverse_proxy localhost:8080 {
trusted_proxies private_ranges
header_up Host {http.request.host} header_up Host {http.request.host}
header_up X-Real-IP {http.request.header.X-Forwarded-For} header_up X-Real-IP {http.request.header.X-Forwarded-For}
header_up X-Forwarded-For {http.request.header.X-Forwarded-For} header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
@@ -1230,7 +1458,7 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
handle /_next/* { handle /_next/* {
root * $WEB_RUNTIME_DIR root * $WEB_RUNTIME_DIR
try_files {path} auth{path} accounts{path} photos{path} cast{path} albums{path} family{path} try_files {path} auth{path} accounts{path} photos{path} cast{path} albums{path} family{path} share{path} embed{path} payments{path}
file_server file_server
} }
@@ -1269,6 +1497,24 @@ cat > "$CADDY_CONFIG" <<EOF_CADDY
file_server file_server
} }
handle /share/* {
root * $WEB_RUNTIME_DIR
try_files {path} {path}/index.html /share/index.html
file_server
}
handle /embed/* {
root * $WEB_RUNTIME_DIR
try_files {path} {path}/index.html /embed/index.html
file_server
}
handle /payments/* {
root * $WEB_RUNTIME_DIR
try_files {path} {path}/index.html /payments/index.html
file_server
}
handle /photos/* { handle /photos/* {
root * $WEB_RUNTIME_DIR root * $WEB_RUNTIME_DIR
try_files {path} {path}/index.html /photos/index.html try_files {path} {path}/index.html /photos/index.html