feat: add nginx proxy for theme injection (cleaner approach)
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
services:
|
||||
asciinema:
|
||||
asciinema_backend:
|
||||
image: ${ASCIINEMA_IMAGE:-ghcr.io/asciinema/asciinema-server:latest}
|
||||
container_name: ${ASCIINEMA_COMPOSE_PROJECT_NAME}_app
|
||||
container_name: ${ASCIINEMA_COMPOSE_PROJECT_NAME}_backend
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- compose_network
|
||||
volumes:
|
||||
- asciinema_data:/var/opt/asciinema
|
||||
- ./custom.exs:/opt/app/etc/custom.exs:ro
|
||||
- ./theme:/opt/app/etc/theme:ro
|
||||
environment:
|
||||
SECRET_KEY_BASE: ${ASCIINEMA_SECRET_KEY}
|
||||
URL_HOST: ${ASCIINEMA_TRAEFIK_HOST}
|
||||
@@ -20,6 +19,18 @@ services:
|
||||
SMTP_FROM_ADDRESS: ${EMAIL_FROM}
|
||||
SIGN_UP_DISABLED: ${ASCIINEMA_SIGN_UP_DISABLED:-false}
|
||||
DEFAULT_AVATAR: gravatar
|
||||
|
||||
asciinema:
|
||||
image: nginx:alpine
|
||||
container_name: ${ASCIINEMA_COMPOSE_PROJECT_NAME}_app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- compose_network
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./theme:/theme:ro
|
||||
depends_on:
|
||||
- asciinema_backend
|
||||
labels:
|
||||
- 'traefik.enable=${ASCIINEMA_TRAEFIK_ENABLED}'
|
||||
# Main web interface - HTTP to HTTPS redirect
|
||||
@@ -35,7 +46,7 @@ services:
|
||||
- 'traefik.http.middlewares.${ASCIINEMA_COMPOSE_PROJECT_NAME}-compress.compress=true'
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-web-secure.middlewares=${ASCIINEMA_COMPOSE_PROJECT_NAME}-compress,security-headers@file'
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-web-secure.service=${ASCIINEMA_COMPOSE_PROJECT_NAME}'
|
||||
- 'traefik.http.services.${ASCIINEMA_COMPOSE_PROJECT_NAME}.loadbalancer.server.port=4000'
|
||||
- 'traefik.http.services.${ASCIINEMA_COMPOSE_PROJECT_NAME}.loadbalancer.server.port=8080'
|
||||
# Admin interface - HTTP to HTTPS redirect
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin-web.middlewares=${ASCIINEMA_COMPOSE_PROJECT_NAME}-redirect-web-secure'
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin-web.rule=Host(`admin.${ASCIINEMA_TRAEFIK_HOST}`)'
|
||||
@@ -48,7 +59,7 @@ services:
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin-web-secure.entrypoints=web-secure'
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin-web-secure.middlewares=${ASCIINEMA_COMPOSE_PROJECT_NAME}-auth,${ASCIINEMA_COMPOSE_PROJECT_NAME}-compress,security-headers@file'
|
||||
- 'traefik.http.routers.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin-web-secure.service=${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin'
|
||||
- 'traefik.http.services.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin.loadbalancer.server.port=4002'
|
||||
- 'traefik.http.services.${ASCIINEMA_COMPOSE_PROJECT_NAME}-admin.loadbalancer.server.port=8080'
|
||||
# Network
|
||||
- 'traefik.docker.network=${NETWORK_NAME}'
|
||||
# Watchtower
|
||||
|
||||
@@ -15,58 +15,3 @@ config :asciinema, Asciinema.Emails.Mailer,
|
||||
verify: :verify_none,
|
||||
versions: [:"tlsv1.2", :"tlsv1.3"]
|
||||
]
|
||||
|
||||
# Custom theme configuration - inject custom CSS and favicon
|
||||
defmodule AsciinemaWeb.CustomThemePlug do
|
||||
@moduledoc """
|
||||
Plug to inject custom CSS and favicon into HTML responses
|
||||
"""
|
||||
import Plug.Conn
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, _opts) do
|
||||
register_before_send(conn, fn conn ->
|
||||
if html_response?(conn) do
|
||||
inject_custom_theme(conn)
|
||||
else
|
||||
conn
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp html_response?(conn) do
|
||||
case get_resp_header(conn, "content-type") do
|
||||
[content_type | _] -> String.contains?(content_type, "text/html")
|
||||
[] -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp inject_custom_theme(conn) do
|
||||
custom_head = """
|
||||
<link rel="stylesheet" href="/theme/custom.css">
|
||||
<link rel="icon" type="image/svg+xml" href="/theme/favicon.svg">
|
||||
"""
|
||||
|
||||
case conn.resp_body do
|
||||
body when is_binary(body) ->
|
||||
new_body = String.replace(body, "</head>", "#{custom_head}</head>")
|
||||
%{conn | resp_body: new_body}
|
||||
_ ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Configure Phoenix endpoint to serve custom theme files
|
||||
config :asciinema, AsciinemaWeb.Endpoint,
|
||||
# Serve theme files from /opt/app/etc/theme
|
||||
static_dirs: %{
|
||||
at: "/theme",
|
||||
from: "/opt/app/etc/theme",
|
||||
gzip: false
|
||||
}
|
||||
|
||||
# Inject the custom theme Plug into the endpoint
|
||||
config :asciinema, AsciinemaWeb.Endpoint,
|
||||
plug: AsciinemaWeb.CustomThemePlug
|
||||
|
||||
40
asciinema/nginx/nginx.conf
Normal file
40
asciinema/nginx/nginx.conf
Normal file
@@ -0,0 +1,40 @@
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Map to determine which backend to use based on host
|
||||
map $http_host $backend_port {
|
||||
default 4000;
|
||||
~^admin\. 4002;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name _;
|
||||
|
||||
# Proxy to actual asciinema server (port depends on subdomain)
|
||||
location / {
|
||||
proxy_pass http://asciinema_backend:$backend_port;
|
||||
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;
|
||||
|
||||
# Inject custom JavaScript into HTML responses
|
||||
sub_filter '</head>' '<script src="/theme/inject.js"></script></head>';
|
||||
sub_filter_once on;
|
||||
sub_filter_types text/html;
|
||||
}
|
||||
|
||||
# Serve theme files directly
|
||||
location /theme/ {
|
||||
alias /theme/;
|
||||
expires 1d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 29 KiB |
22
asciinema/theme/inject.js
Normal file
22
asciinema/theme/inject.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// Inject custom theme CSS and favicon
|
||||
(function() {
|
||||
// Inject custom CSS
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = '/theme/custom.css';
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Inject custom favicon
|
||||
var favicon = document.createElement('link');
|
||||
favicon.rel = 'icon';
|
||||
favicon.type = 'image/svg+xml';
|
||||
favicon.href = '/theme/favicon.svg';
|
||||
|
||||
// Remove existing favicons first
|
||||
var existingFavicons = document.querySelectorAll('link[rel*="icon"]');
|
||||
existingFavicons.forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
document.head.appendChild(favicon);
|
||||
})();
|
||||
Reference in New Issue
Block a user