mirror of
https://github.com/wanderer-industries/wanderer
synced 2026-04-01 16:28:04 +00:00
Compare commits
18 Commits
v1.97.1
...
passages-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e5ed22bc0 | ||
|
|
c61b8a9942 | ||
|
|
0891706489 | ||
|
|
7d720dcfb5 | ||
|
|
63b40b9c75 | ||
|
|
fc167fafaf | ||
|
|
b9197880f0 | ||
|
|
88f027facd | ||
|
|
d62ad709ab | ||
|
|
15aeb8eb85 | ||
|
|
6970db438d | ||
|
|
9ab7fcc46e | ||
|
|
931a8e629d | ||
|
|
a06df968a2 | ||
|
|
965df31da0 | ||
|
|
171591f07d | ||
|
|
9a63700dfb | ||
|
|
f00deb1556 |
36
CHANGELOG.md
36
CHANGELOG.md
@@ -2,6 +2,42 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.97.5](https://github.com/wanderer-industries/wanderer/compare/v1.97.4...v1.97.5) (2026-03-26)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* core: Fixed character re-auth issues
|
||||
|
||||
## [v1.97.4](https://github.com/wanderer-industries/wanderer/compare/v1.97.3...v1.97.4) (2026-03-26)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* core: Fixed character re-auth issues
|
||||
|
||||
## [v1.97.3](https://github.com/wanderer-industries/wanderer/compare/v1.97.2...v1.97.3) (2026-03-25)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* core: Fixed character re-auth issues
|
||||
|
||||
## [v1.97.2](https://github.com/wanderer-industries/wanderer/compare/v1.97.1...v1.97.2) (2026-03-23)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* core: Fixed tracking issues & adding systems to map from routes
|
||||
|
||||
## [v1.97.1](https://github.com/wanderer-industries/wanderer/compare/v1.97.0...v1.97.1) (2026-03-21)
|
||||
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ export const useContextMenuSystemInfoHandlers = () => {
|
||||
}
|
||||
|
||||
outCommand({
|
||||
type: OutCommand.addSystem,
|
||||
type: OutCommand.manualAddSystem,
|
||||
data: {
|
||||
system_id: solarSystemId,
|
||||
solar_system_id: parseInt(solarSystemId),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -17,12 +17,15 @@ defmodule WandererApp.Api.MapChainPassages do
|
||||
define(:read, action: :read)
|
||||
define(:by_map_id, action: :by_map_id)
|
||||
define(:by_connection, action: :by_connection)
|
||||
define(:update_mass, action: :update_mass)
|
||||
define(:by_id, get_by: [:id], action: :read)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:ship_type_id,
|
||||
:ship_name,
|
||||
:mass,
|
||||
:solar_system_source_id,
|
||||
:solar_system_target_id
|
||||
]
|
||||
@@ -30,6 +33,12 @@ defmodule WandererApp.Api.MapChainPassages do
|
||||
defaults [:create, :read, :destroy]
|
||||
|
||||
update :update do
|
||||
accept [:mass]
|
||||
require_atomic? false
|
||||
end
|
||||
|
||||
update :update_mass do
|
||||
accept [:mass]
|
||||
require_atomic? false
|
||||
end
|
||||
|
||||
@@ -37,6 +46,7 @@ defmodule WandererApp.Api.MapChainPassages do
|
||||
accept [
|
||||
:ship_type_id,
|
||||
:ship_name,
|
||||
:mass,
|
||||
:solar_system_source_id,
|
||||
:solar_system_target_id,
|
||||
:map_id,
|
||||
@@ -85,8 +95,10 @@ defmodule WandererApp.Api.MapChainPassages do
|
||||
|> WandererApp.Repo.all()
|
||||
|> Enum.map(fn [passage, character] ->
|
||||
%{
|
||||
id: passage.id,
|
||||
ship_type_id: passage.ship_type_id,
|
||||
ship_name: passage.ship_name,
|
||||
mass: passage.mass,
|
||||
inserted_at: passage.inserted_at,
|
||||
character: character
|
||||
}
|
||||
@@ -108,6 +120,7 @@ defmodule WandererApp.Api.MapChainPassages do
|
||||
|
||||
attribute :ship_type_id, :integer
|
||||
attribute :ship_name, :string
|
||||
attribute :mass, :integer
|
||||
attribute :solar_system_source_id, :integer
|
||||
attribute :solar_system_target_id, :integer
|
||||
|
||||
|
||||
@@ -115,9 +115,11 @@ defmodule WandererApp.Character.TrackingUtils do
|
||||
end)}
|
||||
end
|
||||
|
||||
# Filter characters to only include those with actual tracking permission
|
||||
# This prevents showing characters in the tracking dialog that will fail when toggled
|
||||
defp filter_characters_with_tracking_permission(characters, %{id: map_id, owner_id: owner_id}) do
|
||||
@doc """
|
||||
Filters a list of characters to only include those with actual tracking permission on a map.
|
||||
This prevents showing characters in the tracking dialog that will fail when toggled.
|
||||
"""
|
||||
def filter_characters_with_tracking_permission(characters, %{id: map_id, owner_id: owner_id}) do
|
||||
# Load ACLs with members properly (same approach as get_map_characters)
|
||||
acls = load_map_acls_with_members(map_id)
|
||||
|
||||
|
||||
@@ -804,7 +804,8 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
})
|
||||
|
||||
if count >= 3 do
|
||||
Logger.warning("TOKEN_REFRESH_FAILED: Invalid grant error (#{count}/3, invalidating tokens)",
|
||||
Logger.warning(
|
||||
"TOKEN_REFRESH_FAILED: Invalid grant error (#{count}/3, invalidating tokens)",
|
||||
character_id: character_id,
|
||||
error_message: error_message,
|
||||
time_since_expiry_seconds: time_since_expiry,
|
||||
@@ -861,7 +862,8 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
expires_at,
|
||||
_scopes
|
||||
) do
|
||||
time_since_expiry = DateTime.diff(DateTime.utc_now(), DateTime.from_unix!(expires_at), :second)
|
||||
time_since_expiry =
|
||||
DateTime.diff(DateTime.utc_now(), DateTime.from_unix!(expires_at), :second)
|
||||
|
||||
Logger.warning("TOKEN_REFRESH_FAILED: Transient OAuth2 error during token refresh",
|
||||
character_id: character_id,
|
||||
@@ -879,7 +881,8 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
end
|
||||
|
||||
defp handle_refresh_token_result(error, _character, character_id, expires_at, _scopes) do
|
||||
time_since_expiry = DateTime.diff(DateTime.utc_now(), DateTime.from_unix!(expires_at), :second)
|
||||
time_since_expiry =
|
||||
DateTime.diff(DateTime.utc_now(), DateTime.from_unix!(expires_at), :second)
|
||||
|
||||
Logger.warning("TOKEN_REFRESH_FAILED: Unexpected error during token refresh",
|
||||
character_id: character_id,
|
||||
@@ -897,20 +900,48 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
end
|
||||
|
||||
defp invalidate_character_tokens(character, character_id, expires_at, scopes) do
|
||||
attrs = %{access_token: nil, refresh_token: nil, expires_at: expires_at, scopes: scopes}
|
||||
|
||||
with {:ok, _} <- WandererApp.Api.Character.update(character, attrs) do
|
||||
WandererApp.Character.update_character(character_id, attrs)
|
||||
# Skip invalidation if the character was recently re-authorized via SSO.
|
||||
# This protects fresh tokens from being wiped by transient invalid_grant
|
||||
# errors that can occur shortly after re-auth.
|
||||
if WandererApp.Cache.lookup!("character:#{character_id}:reauth_grace", false) do
|
||||
Logger.info(
|
||||
"[ApiClient] Skipping token invalidation for #{character_id} - within re-auth grace period"
|
||||
)
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to clear tokens for #{character_id}: #{inspect(error)}")
|
||||
end
|
||||
# Re-load from DB to avoid race with concurrent re-auth
|
||||
case WandererApp.Api.Character.by_id(character_id) do
|
||||
{:ok, current_character} ->
|
||||
# Only invalidate if tokens haven't been refreshed since we started
|
||||
if current_character.access_token == character.access_token do
|
||||
attrs = %{
|
||||
access_token: nil,
|
||||
refresh_token: nil,
|
||||
expires_at: expires_at,
|
||||
scopes: scopes
|
||||
}
|
||||
|
||||
Phoenix.PubSub.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}",
|
||||
:character_token_invalid
|
||||
)
|
||||
with {:ok, _} <- WandererApp.Api.Character.update(current_character, attrs) do
|
||||
WandererApp.Character.update_character(character_id, attrs)
|
||||
else
|
||||
error ->
|
||||
Logger.error("Failed to clear tokens for #{character_id}: #{inspect(error)}")
|
||||
end
|
||||
|
||||
Phoenix.PubSub.broadcast(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}",
|
||||
:character_token_invalid
|
||||
)
|
||||
else
|
||||
Logger.info(
|
||||
"[ApiClient] Skipping token invalidation for #{character_id} - tokens were refreshed concurrently"
|
||||
)
|
||||
end
|
||||
|
||||
{:error, _} ->
|
||||
Logger.error("Failed to load character #{character_id} for token invalidation")
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@@ -336,7 +336,11 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
"[CharacterCleanup] Map #{map_id} - untracking settings and removing character #{s.character_id}"
|
||||
end)
|
||||
|
||||
WandererApp.MapCharacterSettingsRepo.untrack!(%{map_id: s.map_id, character_id: s.character_id})
|
||||
WandererApp.MapCharacterSettingsRepo.untrack!(%{
|
||||
map_id: s.map_id,
|
||||
character_id: s.character_id
|
||||
})
|
||||
|
||||
remove_character(map_id, s.character_id)
|
||||
end)
|
||||
|
||||
|
||||
@@ -278,8 +278,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|
||||
),
|
||||
do:
|
||||
update_connection(map_id, :update_mass_status, [:mass_status], connection_update, fn
|
||||
%{mass_status: old_mass_status},
|
||||
%{mass_status: mass_status} = updated_connection ->
|
||||
%{mass_status: old_mass_status}, %{mass_status: mass_status} = updated_connection ->
|
||||
if mass_status != old_mass_status do
|
||||
maybe_update_linked_signature_mass_status(map_id, updated_connection)
|
||||
end
|
||||
|
||||
@@ -789,8 +789,7 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
defp do_add_system(
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: solar_system_id,
|
||||
coordinates: coordinates
|
||||
solar_system_id: solar_system_id
|
||||
} = system_info,
|
||||
user_id,
|
||||
character_id
|
||||
@@ -803,19 +802,14 @@ defmodule WandererApp.Map.Server.SystemsImpl do
|
||||
rtree_name = "rtree_#{map_id}"
|
||||
|
||||
%{"x" => x, "y" => y} =
|
||||
coordinates
|
||||
system_info
|
||||
|> Map.get(:coordinates)
|
||||
|> case do
|
||||
%{"x" => x, "y" => y} ->
|
||||
%{"x" => x, "y" => y}
|
||||
|
||||
_ ->
|
||||
%{x: x, y: y} =
|
||||
WandererApp.Map.PositionCalculator.get_new_system_position(
|
||||
nil,
|
||||
rtree_name,
|
||||
map_opts
|
||||
)
|
||||
|
||||
{:ok, %{x: x, y: y}} = calc_new_system_position(map_id, nil, rtree_name, map_opts)
|
||||
%{"x" => x, "y" => y}
|
||||
end
|
||||
|
||||
|
||||
@@ -46,14 +46,18 @@ defmodule WandererApp.Ueberauth.Strategy.Eve do
|
||||
|> with_param(:hl, conn)
|
||||
|> with_state_param(conn)
|
||||
|
||||
opts = oauth_client_options_from_conn(conn, with_wallet, is_admin?)
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"eve_auth_#{params[:state]}",
|
||||
[with_wallet: with_wallet, is_admin?: is_admin?],
|
||||
[
|
||||
with_wallet: with_wallet,
|
||||
is_admin?: is_admin?,
|
||||
tracking_pool: Keyword.get(opts, :tracking_pool)
|
||||
],
|
||||
ttl: :timer.minutes(30)
|
||||
)
|
||||
|
||||
opts = oauth_client_options_from_conn(conn, with_wallet, is_admin?)
|
||||
|
||||
redirect!(conn, WandererApp.Ueberauth.Strategy.Eve.OAuth.authorize_url!(params, opts))
|
||||
|
||||
false ->
|
||||
|
||||
@@ -10,6 +10,12 @@ defmodule WandererAppWeb.AuthController do
|
||||
def callback(%{assigns: %{ueberauth_auth: auth, current_user: user} = _assigns} = conn, _params) do
|
||||
active_tracking_pool = WandererApp.Character.TrackingConfigUtils.get_active_pool!()
|
||||
|
||||
Logger.info(
|
||||
"[AuthController] SSO callback SUCCESS for eve_id=#{auth.info.email}, " <>
|
||||
"has_token=#{not is_nil(auth.credentials.token)}, " <>
|
||||
"has_refresh=#{not is_nil(auth.credentials.refresh_token)}"
|
||||
)
|
||||
|
||||
character_data = %{
|
||||
eve_id: "#{auth.info.email}",
|
||||
name: auth.info.name,
|
||||
@@ -40,8 +46,25 @@ defmodule WandererAppWeb.AuthController do
|
||||
character
|
||||
|> WandererApp.Api.Character.update(character_update)
|
||||
|
||||
Logger.info(
|
||||
"[AuthController] Character #{character.id} tokens updated in DB, " <>
|
||||
"access_token_present=#{not is_nil(character.access_token)}"
|
||||
)
|
||||
|
||||
WandererApp.Character.update_character(character.id, character_update)
|
||||
|
||||
# Clear the invalid_grant counter so stale failures don't cause
|
||||
# premature token invalidation after a successful re-auth
|
||||
WandererApp.Cache.delete("character:#{character.id}:invalid_grant_count")
|
||||
|
||||
# Set a grace period to protect fresh tokens from being wiped by
|
||||
# in-flight or immediately-subsequent invalid_grant errors
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character.id}:reauth_grace",
|
||||
true,
|
||||
ttl: :timer.minutes(5)
|
||||
)
|
||||
|
||||
# Update corporation/alliance data from ESI to ensure access control is current
|
||||
update_character_affiliation(character)
|
||||
|
||||
@@ -96,7 +119,16 @@ defmodule WandererAppWeb.AuthController do
|
||||
end
|
||||
|
||||
def callback(conn, _params) do
|
||||
# This runs when Ueberauth auth FAILED — tokens are NOT updated
|
||||
ueberauth_failure = conn.assigns[:ueberauth_failure]
|
||||
|
||||
Logger.warning(
|
||||
"[AuthController] SSO callback FAILED - no ueberauth_auth in assigns. " <>
|
||||
"Failure: #{inspect(ueberauth_failure)}"
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_flash(:error, "Authorization failed. Please try again.")
|
||||
|> redirect(to: "/characters")
|
||||
end
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ defmodule WandererAppWeb.RouteBuilderController do
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("[RouteBuilderController] find_closest failed: #{inspect(reason)}")
|
||||
|
||||
conn
|
||||
|> put_status(:bad_gateway)
|
||||
|> json(%{error: "route_builder_failed"})
|
||||
|
||||
@@ -22,6 +22,11 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
"character:#{character_id}:corporation"
|
||||
)
|
||||
|
||||
Phoenix.PubSub.subscribe(
|
||||
WandererApp.PubSub,
|
||||
"character:#{character_id}"
|
||||
)
|
||||
|
||||
:ok = WandererApp.Character.TrackerManager.start_tracking(character_id)
|
||||
end)
|
||||
|
||||
@@ -83,12 +88,11 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
{:ok, _} = WandererApp.MapCharacterSettingsRepo.untrack(settings)
|
||||
end)
|
||||
|
||||
{:ok, updated_character} =
|
||||
socket.assigns.characters
|
||||
|> Enum.find(&(&1.id == character_id))
|
||||
|> WandererApp.Api.Character.mark_as_deleted()
|
||||
# Load character from DB instead of using plain map from assigns
|
||||
{:ok, character} = WandererApp.Api.Character.by_id(character_id)
|
||||
{:ok, _updated_character} = WandererApp.Api.Character.mark_as_deleted(character)
|
||||
|
||||
WandererApp.Character.update_character(character_id, updated_character)
|
||||
WandererApp.Character.update_character(character_id, %{deleted: true, user_id: nil})
|
||||
|
||||
{:ok, characters} =
|
||||
WandererApp.Api.Character.active_by_user(%{user_id: socket.assigns.user_id})
|
||||
@@ -148,6 +152,18 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
{:noreply, socket |> assign(characters: characters |> Enum.map(&map_ui_character/1))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(
|
||||
event,
|
||||
socket
|
||||
)
|
||||
when event in [:character_token_invalid, :token_updated] do
|
||||
{:ok, characters} =
|
||||
WandererApp.Api.Character.active_by_user(%{user_id: socket.assigns.user_id})
|
||||
|
||||
{:noreply, socket |> assign(characters: characters |> Enum.map(&map_ui_character/1))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(
|
||||
_event,
|
||||
|
||||
@@ -54,7 +54,7 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|
||||
selected_map_slug: map_slug
|
||||
)
|
||||
|> assign_async(:characters, fn ->
|
||||
WandererApp.Maps.load_characters(selected_map, current_user.id)
|
||||
load_trackable_characters(selected_map, current_user.id)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -100,37 +100,55 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|
||||
selected_map = socket.assigns.selected_map
|
||||
%{result: characters} = socket.assigns.characters
|
||||
|
||||
case characters |> Enum.find(&(&1.id == character_id)) do
|
||||
%{tracked: current_tracked, eve_id: eve_id} ->
|
||||
# Use TrackingUtils.update_tracking to properly set/unset the tracking_start_time
|
||||
# cache key, which is required for the character to appear in get_tracked_character_ids
|
||||
case TrackingUtils.update_tracking(
|
||||
selected_map.id,
|
||||
eve_id,
|
||||
current_user.id,
|
||||
not current_tracked,
|
||||
self(),
|
||||
false
|
||||
) do
|
||||
{:ok, _tracking_data, _event} ->
|
||||
:ok
|
||||
result =
|
||||
case characters |> Enum.find(&(&1.id == character_id)) do
|
||||
%{tracked: current_tracked, eve_id: eve_id} ->
|
||||
# Use TrackingUtils.update_tracking to properly set/unset the tracking_start_time
|
||||
# cache key, which is required for the character to appear in get_tracked_character_ids
|
||||
case TrackingUtils.update_tracking(
|
||||
selected_map.id,
|
||||
eve_id,
|
||||
current_user.id,
|
||||
not current_tracked,
|
||||
self(),
|
||||
false
|
||||
) do
|
||||
{:ok, _tracking_data, _event} ->
|
||||
:ok
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error(
|
||||
"Failed to toggle tracking for character #{character_id} on map #{selected_map.id}: #{inspect(reason)}"
|
||||
)
|
||||
end
|
||||
{:error, reason} ->
|
||||
Logger.error(
|
||||
"Failed to toggle tracking for character #{character_id} on map #{selected_map.id}: #{inspect(reason)}"
|
||||
)
|
||||
|
||||
nil ->
|
||||
Logger.warning(
|
||||
"Character #{character_id} not found in available characters for map #{selected_map.id}"
|
||||
)
|
||||
end
|
||||
{:error, reason}
|
||||
end
|
||||
|
||||
nil ->
|
||||
Logger.warning(
|
||||
"Character #{character_id} not found in available characters for map #{selected_map.id}"
|
||||
)
|
||||
|
||||
{:error, "Character not found"}
|
||||
end
|
||||
|
||||
socket =
|
||||
case result do
|
||||
{:error, _reason} ->
|
||||
put_flash(
|
||||
socket,
|
||||
:error,
|
||||
"Failed to toggle tracking. Character may not have sufficient permissions on this map."
|
||||
)
|
||||
|
||||
_ ->
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign_async(:characters, fn ->
|
||||
WandererApp.Maps.load_characters(selected_map, current_user.id)
|
||||
load_trackable_characters(selected_map, current_user.id)
|
||||
end)}
|
||||
end
|
||||
|
||||
@@ -154,10 +172,21 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign_async(:characters, fn ->
|
||||
WandererApp.Maps.load_characters(selected_map, current_user.id)
|
||||
load_trackable_characters(selected_map, current_user.id)
|
||||
end)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(_event, socket), do: {:noreply, socket}
|
||||
|
||||
defp load_trackable_characters(map, user_id) do
|
||||
case WandererApp.Maps.load_characters(map, user_id) do
|
||||
{:ok, %{characters: characters}} ->
|
||||
filtered = TrackingUtils.filter_characters_with_tracking_permission(characters, map)
|
||||
{:ok, %{characters: filtered}}
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -265,6 +265,42 @@ defmodule WandererAppWeb.MapConnectionsEventHandler do
|
||||
{:reply, passages, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"update_passage_mass",
|
||||
%{"id" => passage_id, "mass" => mass} = _event,
|
||||
%{
|
||||
assigns: %{
|
||||
has_tracked_characters?: true,
|
||||
user_permissions: %{update_system: true}
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
mass_value =
|
||||
cond do
|
||||
is_integer(mass) ->
|
||||
mass
|
||||
|
||||
is_binary(mass) ->
|
||||
case Integer.parse(mass) do
|
||||
{int_val, _} -> int_val
|
||||
:error -> nil
|
||||
end
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
|
||||
case WandererApp.Api.MapChainPassages.by_id(passage_id) do
|
||||
{:ok, passage} when not is_nil(passage) ->
|
||||
WandererApp.Api.MapChainPassages.update_mass(passage, %{mass: mass_value})
|
||||
|
||||
_ ->
|
||||
Logger.warning("update_passage_mass: passage not found id=#{passage_id}")
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(event, body, socket),
|
||||
do: MapCoreEventHandler.handle_ui_event(event, body, socket)
|
||||
|
||||
|
||||
@@ -614,7 +614,8 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
nil
|
||||
end
|
||||
|
||||
expired_characters = tracked_characters |> Enum.filter(&(&1.access_token == nil)) |> Enum.map(& &1.eve_id)
|
||||
expired_characters =
|
||||
tracked_characters |> Enum.filter(&(&1.access_token == nil)) |> Enum.map(& &1.eve_id)
|
||||
|
||||
initial_data =
|
||||
%{
|
||||
|
||||
@@ -176,12 +176,14 @@ defmodule WandererAppWeb.MapRoutesEventHandler do
|
||||
routes_type = Map.get(event, "type", "blueLoot")
|
||||
security_type = Map.get(event, "securityType", "both")
|
||||
is_subscription_active? = Map.get(socket.assigns, :is_subscription_active?, false)
|
||||
|
||||
routes_limit =
|
||||
if is_subscription_active? == true do
|
||||
@paid_routes_limit
|
||||
else
|
||||
Map.get(@alpha_routes_limit_by_type, routes_type, @default_alpha_routes_limit)
|
||||
end
|
||||
|
||||
routes_settings =
|
||||
routes_settings
|
||||
|> get_routes_settings()
|
||||
|
||||
@@ -128,6 +128,33 @@ defmodule WandererAppWeb.MapSystemsEventHandler do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"manual_add_system",
|
||||
%{"solar_system_id" => solar_system_id} = _event,
|
||||
%{
|
||||
assigns: %{
|
||||
current_user: %{id: current_user_id},
|
||||
has_tracked_characters?: true,
|
||||
map_id: map_id,
|
||||
main_character_id: main_character_id,
|
||||
user_permissions: %{add_system: true}
|
||||
}
|
||||
} =
|
||||
socket
|
||||
)
|
||||
when not is_nil(main_character_id) do
|
||||
WandererApp.Map.Server.add_system(
|
||||
map_id,
|
||||
%{
|
||||
solar_system_id: solar_system_id
|
||||
},
|
||||
current_user_id,
|
||||
main_character_id
|
||||
)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"manual_paste_systems_and_connections",
|
||||
%{
|
||||
|
||||
@@ -88,7 +88,8 @@ defmodule WandererAppWeb.MapEventHandler do
|
||||
"update_connection_mass_status",
|
||||
"update_connection_ship_size_type",
|
||||
"update_connection_locked",
|
||||
"update_connection_custom_info"
|
||||
"update_connection_custom_info",
|
||||
"update_passage_mass"
|
||||
]
|
||||
|
||||
@map_activity_events [
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
|
||||
@version "1.97.1"
|
||||
@version "1.97.5"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddMassToMapChainPassages do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:maps_v1) do
|
||||
modify :scopes, {:array, :text}, default: '{wormholes}'
|
||||
end
|
||||
|
||||
alter table(:map_chain_passages_v1) do
|
||||
add :mass, :bigint
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_chain_passages_v1) do
|
||||
remove :mass
|
||||
end
|
||||
|
||||
alter table(:maps_v1) do
|
||||
modify :scopes, {:array, :text}, default: nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,177 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "ship_type_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "mass",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_passages_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": null,
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_passages_v1_character_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "character_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "00AA9FB7759FCDF16C5C627E6735E0B568E517A360F2002AFE00018BD6CD8F2A",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_passages_v1"
|
||||
}
|
||||
277
priv/resource_snapshots/repo/maps_v1/20260331192521.json
Normal file
277
priv/resource_snapshots/repo/maps_v1/20260331192521.json
Normal file
@@ -0,0 +1,277 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "slug",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "personal_note",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "public_api_key",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "[]",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "hubs",
|
||||
"type": [
|
||||
"array",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "\"wormholes\"",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "scope",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "only_tracked_characters",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "options",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "webhooks_enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "sse_enabled",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "'{wormholes}'",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": [
|
||||
"array",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"precision": null,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "maps_v1_owner_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
},
|
||||
"scale": null,
|
||||
"size": null,
|
||||
"source": "owner_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "21B2A84E49086754B40476C11B4EA5F576E8537449FB776941098773C5CD705F",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "maps_v1_unique_public_api_key_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "public_api_key"
|
||||
}
|
||||
],
|
||||
"name": "unique_public_api_key",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "maps_v1_unique_slug_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "slug"
|
||||
}
|
||||
],
|
||||
"name": "unique_slug",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "maps_v1"
|
||||
}
|
||||
@@ -155,7 +155,7 @@ defmodule WandererAppWeb.OpenAPISpecAnalyzer do
|
||||
# Categorize schemas based on naming patterns
|
||||
request_schemas = Enum.filter(schema_names, &String.contains?(&1, "Request"))
|
||||
response_schemas = Enum.filter(schema_names, &String.contains?(&1, "Response"))
|
||||
shared_schemas = schema_names -- request_schemas -- response_schemas
|
||||
shared_schemas = schema_names -- (request_schemas -- response_schemas)
|
||||
|
||||
%{
|
||||
total: length(schema_names),
|
||||
|
||||
Reference in New Issue
Block a user