mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
feat(portal): Enable address_description field to provide hint for accessing Resources in Clients (#5273)
Fixes #5270 - Relaxes the `NOT NULL` constraint because in Clients we already account for empty address descriptions (by showing the address in its place if missing). We may want to simply hide the Resource altogether if the description is missing (based on user feedback). With a blank field, we can differentiate between not entered vs entered an address. - Updates help text a bit ```[tasklist] - [x] Update docs with examples ``` <img width="772" alt="Screenshot 2024-06-06 at 12 01 48 PM" src="https://github.com/firezone/firezone/assets/167144/523aa0ff-f30d-44cb-bb3c-5d5cda7236e6"> --------- Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
This commit is contained in:
@@ -5,7 +5,7 @@ defmodule Domain.Resources.Resource.Changeset do
|
||||
|
||||
@fields ~w[address address_description name type]a
|
||||
@update_fields ~w[name address_description]a
|
||||
@required_fields ~w[name address address_description type]a
|
||||
@required_fields ~w[name address type]a
|
||||
|
||||
def create(%Accounts.Account{} = account, attrs, %Auth.Subject{} = subject) do
|
||||
%Resource{connections: []}
|
||||
@@ -152,8 +152,6 @@ defmodule Domain.Resources.Resource.Changeset do
|
||||
changeset
|
||||
|> validate_length(:name, min: 1, max: 255)
|
||||
|> validate_length(:address_description, min: 1, max: 512)
|
||||
# TODO: remove once address_description is visible again
|
||||
|> copy_change(:address, :address_description)
|
||||
|> cast_embed(:filters, with: &cast_filter/2)
|
||||
|> unique_constraint(:ipv4, name: :resources_account_id_ipv4_index)
|
||||
|> unique_constraint(:ipv6, name: :resources_account_id_ipv6_index)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
defmodule Domain.Repo.Migrations.RemoveNullConstraintFromAddressDescription do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:resources) do
|
||||
modify(:address_description, :string, null: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -892,7 +892,6 @@ defmodule Domain.ResourcesTest do
|
||||
assert errors_on(changeset) == %{
|
||||
name: ["can't be blank"],
|
||||
address: ["can't be blank"],
|
||||
address_description: ["can't be blank"],
|
||||
type: ["can't be blank"],
|
||||
connections: ["can't be blank"]
|
||||
}
|
||||
@@ -987,8 +986,7 @@ defmodule Domain.ResourcesTest do
|
||||
assert {:ok, resource} = create_resource(attrs, subject)
|
||||
|
||||
assert resource.address == attrs.address
|
||||
# TODO: uncomment once we show address_description
|
||||
# assert resource.address_description == attrs.address_description
|
||||
assert resource.address_description == attrs.address_description
|
||||
assert resource.name == attrs.address
|
||||
assert resource.account_id == account.id
|
||||
|
||||
@@ -1021,7 +1019,7 @@ defmodule Domain.ResourcesTest do
|
||||
type: :cidr,
|
||||
name: "mycidr",
|
||||
address: "192.168.1.1/28",
|
||||
address_description: "192.168.1.1/28"
|
||||
address_description: "https://google.com"
|
||||
)
|
||||
|
||||
assert {:ok, resource} = create_resource(attrs, subject)
|
||||
|
||||
@@ -58,16 +58,15 @@ defmodule Web.Resources.Edit do
|
||||
required
|
||||
/>
|
||||
|
||||
<div class="hidden">
|
||||
<div>
|
||||
<.input
|
||||
field={@form[:address_description]}
|
||||
type="text"
|
||||
label="Address Description"
|
||||
placeholder={@form[:address].value || "http://example.com/"}
|
||||
required
|
||||
placeholder="Enter a description or URL"
|
||||
/>
|
||||
<p class="mt-2 text-xs text-neutral-500">
|
||||
This will be displayed in client applications to assist users in understanding how to access the resource.
|
||||
Optional description or URL to show in Clients to help users access this Resource.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -108,16 +108,15 @@ defmodule Web.Resources.New do
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="hidden">
|
||||
<div>
|
||||
<.input
|
||||
field={@form[:address_description]}
|
||||
type="text"
|
||||
label="Address Description"
|
||||
placeholder={@form[:address].value || "http://example.com/"}
|
||||
required
|
||||
placeholder="Enter a description or URL"
|
||||
/>
|
||||
<p class="mt-2 text-xs text-neutral-500">
|
||||
This will be displayed in client applications to assist users in understanding how to access the resource.
|
||||
Optional description or URL to show in Clients to help users access this Resource.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -161,7 +160,6 @@ defmodule Web.Resources.New do
|
||||
attrs =
|
||||
attrs
|
||||
|> maybe_put_default_name(name_changed?)
|
||||
|> maybe_put_default_address_description(address_description_changed?)
|
||||
|> map_filters_form_attrs(socket.assigns.account)
|
||||
|> map_connections_form_attrs()
|
||||
|> maybe_put_connections(socket.assigns.params)
|
||||
@@ -184,7 +182,6 @@ defmodule Web.Resources.New do
|
||||
attrs =
|
||||
attrs
|
||||
|> maybe_put_default_name()
|
||||
|> maybe_put_default_address_description()
|
||||
|> map_filters_form_attrs(socket.assigns.account)
|
||||
|> map_connections_form_attrs()
|
||||
|> maybe_put_connections(socket.assigns.params)
|
||||
@@ -212,32 +209,6 @@ defmodule Web.Resources.New do
|
||||
Map.put(attrs, "name", attrs["address"])
|
||||
end
|
||||
|
||||
defp maybe_put_default_address_description(attrs, address_description_changed? \\ true)
|
||||
|
||||
defp maybe_put_default_address_description(
|
||||
%{"type" => "dns", "address" => address} = attrs,
|
||||
false
|
||||
)
|
||||
when is_binary(address) do
|
||||
Map.put(attrs, "address_description", "http://#{address}/")
|
||||
end
|
||||
|
||||
defp maybe_put_default_address_description(
|
||||
%{"type" => "ip", "address" => address} = attrs,
|
||||
false
|
||||
)
|
||||
when is_binary(address) do
|
||||
Map.put(attrs, "address_description", "http://#{address}/")
|
||||
end
|
||||
|
||||
defp maybe_put_default_address_description(attrs, false) do
|
||||
Map.put(attrs, "address_description", "")
|
||||
end
|
||||
|
||||
defp maybe_put_default_address_description(attrs, true) do
|
||||
attrs
|
||||
end
|
||||
|
||||
defp maybe_put_connections(attrs, params) do
|
||||
if site_id = params["site_id"] do
|
||||
Map.put(attrs, "connections", %{
|
||||
|
||||
@@ -116,7 +116,7 @@ defmodule Web.Resources.Show do
|
||||
<%= @resource.address %>
|
||||
</:value>
|
||||
</.vertical_table_row>
|
||||
<.vertical_table_row :if={false}>
|
||||
<.vertical_table_row>
|
||||
<:label>
|
||||
Address Description
|
||||
</:label>
|
||||
|
||||
@@ -302,6 +302,7 @@ defmodule Web.Live.Resources.NewTest do
|
||||
attrs = %{
|
||||
name: "foobar.com",
|
||||
address: "",
|
||||
address_description: String.duplicate("a", 513),
|
||||
filters: %{
|
||||
tcp: %{ports: "80, 443", enabled: true},
|
||||
udp: %{ports: "100", enabled: true}
|
||||
@@ -318,7 +319,10 @@ defmodule Web.Live.Resources.NewTest do
|
||||
|> form("form")
|
||||
# Generate onchange to trigger visible elements, otherwise the form won't be valid
|
||||
|> render_change(
|
||||
resource: %{type: :dns, filters: %{tcp: %{enabled: true}, udp: %{enabled: true}}}
|
||||
resource: %{
|
||||
type: :dns,
|
||||
filters: %{tcp: %{enabled: true}, udp: %{enabled: true}}
|
||||
}
|
||||
)
|
||||
|
||||
assert lv
|
||||
@@ -326,7 +330,7 @@ defmodule Web.Live.Resources.NewTest do
|
||||
|> render_submit()
|
||||
|> form_validation_errors() == %{
|
||||
"resource[address]" => ["can't be blank"],
|
||||
"resource[address_description]" => ["can't be blank"]
|
||||
"resource[address_description]" => ["should be at most 512 character(s)"]
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 254 KiB |
6
website/src/app/kb/deploy/resources/_page.tsx
Normal file
6
website/src/app/kb/deploy/resources/_page.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
"use client";
|
||||
import Content from "./readme.mdx";
|
||||
|
||||
export default function Page() {
|
||||
return <Content />;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import Content from "./readme.mdx";
|
||||
import _Page from "./_page";
|
||||
import { Metadata } from "next";
|
||||
import LastUpdated from "@/components/LastUpdated";
|
||||
|
||||
@@ -10,7 +10,7 @@ export const metadata: Metadata = {
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Content />
|
||||
<_Page />
|
||||
<LastUpdated dirname={__dirname} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,8 @@ import Alert from "@/components/DocsAlert";
|
||||
import SupportOptions from "@/components/SupportOptions";
|
||||
import NextStep from "@/components/NextStep";
|
||||
import PlanBadge from "@/components/PlanBadge";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
<PlanBadge plans={["starter", "team", "enterprise"]}>
|
||||
|
||||
@@ -38,6 +40,31 @@ From there, you can select the type of Resource you want to create:
|
||||
before creating the Resource.
|
||||
</Alert>
|
||||
|
||||
### Address description
|
||||
|
||||
When creating a Resource, you'll be given the option to add an
|
||||
`address_description`. If given, this will be displayed in the Client's Resource
|
||||
list to help identify the Resource. If a URL is entered, it will be displayed as
|
||||
a clickable link.
|
||||
|
||||
<Link
|
||||
target="_blank"
|
||||
href="/images/kb/deploy/resources/address_description.png"
|
||||
>
|
||||
<Image
|
||||
src="/images/kb/deploy/resources/address_description.png"
|
||||
alt="Address description field"
|
||||
width={600}
|
||||
height={600}
|
||||
className="mx-auto"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
This is commonly used to show a different address to end users than the one used
|
||||
for routing, where field validations are more restrictive. This can be useful to
|
||||
provide a bookmark to a service like `https://gitlab.company.com`, or give hints
|
||||
for accessing the service, like `10.0.0.1:2222`.
|
||||
|
||||
<PlanBadge plans={["team", "enterprise"]}>
|
||||
|
||||
### Traffic restrictions
|
||||
|
||||
Reference in New Issue
Block a user