Compare commits

..

3 Commits

Author SHA1 Message Date
CI
e49471fb94 chore: release version v1.96.6 2026-03-13 11:37:39 +00:00
Dmitry Popov
ad35d9e172 fix(core): Fixed tracking issues 2026-03-13 12:33:18 +01:00
CI
d8fb980a3b chore: [skip ci] 2026-02-27 17:48:31 +00:00
5 changed files with 110 additions and 21 deletions

View File

@@ -2,6 +2,15 @@
<!-- changelog -->
## [v1.96.6](https://github.com/wanderer-industries/wanderer/compare/v1.96.5...v1.96.6) (2026-03-13)
### Bug Fixes:
* core: Fixed tracking issues
## [v1.96.5](https://github.com/wanderer-industries/wanderer/compare/v1.96.4...v1.96.5) (2026-02-27)

View File

@@ -215,8 +215,11 @@ defmodule WandererApp.Character.TrackingUtils do
end
end
# Check if a character has permission to be tracked on a map
defp check_character_tracking_permission(character, map_id) do
@doc """
Checks if a character has permission to be tracked on a map.
Returns {:ok, :allowed} or {:error, reason}.
"""
def check_character_tracking_permission(character, map_id) do
with {:ok, %{acls: acls, owner_id: owner_id}} <-
WandererApp.MapRepo.get(map_id,
acls: [

View File

@@ -754,6 +754,9 @@ defmodule WandererApp.Esi.ApiClient do
new_expires_at: token.expires_at
)
# Clear any previous invalid_grant failure counter on successful refresh
WandererApp.Cache.delete("character:#{character_id}:invalid_grant_count")
{:ok, _character} =
character
|> WandererApp.Api.Character.update(%{
@@ -786,12 +789,12 @@ defmodule WandererApp.Esi.ApiClient do
expires_at_datetime = DateTime.from_unix!(expires_at)
time_since_expiry = DateTime.diff(DateTime.utc_now(), expires_at_datetime, :second)
Logger.warning("TOKEN_REFRESH_FAILED: Invalid grant error during token refresh",
character_id: character_id,
error_message: error_message,
time_since_expiry_seconds: time_since_expiry,
original_expires_at: expires_at
)
# Track consecutive invalid_grant failures before permanently invalidating tokens.
# EVE SSO can return invalid_grant for transient server issues, so we require
# 3 consecutive failures before wiping tokens.
fail_key = "character:#{character_id}:invalid_grant_count"
count = WandererApp.Cache.lookup!(fail_key, 0) + 1
WandererApp.Cache.put(fail_key, count, ttl: :timer.hours(2))
# Emit telemetry for token refresh failures
:telemetry.execute([:wanderer_app, :token, :refresh_failed], %{count: 1}, %{
@@ -800,8 +803,28 @@ defmodule WandererApp.Esi.ApiClient do
time_since_expiry: time_since_expiry
})
invalidate_character_tokens(character, character_id, expires_at, scopes)
{:error, :invalid_grant}
if count >= 3 do
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,
original_expires_at: expires_at
)
WandererApp.Cache.delete(fail_key)
invalidate_character_tokens(character, character_id, expires_at, scopes)
{:error, :invalid_grant}
else
Logger.warning(
"TOKEN_REFRESH_FAILED: Invalid grant error (#{count}/3, deferring invalidation)",
character_id: character_id,
error_message: error_message,
time_since_expiry_seconds: time_since_expiry,
original_expires_at: expires_at
)
{:error, :token_refresh_failed}
end
end
defp handle_refresh_token_result(

View File

@@ -159,7 +159,7 @@ defmodule WandererApp.Map.Server.CharactersImpl do
if is_same_user_as_owner do
# All characters from the map owner's account have full access
:ok
{:ok, character_id}
else
[character_permissions] =
WandererApp.Permissions.check_characters_access([character], acls)
@@ -179,12 +179,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
{:remove_character, character_id, :no_track_permission}
_ ->
:ok
{:ok, character_id}
end
end
_ ->
:ok
{:ok, character_id}
end
end,
timeout: :timer.seconds(60),
@@ -193,7 +193,26 @@ defmodule WandererApp.Map.Server.CharactersImpl do
)
|> Enum.reduce([], fn
{:ok, {:remove_character, character_id, reason}}, acc ->
[{character_id, reason} | acc]
# Track consecutive permission failures - only remove after 3 consecutive hourly failures
fail_key = "map_#{map_id}:char_#{character_id}:perm_fail_count"
count = WandererApp.Cache.lookup!(fail_key, 0) + 1
WandererApp.Cache.put(fail_key, count, ttl: :timer.hours(4))
if count >= 3 do
WandererApp.Cache.delete(fail_key)
[{character_id, reason} | acc]
else
Logger.info(
"[CharacterCleanup] Character #{character_id} permission fail #{count}/3 on map #{map_id}, deferring removal"
)
acc
end
{:ok, {:ok, character_id}}, acc ->
# Character passed permission check - clear any previous failure counter
WandererApp.Cache.delete("map_#{map_id}:char_#{character_id}:perm_fail_count")
acc
{:ok, _result}, acc ->
acc
@@ -966,13 +985,48 @@ defmodule WandererApp.Map.Server.CharactersImpl do
character_id: character_id
}) do
{:ok, %{tracked: false}} ->
# Was explicitly untracked (e.g., by permission cleanup) - don't re-enable
Logger.debug(fn ->
"[CharactersImpl] Skipping re-track for character #{character_id} on map #{map_id} - " <>
"character was explicitly untracked"
end)
# Was previously untracked (by system cleanup or user).
# If character now has valid tokens, check permissions and auto-restore tracking.
# This handles the case where re-auth gives fresh tokens but DB still says tracked: false.
if not is_nil(character.access_token) do
case WandererApp.Character.TrackingUtils.check_character_tracking_permission(
character,
map_id
) do
{:ok, :allowed} ->
Logger.info(
"[CharactersImpl] Auto-restoring tracking for character #{character_id} on map #{map_id} - " <>
"character has valid tokens and permissions after reconnect"
)
add_character(map_id, character, false)
add_character(map_id, character, true)
WandererApp.MapCharacterSettingsRepo.track(%{
map_id: map_id,
character_id: character_id
})
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
map_id: map_id,
track: true
})
_ ->
Logger.debug(fn ->
"[CharactersImpl] Skipping re-track for character #{character_id} on map #{map_id} - " <>
"character lacks permissions"
end)
add_character(map_id, character, false)
end
else
Logger.debug(fn ->
"[CharactersImpl] Skipping re-track for character #{character_id} on map #{map_id} - " <>
"character has no valid access token"
end)
add_character(map_id, character, false)
end
_ ->
# New character or already tracked - enable tracking

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.96.5"
@version "1.96.6"
def project do
[