Module: Portless::RailsHosts
- Defined in:
- lib/portless/rails_hosts.rb
Overview
The host matchers to whitelist in Rails development, derived from the URL
rb-portless run injects (PORTLESS_URL). Plain Ruby so it's testable without
booting Rails; the Railtie is just glue around it.
Class Method Summary collapse
-
.allowed(portless_url = ENV["PORTLESS_URL"], lan_host = ENV["PORTLESS_LAN_HOST"]) ⇒ Object
Empty unless we're actually running under rb-portless.
-
.cable_origins(portless_url = , lan_host = ) ⇒ Object
Action Cable rejects a WebSocket whose
Originisn't inallowed_request_origins; under rb-portless that's the portless host and any of its subdomains (tenant hosts), over the portless scheme — not thelocalhostdefault. -
.merge_url_options(existing, options) ⇒ Object
default_url_options merge that can't leave a stale :port behind: when the portless URL has no explicit port (443/80), an app-configured
port: 3000must not survive into https://.localhost:3000 links. -
.share_hosts(env = ENV) ⇒ Object
Hostnames of any active public tunnels (
--tailscale/--ngrok), from the env the runner injects. -
.url_options(portless_url = ) ⇒ Object
The
default_url_options(host + scheme) that mailers, jobs, and other request-less URL generation need so their links point at the portless URL instead of a barelocalhost:<random-port>.
Class Method Details
.allowed(portless_url = ENV["PORTLESS_URL"], lan_host = ENV["PORTLESS_LAN_HOST"]) ⇒ Object
Empty unless we're actually running under rb-portless.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/portless/rails_hosts.rb', line 13 def allowed(portless_url = ENV["PORTLESS_URL"], lan_host = ENV["PORTLESS_LAN_HOST"]) return [] if portless_url.to_s.empty? # Rails wraps a Regexp as /\A<re>(:port)?\z/, so match the whole host. patterns = [ /.+\.localhost/ ] host = begin URI(portless_url).host rescue StandardError nil end # A custom, non-.localhost tld too. A regexp, not a ".host" string — # Rails' leading-dot shorthand only matches a single subdomain label, # which would 403 a.b.myapp.test while a.b.myapp.localhost passes. patterns.push(host, /.+\.#{Regexp.escape(host)}/) if host && !host.end_with?(".localhost") # The `--lan` mDNS host (<name>.local) — not covered by any default. patterns.push(lan_host) unless lan_host.to_s.empty? # Public tunnels forward with their own Host (*.ts.net / *.ngrok.app). patterns.concat(share_hosts) patterns end |
.cable_origins(portless_url = , lan_host = ) ⇒ Object
Action Cable rejects a WebSocket whose Origin isn't in
allowed_request_origins; under rb-portless that's the portless host and
any of its subdomains (tenant hosts), over the portless scheme — not the
localhost default. Empty unless we're running under rb-portless.
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/portless/rails_hosts.rb', line 82 def cable_origins(portless_url = ENV["PORTLESS_URL"], lan_host = ENV["PORTLESS_LAN_HOST"]) = (portless_url) or return [] hosts = [ [:host] ] hosts << lan_host unless lan_host.to_s.empty? hosts.concat(share_hosts) hosts.map do |host| %r{\A#{[:protocol]}://([\w-]+\.)*#{Regexp.escape(host)}(:\d+)?\z} end end |
.merge_url_options(existing, options) ⇒ Object
default_url_options merge that can't leave a stale :port behind: when the
portless URL has no explicit port (443/80), an app-configured
port: 3000 must not survive into https://
52 53 54 55 56 |
# File 'lib/portless/rails_hosts.rb', line 52 def (existing, ) merged = existing.to_h.merge() merged.delete(:port) unless .key?(:port) merged end |
.share_hosts(env = ENV) ⇒ Object
Hostnames of any active public tunnels (--tailscale / --ngrok),
from the env the runner injects.
37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/portless/rails_hosts.rb', line 37 def share_hosts(env = ENV) [ env["PORTLESS_TAILSCALE_URL"], env["PORTLESS_NGROK_URL"] ].filter_map do |url| next if url.to_s.empty? begin URI(url).host rescue StandardError nil end end end |
.url_options(portless_url = ) ⇒ Object
The default_url_options (host + scheme) that mailers, jobs, and other
request-less URL generation need so their links point at the portless URL
instead of a bare localhost:<random-port>. Nil unless we're running
under rb-portless. The standard 80/443 ports are dropped (portless serves
without a port number); a custom one is carried through.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/portless/rails_hosts.rb', line 63 def (portless_url = ENV["PORTLESS_URL"]) return if portless_url.to_s.empty? uri = begin URI(portless_url) rescue StandardError nil end return unless uri&.host = { host: uri.host, protocol: uri.scheme } [:port] = uri.port if uri.port && ![ 80, 443 ].include?(uri.port) end |