fix(portal): Refactor API URL config for Web app (#6202)

Why:

* The Swagger UI is currently served from the API application. This
means that the Web application does not have access to the external URL
in the API configuration during/after compilation. Without the API
external URL, we cannot generate a proper link in the portal to the
Swagger UI. This commit refactors how the API external URL is set from
the environment variables and allows the Web app to have access to the
value of the API URL.

Co-authored-by: Jamil <jamilbk@users.noreply.github.com>
This commit is contained in:
Brian Manifold
2024-08-07 12:30:18 -07:00
committed by GitHub
parent 0c6cd4a804
commit e78737c4c8
10 changed files with 113 additions and 65 deletions

View File

@@ -60,8 +60,10 @@ services:
- 8080:8080/tcp
environment:
# Web Server
EXTERNAL_URL: http://localhost:8080/
WEB_EXTERNAL_URL: http://localhost:8080/
API_EXTERNAL_URL: http://localhost:8081/
PHOENIX_HTTP_WEB_PORT: "8080"
PHOENIX_HTTP_API_PORT: "8081"
PHOENIX_SECURE_COOKIES: "false"
# Erlang
ERLANG_DISTRIBUTION_PORT: 9000
@@ -129,7 +131,9 @@ services:
- 8081:8081/tcp
environment:
# Web Server
EXTERNAL_URL: http://localhost:8081/
WEB_EXTERNAL_URL: http://localhost:8080/
API_EXTERNAL_URL: http://localhost:8081/
PHOENIX_HTTP_WEB_PORT: "8080"
PHOENIX_HTTP_API_PORT: "8081"
PHOENIX_SECURE_COOKIES: "false"
# Erlang
@@ -262,7 +266,8 @@ services:
hostname: elixir
environment:
# Web Server
EXTERNAL_URL: http://localhost:8081/
WEB_EXTERNAL_URL: http://localhost:8080/
API_EXTERNAL_URL: http://localhost:8081/
# Erlang
ERLANG_DISTRIBUTION_PORT: 9000
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"

View File

@@ -48,7 +48,8 @@ defmodule Domain.Config.Definitions do
]},
{"WebServer",
[
:external_url,
:web_external_url,
:api_external_url,
:phoenix_secure_cookies,
:phoenix_listen_address,
:phoenix_http_web_port,
@@ -149,11 +150,26 @@ defmodule Domain.Config.Definitions do
##############################################
@doc """
The external URL the UI/API will be accessible at.
The external URL the UI will be accessible at.
If this field is not set or set to `nil`, the server for `api` and `web` apps will not start.
"""
defconfig(:external_url, :string,
defconfig(:web_external_url, :string,
default: nil,
changeset: fn changeset, key ->
changeset
|> Domain.Repo.Changeset.validate_uri(key, require_trailing_slash: true)
|> Domain.Repo.Changeset.normalize_url(key)
end
)
@doc """
The external URL the API will be accessible at.
If this field is not set or set to `nil`, the server for `api` and `web` apps will not start.
"""
defconfig(:api_external_url, :string,
default: nil,
changeset: fn changeset, key ->
changeset

View File

@@ -171,10 +171,10 @@ defmodule Domain.ConfigTest do
end
test "raises an error when value is invalid", %{account: account} do
put_system_env_override(:external_url, "https://example.com/vpn")
put_system_env_override(:web_external_url, "https://example.com/vpn")
message = """
Invalid configuration for 'external_url' retrieved from environment variable EXTERNAL_URL.
Invalid configuration for 'web_external_url' retrieved from environment variable WEB_EXTERNAL_URL.
Errors:
@@ -182,7 +182,7 @@ defmodule Domain.ConfigTest do
## Documentation
The external URL the UI/API will be accessible at.
The external URL the UI will be accessible at.
If this field is not set or set to `nil`, the server for `api` and `web` apps will not start.
@@ -191,7 +191,7 @@ defmodule Domain.ConfigTest do
"""
assert_raise RuntimeError, message, fn ->
fetch_resolved_configs_with_sources!(account.id, [:external_url])
fetch_resolved_configs_with_sources!(account.id, [:web_external_url])
end
end
end

View File

@@ -6,9 +6,12 @@ defmodule Web.Settings.ApiClients.Beta do
{:ok, push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/api_clients")}
else
socket =
socket
|> assign(:page_title, "API Clients")
|> assign(:requested, false)
assign(
socket,
page_title: "API Clients",
requested: false,
api_url: Domain.Config.get_env(:web, :api_external_url)
)
{:ok, socket}
end
@@ -25,7 +28,10 @@ defmodule Web.Settings.ApiClients.Beta do
<:title><%= @page_title %></:title>
<:help>
API Clients are used to manage Firezone configuration through a REST API. See our
<a class={link_style()} href="https://api.firezone.dev/swaggerui">interactive API docs</a>
<.link navigate={"#{@api_url}/swaggerui"} class={link_style()} target="_blank">
OpenAPI-powered docs
</.link>
for more information.
</:help>
<:content>
<.flash kind={:info}>

View File

@@ -7,6 +7,7 @@ defmodule Web.Settings.ApiClients.Index do
socket =
socket
|> assign(page_title: "API Clients")
|> assign(api_url: Domain.Config.get_env(:web, :api_external_url))
|> assign_live_table("actors",
query_module: Actors.Actor.Query,
sortable_fields: [
@@ -56,7 +57,7 @@ defmodule Web.Settings.ApiClients.Index do
<:title><%= @page_title %></:title>
<:help>
API Clients are used to manage Firezone configuration through a REST API. See our
<.link navigate="https://api.firezone.dev/swaggerui" class={link_style()}>
<.link navigate={"#{@api_url}/swaggerui"} class={link_style()} target="_blank">
OpenAPI-powered docs
</.link>
for more information.

View File

@@ -128,6 +128,9 @@ config :web, Web.Endpoint,
signing_salt: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDejX"
]
config :web,
api_external_url: "http://localhost:13001"
config :web,
cookie_secure: false,
cookie_signing_salt: "WjllcThpb2Y=",

View File

@@ -50,6 +50,9 @@ config :web, Web.Endpoint,
reloadable_apps: [:domain, :web],
server: true
config :web,
api_external_url: "http://localhost:13001"
root_path =
__ENV__.file
|> Path.dirname()

View File

@@ -98,13 +98,13 @@ if config_env() == :prod do
config :domain, Domain.Auth.Adapters.Okta.Jobs.SyncDirectory,
enabled: compile_config!(:background_jobs_enabled)
if external_url = compile_config!(:external_url) do
if web_external_url = compile_config!(:web_external_url) do
%{
scheme: external_url_scheme,
host: external_url_host,
port: external_url_port,
path: external_url_path
} = URI.parse(external_url)
scheme: web_external_url_scheme,
host: web_external_url_host,
port: web_external_url_port,
path: web_external_url_path
} = URI.parse(web_external_url)
###############################
##### Web #####################
@@ -117,17 +117,17 @@ if config_env() == :prod do
protocol_options: compile_config!(:phoenix_http_protocol_options)
],
url: [
scheme: external_url_scheme,
host: external_url_host,
port: external_url_port,
path: external_url_path
scheme: web_external_url_scheme,
host: web_external_url_host,
port: web_external_url_port,
path: web_external_url_path
],
secret_key_base: compile_config!(:secret_key_base),
check_origin: [
"#{external_url_scheme}://#{external_url_host}:#{external_url_port}",
"#{external_url_scheme}://*.#{external_url_host}:#{external_url_port}",
"#{external_url_scheme}://#{external_url_host}",
"#{external_url_scheme}://*.#{external_url_host}"
"#{web_external_url_scheme}://#{web_external_url_host}:#{web_external_url_port}",
"#{web_external_url_scheme}://*.#{web_external_url_host}:#{web_external_url_port}",
"#{web_external_url_scheme}://#{web_external_url_host}",
"#{web_external_url_scheme}://*.#{web_external_url_host}"
],
live_view: [
signing_salt: compile_config!(:live_view_signing_salt)
@@ -143,6 +143,15 @@ if config_env() == :prod do
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
config :web, api_url_override: compile_config!(:api_url_override)
end
if api_external_url = compile_config!(:api_external_url) do
%{
scheme: api_external_url_scheme,
host: api_external_url_host,
port: api_external_url_port,
path: api_external_url_path
} = URI.parse(api_external_url)
###############################
##### API #####################
@@ -155,10 +164,10 @@ if config_env() == :prod do
protocol_options: compile_config!(:phoenix_http_protocol_options)
],
url: [
scheme: external_url_scheme,
host: external_url_host,
port: external_url_port,
path: external_url_path
scheme: api_external_url_scheme,
host: api_external_url_host,
port: api_external_url_port,
path: api_external_url_path
],
secret_key_base: compile_config!(:secret_key_base)
@@ -170,6 +179,9 @@ if config_env() == :prod do
config :api,
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
private_clients: compile_config!(:phoenix_private_clients)
config :web,
api_external_url: api_external_url
end
###############################

View File

@@ -218,6 +218,23 @@ locals {
}
shared_application_environment_variables = [
# Apps
{
name = "WEB_EXTERNAL_URL"
value = "https://app.${local.tld}"
},
{
name = "API_EXTERNAL_URL"
value = "https://api.${local.tld}"
},
{
name = "PHOENIX_HTTP_WEB_PORT"
value = "8080"
},
{
name = "PHOENIX_HTTP_API_PORT"
value = "8080"
},
# Database
{
name = "DATABASE_HOST"
@@ -519,14 +536,6 @@ module "web" {
application_environment_variables = concat([
# Web Server
{
name = "EXTERNAL_URL"
value = "https://app.${local.tld}"
},
{
name = "PHOENIX_HTTP_WEB_PORT"
value = "8080"
},
{
name = "BACKGROUND_JOBS_ENABLED"
value = "false"
@@ -595,14 +604,6 @@ module "api" {
application_environment_variables = concat([
# Web Server
{
name = "EXTERNAL_URL"
value = "https://api.${local.tld}"
},
{
name = "PHOENIX_HTTP_API_PORT"
value = "8080"
},
{
name = "BACKGROUND_JOBS_ENABLED"
value = "false"

View File

@@ -191,6 +191,23 @@ locals {
}
shared_application_environment_variables = [
# Apps
{
name = "WEB_EXTERNAL_URL"
value = "https://app.${local.tld}"
},
{
name = "API_EXTERNAL_URL"
value = "https://api.${local.tld}"
},
{
name = "PHOENIX_HTTP_WEB_PORT"
value = "8080"
},
{
name = "PHOENIX_HTTP_API_PORT"
value = "8080"
},
# Database
{
name = "DATABASE_HOST"
@@ -498,14 +515,6 @@ module "web" {
application_environment_variables = concat([
# Web Server
{
name = "EXTERNAL_URL"
value = "https://app.${local.tld}"
},
{
name = "PHOENIX_HTTP_WEB_PORT"
value = "8080"
},
{
name = "API_URL_OVERRIDE"
value = "wss://api.${local.tld}"
@@ -577,14 +586,6 @@ module "api" {
application_environment_variables = concat([
# Web Server
{
name = "EXTERNAL_URL"
value = "https://api.${local.tld}"
},
{
name = "PHOENIX_HTTP_API_PORT"
value = "8080"
},
{
name = "BACKGROUND_JOBS_ENABLED"
value = "false"