add snmp port
This commit is contained in:
parent
88d549cd92
commit
c198fdeaa1
13 changed files with 92 additions and 13 deletions
|
|
@ -102,4 +102,7 @@
|
|||
/* Make LiveView wrapper divs transparent for layout */
|
||||
[data-phx-session], [data-phx-teleported-src] { display: contents }
|
||||
|
||||
/* Hide 1Password live region */
|
||||
[id="1p-live-region"] { display: none !important; }
|
||||
|
||||
/* This file is for your main application CSS */
|
||||
|
|
|
|||
|
|
@ -164,6 +164,9 @@ config :towerops, :mib_dirs, [
|
|||
"/usr/share/snmp/mibs"
|
||||
]
|
||||
|
||||
# Disable rate limiting in development
|
||||
config :towerops, :rate_limiting_enabled, false
|
||||
|
||||
# Configure Redis/Valkey for development
|
||||
config :towerops, :redis,
|
||||
host: "localhost",
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ defmodule Towerops.Accounts do
|
|||
terms_consent = attrs["terms_of_service_consent"] || attrs[:terms_of_service_consent]
|
||||
|
||||
multi =
|
||||
if privacy_consent in ["true", true] do
|
||||
if privacy_consent in ["true", true, "on"] do
|
||||
Ecto.Multi.run(multi, :privacy_policy_consent, fn _repo, %{user: user} ->
|
||||
grant_consent(user.id, "privacy_policy")
|
||||
end)
|
||||
|
|
@ -191,7 +191,7 @@ defmodule Towerops.Accounts do
|
|||
multi
|
||||
end
|
||||
|
||||
if terms_consent in ["true", true] do
|
||||
if terms_consent in ["true", true, "on"] do
|
||||
Ecto.Multi.run(multi, :terms_of_service_consent, fn _repo, %{user: user} ->
|
||||
grant_consent(user.id, "terms_of_service")
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ defmodule Towerops.Devices do
|
|||
end
|
||||
|
||||
defp resolve_snmp_config(device) do
|
||||
# Resolve community and version independently with hierarchical fallback
|
||||
# Resolve community, version, and port independently with hierarchical fallback
|
||||
# This allows setting version at device level while inheriting community from org
|
||||
community =
|
||||
device.snmp_community ||
|
||||
|
|
@ -310,11 +310,18 @@ defmodule Towerops.Devices do
|
|||
device.site.organization.snmp_version ||
|
||||
"2c"
|
||||
|
||||
port =
|
||||
device.snmp_port ||
|
||||
device.site.snmp_port ||
|
||||
device.site.organization.snmp_port ||
|
||||
161
|
||||
|
||||
source = determine_snmp_source(device)
|
||||
|
||||
%{
|
||||
version: version,
|
||||
community: community,
|
||||
port: port,
|
||||
source: source
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ defmodule Towerops.Organizations.Organization do
|
|||
# device)
|
||||
field :snmp_version, :string, default: "2c"
|
||||
field :snmp_community, :string
|
||||
field :snmp_port, :integer, default: 161
|
||||
|
||||
# MikroTik API credentials (organization-level defaults cascade to site → device)
|
||||
field :mikrotik_username, Binary
|
||||
|
|
@ -54,6 +55,7 @@ defmodule Towerops.Organizations.Organization do
|
|||
subscription_plan: String.t(),
|
||||
snmp_version: String.t() | nil,
|
||||
snmp_community: String.t() | nil,
|
||||
snmp_port: integer(),
|
||||
mikrotik_username: String.t() | nil,
|
||||
mikrotik_password: String.t() | nil,
|
||||
mikrotik_port: integer() | nil,
|
||||
|
|
@ -78,6 +80,7 @@ defmodule Towerops.Organizations.Organization do
|
|||
:default_agent_token_id,
|
||||
:snmp_version,
|
||||
:snmp_community,
|
||||
:snmp_port,
|
||||
:mikrotik_username,
|
||||
:mikrotik_password,
|
||||
:mikrotik_port,
|
||||
|
|
@ -89,6 +92,7 @@ defmodule Towerops.Organizations.Organization do
|
|||
|> validate_length(:name, min: 2, max: 100)
|
||||
|> validate_inclusion(:subscription_plan, ["free"])
|
||||
|> validate_inclusion(:snmp_version, ["1", "2c", "3"], message: "must be 1, 2c, or 3")
|
||||
|> validate_number(:snmp_port, greater_than: 0, less_than: 65_536)
|
||||
|> validate_mikrotik_fields()
|
||||
|> generate_slug()
|
||||
|> validate_required([:slug])
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ defmodule Towerops.Sites.Site do
|
|||
# SNMP configuration (overrides organization default)
|
||||
field :snmp_version, :string
|
||||
field :snmp_community, :string
|
||||
field :snmp_port, :integer
|
||||
|
||||
# MikroTik API credentials (overrides organization default)
|
||||
field :mikrotik_username, Binary
|
||||
|
|
@ -52,6 +53,7 @@ defmodule Towerops.Sites.Site do
|
|||
display_order: integer() | nil,
|
||||
snmp_version: String.t() | nil,
|
||||
snmp_community: String.t() | nil,
|
||||
snmp_port: integer() | nil,
|
||||
mikrotik_username: String.t() | nil,
|
||||
mikrotik_password: String.t() | nil,
|
||||
mikrotik_port: integer() | nil,
|
||||
|
|
@ -83,6 +85,7 @@ defmodule Towerops.Sites.Site do
|
|||
:parent_site_id,
|
||||
:snmp_version,
|
||||
:snmp_community,
|
||||
:snmp_port,
|
||||
:mikrotik_username,
|
||||
:mikrotik_password,
|
||||
:mikrotik_port,
|
||||
|
|
@ -95,6 +98,7 @@ defmodule Towerops.Sites.Site do
|
|||
|> validate_length(:description, max: 1000)
|
||||
|> validate_length(:location, max: 200)
|
||||
|> validate_inclusion(:snmp_version, ["1", "2c", "3"], message: "must be 1, 2c, or 3")
|
||||
|> validate_number(:snmp_port, greater_than: 0, less_than: 65_536)
|
||||
|> validate_mikrotik_fields()
|
||||
|> foreign_key_constraint(:organization_id)
|
||||
|> foreign_key_constraint(:agent_token_id)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ defmodule ToweropsWeb.UserSessionController do
|
|||
alias Towerops.Accounts
|
||||
alias ToweropsWeb.UserAuth
|
||||
|
||||
def new(conn, _params) do
|
||||
email = get_in(conn.assigns, [:current_scope, Access.key(:user), Access.key(:email)])
|
||||
def new(conn, params) do
|
||||
# Check for email in query params (from registration redirect) or current user
|
||||
email = params["email"] || get_in(conn.assigns, [:current_scope, Access.key(:user), Access.key(:email)])
|
||||
form = Phoenix.Component.to_form(%{"email" => email}, as: "user")
|
||||
|
||||
render(conn, :new, form: form)
|
||||
|
|
|
|||
|
|
@ -34,12 +34,10 @@
|
|||
{t_auth("You are running the local mail adapter.")}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-blue-700 dark:text-blue-200">
|
||||
{t_auth("To see sent emails, visit %{link}.",
|
||||
link:
|
||||
raw(
|
||||
~s(<a href="/dev/mailbox" class="font-medium underline">#{t_auth("the mailbox page")}</a>)
|
||||
)
|
||||
)}
|
||||
{t_auth("To see sent emails, visit")}
|
||||
<a href="/dev/mailbox" class="font-medium underline">
|
||||
{t_auth("the mailbox page")}
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -51,6 +49,7 @@
|
|||
<.input
|
||||
readonly={!!@current_scope}
|
||||
field={f[:email]}
|
||||
id="user_email_password"
|
||||
type="email"
|
||||
label={t_auth("Email")}
|
||||
autocomplete="email"
|
||||
|
|
@ -59,6 +58,7 @@
|
|||
/>
|
||||
<.input
|
||||
field={f[:password]}
|
||||
id="user_password"
|
||||
type="password"
|
||||
label={t_auth("Password")}
|
||||
autocomplete="current-password"
|
||||
|
|
@ -95,6 +95,7 @@
|
|||
<.input
|
||||
readonly={!!@current_scope}
|
||||
field={f[:email]}
|
||||
id="user_email_magic"
|
||||
type="email"
|
||||
label={t_auth("Email")}
|
||||
autocomplete="email"
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@
|
|||
placeholder="e.g., public"
|
||||
/>
|
||||
|
||||
<.input
|
||||
field={@form[:snmp_port]}
|
||||
type="number"
|
||||
label="SNMP Port"
|
||||
placeholder="161"
|
||||
/>
|
||||
|
||||
<%= if @organization.snmp_community do %>
|
||||
<div class="mt-6 rounded-md bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 p-4">
|
||||
<h3 class="text-sm font-medium text-amber-900 dark:text-amber-200 mb-2">
|
||||
|
|
|
|||
|
|
@ -158,6 +158,13 @@
|
|||
placeholder="Leave blank to inherit from organization"
|
||||
/>
|
||||
|
||||
<.input
|
||||
field={@form[:snmp_port]}
|
||||
type="number"
|
||||
label="SNMP Port"
|
||||
placeholder="Leave blank to inherit from organization (default: 161)"
|
||||
/>
|
||||
|
||||
<%= if @live_action == :edit and @site.snmp_community do %>
|
||||
<div class="mt-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p class="text-sm font-medium text-amber-900 dark:text-amber-200 mb-2">
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ defmodule ToweropsWeb.UserRegistrationLive do
|
|||
|> assign(:invitation_token, invitation_token)
|
||||
|> assign(:password_breach_count, nil)
|
||||
|> assign(:detected_timezone, detected_timezone)
|
||||
|> assign(:trigger_action, false)}
|
||||
|> assign(:trigger_action, false)
|
||||
|> assign(:privacy_policy_consent, false)
|
||||
|> assign(:terms_of_service_consent, false)}
|
||||
end
|
||||
|
||||
def handle_event("check_password_breach", %{"value" => password}, socket)
|
||||
|
|
@ -50,12 +52,19 @@ defmodule ToweropsWeb.UserRegistrationLive do
|
|||
|> Accounts.change_user_registration(user_params)
|
||||
|> Map.put(:action, :validate)
|
||||
|
||||
{:noreply, assign(socket, :form, to_form(changeset))}
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:form, to_form(changeset))
|
||||
|> assign(:privacy_policy_consent, user_params["privacy_policy_consent"] == "on")
|
||||
|> assign(:terms_of_service_consent, user_params["terms_of_service_consent"] == "on")}
|
||||
end
|
||||
|
||||
def handle_event("save", %{"user" => user_params}, socket) do
|
||||
invitation_token = socket.assigns.invitation_token
|
||||
|
||||
# Normalize checkbox values: HTML sends "on", but changeset expects true
|
||||
user_params = normalize_consent_params(user_params)
|
||||
|
||||
if invitation_token do
|
||||
# Registration via invitation - don't create personal org
|
||||
create_via_invitation(socket, user_params, invitation_token)
|
||||
|
|
@ -65,6 +74,18 @@ defmodule ToweropsWeb.UserRegistrationLive do
|
|||
end
|
||||
end
|
||||
|
||||
defp normalize_consent_params(params) do
|
||||
params
|
||||
|> Map.update("privacy_policy_consent", nil, fn
|
||||
"on" -> true
|
||||
val -> val
|
||||
end)
|
||||
|> Map.update("terms_of_service_consent", nil, fn
|
||||
"on" -> true
|
||||
val -> val
|
||||
end)
|
||||
end
|
||||
|
||||
defp create_with_organization(socket, user_params) do
|
||||
# Merge detected timezone into params
|
||||
user_params_with_timezone = Map.put(user_params, "timezone", socket.assigns.detected_timezone)
|
||||
|
|
@ -238,6 +259,7 @@ defmodule ToweropsWeb.UserRegistrationLive do
|
|||
id="privacy_policy_consent"
|
||||
name="user[privacy_policy_consent]"
|
||||
type="checkbox"
|
||||
checked={@privacy_policy_consent}
|
||||
required
|
||||
class="h-4 w-4 rounded border-zinc-300 text-blue-600 focus:ring-blue-500 dark:border-zinc-600 dark:bg-zinc-800 dark:checked:bg-blue-500 dark:checked:border-blue-500"
|
||||
/>
|
||||
|
|
@ -267,6 +289,7 @@ defmodule ToweropsWeb.UserRegistrationLive do
|
|||
id="terms_of_service_consent"
|
||||
name="user[terms_of_service_consent]"
|
||||
type="checkbox"
|
||||
checked={@terms_of_service_consent}
|
||||
required
|
||||
class="h-4 w-4 rounded border-zinc-300 text-blue-600 focus:ring-blue-500 dark:border-zinc-600 dark:bg-zinc-800 dark:checked:bg-blue-500 dark:checked:border-blue-500"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
defmodule Towerops.Repo.Migrations.AddSnmpPortToOrganizationsAndSites do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
# Add snmp_port to organizations with default 161
|
||||
alter table(:organizations) do
|
||||
add :snmp_port, :integer, default: 161, null: false
|
||||
end
|
||||
|
||||
# Add snmp_port to sites (nullable for inheritance)
|
||||
alter table(:sites) do
|
||||
add :snmp_port, :integer, null: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,10 @@ Devices Tested & Working
|
|||
* Ubiquiti AC, LTU, AirFiber
|
||||
* Cambium ePMP
|
||||
|
||||
2026-02-03
|
||||
* More tests
|
||||
* Bug fix: user registration
|
||||
|
||||
2026-02-02
|
||||
* Experimental mikrotik api and backup
|
||||
* Starting i18n
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue