diff --git a/asciinema/compose.yaml b/asciinema/compose.yaml index 7e68922..e436441 100644 --- a/asciinema/compose.yaml +++ b/asciinema/compose.yaml @@ -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 diff --git a/asciinema/custom.exs b/asciinema/custom.exs index a1cc669..dd1b726 100644 --- a/asciinema/custom.exs +++ b/asciinema/custom.exs @@ -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 = """ - - - """ - - case conn.resp_body do - body when is_binary(body) -> - new_body = String.replace(body, "", "#{custom_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 diff --git a/asciinema/nginx/nginx.conf b/asciinema/nginx/nginx.conf new file mode 100644 index 0000000..bbfadb2 --- /dev/null +++ b/asciinema/nginx/nginx.conf @@ -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 '' ''; + 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"; + } + } +} diff --git a/asciinema/theme/favicon.svg b/asciinema/theme/favicon.svg index 2a96a8b..284333a 100644 --- a/asciinema/theme/favicon.svg +++ b/asciinema/theme/favicon.svg @@ -1,24 +1,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/asciinema/theme/inject.js b/asciinema/theme/inject.js new file mode 100644 index 0000000..aa34e69 --- /dev/null +++ b/asciinema/theme/inject.js @@ -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); +})();