Compare commits

...

42 Commits

Author SHA1 Message Date
CI
1394e2897e chore: release version v1.76.13 2025-08-27 20:46:18 +00:00
Dmitry Popov
5117a1c5af fix(Core): Fixed maps start timeout 2025-08-27 22:42:29 +02:00
CI
3c62403f33 chore: [skip ci] 2025-08-20 14:37:18 +00:00
CI
a4760f5162 chore: release version v1.76.12 2025-08-20 14:37:18 +00:00
Dmitry Popov
b071070431 fix(Core): Reduced ESI api calls to update character corp/ally info 2025-08-20 16:36:46 +02:00
CI
3bcb9628e7 chore: [skip ci] 2025-08-20 07:53:27 +00:00
CI
e62c4cf5bf chore: release version v1.76.11 2025-08-20 07:53:27 +00:00
Dmitry Popov
af46962ce4 Merge pull request #503 from wanderer-industries/revert-501-guarzo/sigsfix
Revert "fix: default signature types not being shown"
2025-08-20 11:53:00 +04:00
Dmitry Popov
0b0967830b Revert "fix: default signature types not being shown" 2025-08-20 11:52:34 +04:00
CI
172251a208 chore: [skip ci] 2025-08-18 23:28:33 +00:00
CI
8a6fb63d55 chore: release version v1.76.10 2025-08-18 23:28:33 +00:00
Dmitry Popov
9652959e5e fix(Core): Added character trackers start queue 2025-08-19 01:27:58 +02:00
CI
825ef46d41 chore: [skip ci] 2025-08-18 11:42:47 +00:00
CI
ad9f7c6b95 chore: release version v1.76.9 2025-08-18 11:42:47 +00:00
Dmitry Popov
b960b5c149 Merge pull request #501 from guarzo/guarzo/sigsfix
fix: default signature types not being shown
2025-08-18 15:42:14 +04:00
CI
0f092d21f9 chore: [skip ci] 2025-08-17 21:28:20 +00:00
CI
031576caa6 chore: release version v1.76.8 2025-08-17 21:28:20 +00:00
Dmitry Popov
7a97a96c42 fix(Core): added DB connection default timeouts 2025-08-17 23:27:21 +02:00
CI
2efb2daba0 chore: [skip ci] 2025-08-16 22:17:44 +00:00
CI
4374c39924 chore: release version v1.76.7 2025-08-16 22:17:44 +00:00
Dmitry Popov
15711495c7 fix(Core): Fixed auth redirect URL 2025-08-17 00:17:17 +02:00
guarzo
236f803427 fix: default signature types not being shown 2025-08-15 23:03:22 +00:00
CI
6772130f2a chore: [skip ci] 2025-08-15 15:27:07 +00:00
CI
ddd72f3fac chore: release version v1.76.6 2025-08-15 15:27:07 +00:00
Dmitry Popov
6e262835ef Merge pull request #500 from guarzo/guarzo/moressefix
fix: empty subscriptions for sse
2025-08-15 19:26:34 +04:00
guarzo
2f3b8ddc5f fix: empty subscriptions for sse 2025-08-15 11:08:40 -04:00
CI
cea3a74b34 chore: [skip ci] 2025-08-15 10:29:11 +00:00
CI
867941a233 chore: release version v1.76.5 2025-08-15 10:29:11 +00:00
Dmitry Popov
3ff388a16d fix(Core): fixed tracking paused issues, fixed user activity data 2025-08-15 12:28:36 +02:00
CI
f4248e9ab9 chore: [skip ci] 2025-08-14 23:40:20 +00:00
CI
507b3289c7 chore: release version v1.76.4 2025-08-14 23:40:20 +00:00
Dmitry Popov
9e1dfc48d5 Merge pull request #499 from guarzo/guarzo/relfixes 2025-08-15 03:39:50 +04:00
guarzo
518cbc7b5d fix: timestamp errors for sse and tracking 2025-08-14 19:22:30 -04:00
CI
ccc8db0620 chore: [skip ci] 2025-08-14 21:41:56 +00:00
CI
7cfb663efd chore: release version v1.76.3 2025-08-14 21:41:56 +00:00
Dmitry Popov
e5103cc925 Merge branch 'main' of github.com:wanderer-industries/wanderer 2025-08-14 23:41:30 +02:00
Dmitry Popov
26458f5a19 chore: Get rid of tracking pauses 2025-08-14 23:41:26 +02:00
CI
79d5ec6caf chore: [skip ci] 2025-08-14 20:56:39 +00:00
CI
034d461ab6 chore: release version v1.76.2 2025-08-14 20:56:39 +00:00
Dmitry Popov
2e9c1c170c chore: Get rid of tracking pauses 2025-08-14 22:56:08 +02:00
Dmitry Popov
24ad3b2c61 chore: [skip ci] 2025-08-14 11:28:56 +02:00
CI
288f55dc2f chore: [skip ci] 2025-08-13 16:15:29 +00:00
21 changed files with 331 additions and 186 deletions

View File

@@ -123,9 +123,9 @@ jobs:
id: get-content
with:
stringToTruncate: |
📣 Wanderer **ARM** (https://hub.docker.com/repository/docker/wandererltd/community-edition-arm/general) release available 🎉
📣 Wanderer **ARM** release available 🎉
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
**Version**: :${{ steps.get-latest-tag.outputs.tag }}
${{ steps.extract-changelog.outputs.body }}
maxLength: 500
@@ -184,4 +184,4 @@ jobs:
uses: tsickert/discord-webhook@v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: Release notes ${{ needs.docker.outputs.release-notes }}
content: ${{ needs.docker.outputs.release-notes }}

View File

@@ -184,4 +184,4 @@ jobs:
uses: tsickert/discord-webhook@v5.3.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
content: Release notes ${{ needs.docker.outputs.release-notes }}
content: ${{ needs.docker.outputs.release-notes }}

View File

@@ -2,6 +2,102 @@
<!-- changelog -->
## [v1.76.13](https://github.com/wanderer-industries/wanderer/compare/v1.76.12...v1.76.13) (2025-08-27)
### Bug Fixes:
* Core: Fixed maps start timeout
## [v1.76.12](https://github.com/wanderer-industries/wanderer/compare/v1.76.11...v1.76.12) (2025-08-20)
### Bug Fixes:
* Core: Reduced ESI api calls to update character corp/ally info
## [v1.76.11](https://github.com/wanderer-industries/wanderer/compare/v1.76.10...v1.76.11) (2025-08-20)
## [v1.76.10](https://github.com/wanderer-industries/wanderer/compare/v1.76.9...v1.76.10) (2025-08-18)
### Bug Fixes:
* Core: Added character trackers start queue
## [v1.76.9](https://github.com/wanderer-industries/wanderer/compare/v1.76.8...v1.76.9) (2025-08-18)
### Bug Fixes:
* default signature types not being shown
## [v1.76.8](https://github.com/wanderer-industries/wanderer/compare/v1.76.7...v1.76.8) (2025-08-17)
### Bug Fixes:
* Core: added DB connection default timeouts
## [v1.76.7](https://github.com/wanderer-industries/wanderer/compare/v1.76.6...v1.76.7) (2025-08-16)
### Bug Fixes:
* Core: Fixed auth redirect URL
## [v1.76.6](https://github.com/wanderer-industries/wanderer/compare/v1.76.5...v1.76.6) (2025-08-15)
### Bug Fixes:
* empty subscriptions for sse
## [v1.76.5](https://github.com/wanderer-industries/wanderer/compare/v1.76.4...v1.76.5) (2025-08-15)
### Bug Fixes:
* Core: fixed tracking paused issues, fixed user activity data
## [v1.76.4](https://github.com/wanderer-industries/wanderer/compare/v1.76.3...v1.76.4) (2025-08-14)
### Bug Fixes:
* timestamp errors for sse and tracking
## [v1.76.3](https://github.com/wanderer-industries/wanderer/compare/v1.76.2...v1.76.3) (2025-08-14)
## [v1.76.2](https://github.com/wanderer-industries/wanderer/compare/v1.76.1...v1.76.2) (2025-08-14)
## [v1.76.1](https://github.com/wanderer-industries/wanderer/compare/v1.76.0...v1.76.1) (2025-08-13)

View File

@@ -11,11 +11,13 @@ config :wanderer_app, WandererAppWeb.Endpoint,
config :wanderer_app, WandererApp.Repo,
ssl: false,
stacktrace: true,
show_sensitive_data_on_connection_error: true,
show_sensitive_data_on_connection_error: false,
pool_size: 15,
migration_timestamps: [type: :utc_datetime_usec],
migration_lock: nil,
queue_target: 5000
queue_target: 5000,
queue_interval: 1000,
checkout_timeout: 15000
# Configures Swoosh API Client
config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: WandererApp.Finch

View File

@@ -124,7 +124,7 @@ defmodule WandererApp.Api.Character do
update :update_corporation do
require_atomic? false
accept([:corporation_id, :corporation_name, :corporation_ticker, :alliance_id])
accept([:corporation_id, :corporation_name, :corporation_ticker])
end
update :update_alliance do

View File

@@ -49,11 +49,13 @@ defmodule WandererApp.Character.Activity do
"""
def process_character_activity(map_id, current_user) do
with {:ok, map_user_settings} <- get_map_user_settings(map_id, current_user.id),
raw_activity <- WandererApp.Map.get_character_activity(map_id),
{:ok, raw_activity} <- WandererApp.Map.get_character_activity(map_id),
{:ok, user_characters} <-
WandererApp.Api.Character.active_by_user(%{user_id: current_user.id}) do
result = process_activity_data(raw_activity, map_user_settings, user_characters)
result
process_activity_data(raw_activity, map_user_settings, user_characters)
else
_ ->
[]
end
end

View File

@@ -7,6 +7,7 @@ defmodule WandererApp.Character.Tracker do
defstruct [
:character_id,
:alliance_id,
:corporation_id,
:opts,
server_online: true,
start_time: nil,
@@ -21,6 +22,8 @@ defmodule WandererApp.Character.Tracker do
@type t :: %__MODULE__{
character_id: integer,
alliance_id: integer,
corporation_id: integer,
opts: map,
server_online: boolean,
start_time: DateTime.t(),
@@ -34,10 +37,10 @@ defmodule WandererApp.Character.Tracker do
}
@pause_tracking_timeout :timer.minutes(60 * 10)
@offline_timeout :timer.minutes(5)
@online_error_timeout :timer.minutes(2)
@ship_error_timeout :timer.minutes(2)
@location_error_timeout :timer.minutes(2)
@offline_timeout :timer.minutes(10)
@online_error_timeout :timer.minutes(10)
@ship_error_timeout :timer.minutes(10)
@location_error_timeout :timer.minutes(10)
@online_forbidden_ttl :timer.seconds(7)
@online_limit_ttl :timer.seconds(7)
@forbidden_ttl :timer.seconds(5)
@@ -49,8 +52,15 @@ defmodule WandererApp.Character.Tracker do
def new(args), do: __struct__(args)
def init(args) do
character_id = args[:character_id]
{:ok, %{corporation_id: corporation_id, alliance_id: alliance_id}} =
WandererApp.Character.get_character(character_id)
%{
character_id: args[:character_id],
character_id: character_id,
corporation_id: corporation_id,
alliance_id: alliance_id,
start_time: DateTime.utc_now(),
opts: args
}
@@ -101,6 +111,7 @@ defmodule WandererApp.Character.Tracker do
if duration >= timeout do
pause_tracking(character_id)
WandererApp.Cache.delete("character:#{character_id}:#{type}_error_time")
:ok
else
@@ -113,15 +124,14 @@ defmodule WandererApp.Character.Tracker do
if WandererApp.Character.can_pause_tracking?(character_id) &&
not WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused") do
# Log character tracking statistics before pausing
{:ok, character_state} = WandererApp.Character.get_character_state(character_id)
Logger.debug(fn ->
{:ok, character_state} = WandererApp.Character.get_character_state(character_id)
Logger.warning(
"CHARACTER_TRACKING_PAUSED: Character tracking paused due to sustained errors",
character_id: character_id,
"CHARACTER_TRACKING_PAUSED: Character tracking paused due to sustained errors: #{inspect(character_id: character_id,
active_maps: length(character_state.active_maps),
is_online: character_state.is_online,
tracking_duration_minutes: get_tracking_duration_minutes(character_id)
)
tracking_duration_minutes: get_tracking_duration_minutes(character_id))}"
end)
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
@@ -193,7 +203,7 @@ defmodule WandererApp.Character.Tracker do
access_token: access_token,
character_id: character_id
) do
{:ok, online} ->
{:ok, online} when is_map(online) ->
online = get_online(online)
if online.online == true do
@@ -258,7 +268,7 @@ defmodule WandererApp.Character.Tracker do
character_id: character_id
})
Logger.warning("ESI_ERROR: Character online tracking failed",
Logger.warning("ESI_ERROR: Character online tracking failed #{inspect(error)}",
character_id: character_id,
tracking_pool: tracking_pool,
error_type: error,
@@ -388,12 +398,21 @@ defmodule WandererApp.Character.Tracker do
{:ok, %{eve_id: eve_id, tracking_pool: tracking_pool}} =
WandererApp.Character.get_character(character_id)
case WandererApp.Esi.get_character_info(eve_id) do
{:ok, _info} ->
character_eve_id = eve_id |> String.to_integer()
case WandererApp.Esi.post_characters_affiliation([character_eve_id]) do
{:ok, [character_aff_info]} when not is_nil(character_aff_info) ->
{:ok, character_state} = WandererApp.Character.get_character_state(character_id)
update = maybe_update_corporation(character_state, eve_id |> String.to_integer())
WandererApp.Character.update_character_state(character_id, update)
alliance_id = character_aff_info |> Map.get("alliance_id")
corporation_id = character_aff_info |> Map.get("corporation_id")
updated_state =
character_state
|> maybe_update_corporation(corporation_id)
|> maybe_update_alliance(alliance_id)
WandererApp.Character.update_character_state(character_id, updated_state)
:ok
@@ -975,7 +994,38 @@ defmodule WandererApp.Character.Tracker do
end
end
defp update_alliance(%{character_id: character_id} = state, alliance_id) do
defp maybe_update_alliance(
%{character_id: character_id, alliance_id: old_alliance_id} = state,
alliance_id
)
when old_alliance_id != alliance_id and is_nil(alliance_id) do
{:ok, character} = WandererApp.Character.get_character(character_id)
character_update = %{
alliance_id: nil,
alliance_name: nil,
alliance_ticker: nil
}
{:ok, _character} =
Character.update_alliance(character, character_update)
WandererApp.Character.update_character(character_id, character_update)
@pubsub_client.broadcast(
WandererApp.PubSub,
"character:#{character_id}:alliance",
{:character_alliance, {character_id, character_update}}
)
state
end
defp maybe_update_alliance(
%{character_id: character_id, alliance_id: old_alliance_id} = state,
alliance_id
)
when old_alliance_id != alliance_id do
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|> case do
@@ -1015,7 +1065,13 @@ defmodule WandererApp.Character.Tracker do
end
end
defp update_corporation(%{character_id: character_id} = state, corporation_id) do
defp maybe_update_alliance(state, _alliance_id), do: state
defp maybe_update_corporation(
%{character_id: character_id, corporation_id: old_corporation_id} = state,
corporation_id
)
when old_corporation_id != corporation_id do
(WandererApp.Cache.has_key?("character:#{character_id}:online_forbidden") ||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|> case do
@@ -1027,16 +1083,13 @@ defmodule WandererApp.Character.Tracker do
|> WandererApp.Esi.get_corporation_info()
|> case do
{:ok, %{"name" => corporation_name, "ticker" => corporation_ticker} = corporation_info} ->
alliance_id = Map.get(corporation_info, "alliance_id")
{:ok, character} =
WandererApp.Character.get_character(character_id)
character_update = %{
corporation_id: corporation_id,
corporation_name: corporation_name,
corporation_ticker: corporation_ticker,
alliance_id: alliance_id
corporation_ticker: corporation_ticker
}
{:ok, _character} =
@@ -1057,8 +1110,7 @@ defmodule WandererApp.Character.Tracker do
)
state
|> Map.merge(%{alliance_id: alliance_id, corporation_id: corporation_id})
|> maybe_update_alliance()
|> Map.merge(%{corporation_id: corporation_id})
error ->
Logger.warning(
@@ -1072,6 +1124,8 @@ defmodule WandererApp.Character.Tracker do
end
end
defp maybe_update_corporation(state, _corporation_id), do: state
defp maybe_update_ship(
%{
character_id: character_id
@@ -1153,58 +1207,6 @@ defmodule WandererApp.Character.Tracker do
structure_id != new_structure_id ||
station_id != new_station_id
defp maybe_update_corporation(
state,
character_eve_id
)
when not is_nil(character_eve_id) and is_integer(character_eve_id) do
case WandererApp.Esi.post_characters_affiliation([character_eve_id]) do
{:ok, [character_aff_info]} when not is_nil(character_aff_info) ->
update_corporation(state, character_aff_info |> Map.get("corporation_id"))
_error ->
state
end
end
defp maybe_update_corporation(
state,
_info
),
do: state
defp maybe_update_alliance(
%{character_id: character_id, alliance_id: alliance_id} =
state
) do
case alliance_id do
nil ->
{:ok, character} = WandererApp.Character.get_character(character_id)
character_update = %{
alliance_id: nil,
alliance_name: nil,
alliance_ticker: nil
}
{:ok, _character} =
Character.update_alliance(character, character_update)
WandererApp.Character.update_character(character_id, character_update)
@pubsub_client.broadcast(
WandererApp.PubSub,
"character:#{character_id}:alliance",
{:character_alliance, {character_id, character_update}}
)
state
_ ->
update_alliance(state, alliance_id)
end
end
defp maybe_update_wallet(
%{character_id: character_id} =
state,

View File

@@ -12,6 +12,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do
opts: map
}
@check_start_queue_interval :timer.seconds(1)
@garbage_collection_interval :timer.minutes(15)
@untrack_characters_interval :timer.minutes(1)
@inactive_character_timeout :timer.minutes(10)
@@ -23,6 +24,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do
def new(args), do: __struct__(args)
def init(args) do
Process.send_after(self(), :check_start_queue, @check_start_queue_interval)
Process.send_after(self(), :garbage_collect, @garbage_collection_interval)
Process.send_after(self(), :untrack_characters, @untrack_characters_interval)
@@ -46,25 +48,19 @@ defmodule WandererApp.Character.TrackerManager.Impl do
end
def start_tracking(state, character_id, opts) do
with {:ok, characters} <- WandererApp.Cache.lookup("tracked_characters", []),
false <- Enum.member?(characters, character_id) do
Logger.debug(fn -> "Start character tracker: #{inspect(character_id)}" end)
if not WandererApp.Cache.has_key?("#{character_id}:track_requested") do
WandererApp.Cache.insert(
"#{character_id}:track_requested",
true
)
tracked_characters = [character_id | characters] |> Enum.uniq()
WandererApp.Cache.insert("tracked_characters", tracked_characters)
WandererApp.Character.update_character(character_id, %{online: false})
WandererApp.Character.update_character_state(character_id, %{
is_online: false
})
WandererApp.Character.TrackerPoolDynamicSupervisor.start_tracking(character_id)
WandererApp.TaskWrapper.start_link(WandererApp.Character, :update_character_state, [
character_id,
%{opts: opts}
])
WandererApp.Cache.insert_or_update(
"track_characters_queue",
[character_id],
fn existing ->
[character_id | existing] |> Enum.uniq()
end
)
end
state
@@ -178,6 +174,21 @@ defmodule WandererApp.Character.TrackerManager.Impl do
end
end
def handle_info(
:check_start_queue,
state
) do
Process.send_after(self(), :check_start_queue, @check_start_queue_interval)
{:ok, track_characters_queue} = WandererApp.Cache.lookup("track_characters_queue", [])
track_characters_queue
|> Enum.each(fn character_id ->
track_character(character_id, %{})
end)
state
end
def handle_info(
:garbage_collect,
state
@@ -294,8 +305,56 @@ defmodule WandererApp.Character.TrackerManager.Impl do
state
end
def handle_info(_event, state),
do: state
def track_character(character_id, opts) do
with {:ok, characters} <- WandererApp.Cache.lookup("tracked_characters", []),
false <- Enum.member?(characters, character_id) do
Logger.debug(fn -> "Start character tracker: #{inspect(character_id)}" end)
WandererApp.Cache.insert_or_update(
"tracked_characters",
[character_id],
fn existing ->
[character_id | existing] |> Enum.uniq()
end
)
WandererApp.Cache.insert_or_update(
"track_characters_queue",
[],
fn existing ->
existing
|> Enum.reject(fn c_id -> c_id == character_id end)
end
)
WandererApp.Cache.delete("#{character_id}:track_requested")
WandererApp.Character.update_character(character_id, %{online: false})
WandererApp.Character.update_character_state(character_id, %{
is_online: false
})
WandererApp.Character.TrackerPoolDynamicSupervisor.start_tracking(character_id)
WandererApp.TaskWrapper.start_link(WandererApp.Character, :update_character_state, [
character_id,
%{opts: opts}
])
else
_ ->
WandererApp.Cache.insert_or_update(
"track_characters_queue",
[],
fn existing ->
existing
|> Enum.reject(fn c_id -> c_id == character_id end)
end
)
WandererApp.Cache.delete("#{character_id}:track_requested")
end
end
def character_is_present(map_id, character_id) do
{:ok, presence_character_ids} =

View File

@@ -23,7 +23,7 @@ defmodule WandererApp.Character.TrackerPool do
@check_ship_errors_interval :timer.minutes(1)
@check_location_errors_interval :timer.minutes(1)
@update_ship_interval :timer.seconds(2)
@update_info_interval :timer.minutes(1)
@update_info_interval :timer.minutes(2)
@update_wallet_interval :timer.minutes(1)
@logger Application.compile_env(:wanderer_app, :logger)
@@ -124,7 +124,7 @@ defmodule WandererApp.Character.TrackerPool do
Process.send_after(self(), :check_online_errors, :timer.seconds(60))
Process.send_after(self(), :check_ship_errors, :timer.seconds(90))
Process.send_after(self(), :check_location_errors, :timer.seconds(120))
Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
# Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
Process.send_after(self(), :update_location, 300)
Process.send_after(self(), :update_ship, 500)
Process.send_after(self(), :update_info, 1500)

View File

@@ -287,8 +287,8 @@ defmodule WandererApp.Esi.ApiClient do
opts: [ttl: @ttl]
)
def get_alliance_info(eve_id, opts \\ []) do
case _get_alliance_info(eve_id, "", opts) do
{:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)}
case get_alliance_info(eve_id, "", opts) do
{:ok, result} when is_map(result) -> {:ok, result |> Map.put("eve_id", eve_id)}
{:error, error} -> {:error, error}
error -> error
end
@@ -309,8 +309,8 @@ defmodule WandererApp.Esi.ApiClient do
opts: [ttl: @ttl]
)
def get_corporation_info(eve_id, opts \\ []) do
case _get_corporation_info(eve_id, "", opts) do
{:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)}
case get_corporation_info(eve_id, "", opts) do
{:ok, result} when is_map(result) -> {:ok, result |> Map.put("eve_id", eve_id)}
{:error, error} -> {:error, error}
error -> error
end
@@ -327,7 +327,7 @@ defmodule WandererApp.Esi.ApiClient do
opts,
@cache_opts
) do
{:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)}
{:ok, result} when is_map(result) -> {:ok, result |> Map.put("eve_id", eve_id)}
{:error, error} -> {:error, error}
error -> error
end
@@ -434,7 +434,7 @@ defmodule WandererApp.Esi.ApiClient do
defp get_auth_opts(opts), do: [auth: {:bearer, opts[:access_token]}]
defp _get_alliance_info(alliance_eve_id, info_path, opts),
defp get_alliance_info(alliance_eve_id, info_path, opts),
do:
get(
"/alliances/#{alliance_eve_id}/#{info_path}",
@@ -442,7 +442,7 @@ defmodule WandererApp.Esi.ApiClient do
@cache_opts
)
defp _get_corporation_info(corporation_eve_id, info_path, opts),
defp get_corporation_info(corporation_eve_id, info_path, opts),
do:
get(
"/corporations/#{corporation_eve_id}/#{info_path}",
@@ -830,7 +830,8 @@ defmodule WandererApp.Esi.ApiClient do
expires_at,
scopes
) do
time_since_expiry = DateTime.diff(DateTime.utc_now(), expires_at, :second)
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,
@@ -857,7 +858,8 @@ defmodule WandererApp.Esi.ApiClient do
expires_at,
scopes
) do
time_since_expiry = DateTime.diff(DateTime.utc_now(), expires_at, :second)
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: Connection refused during token refresh",
character_id: character_id,

View File

@@ -51,7 +51,7 @@ defmodule WandererApp.ExternalEvents.Event do
def new(map_id, event_type, payload) when is_binary(map_id) and is_map(payload) do
if valid_event_type?(event_type) do
%__MODULE__{
id: Ulid.generate(System.system_time(:millisecond)),
id: Ecto.ULID.generate(System.system_time(:millisecond)),
map_id: map_id,
type: event_type,
payload: payload,

View File

@@ -448,7 +448,7 @@ defmodule WandererApp.ExternalEvents.JsonApiFormatter do
"connected" ->
%{
"type" => "connection_status",
"id" => event["id"] || Ulid.generate(),
"id" => event["id"] || Ecto.ULID.generate(),
"attributes" => %{
"status" => "connected",
"server_time" => payload["server_time"],
@@ -465,7 +465,7 @@ defmodule WandererApp.ExternalEvents.JsonApiFormatter do
# Use existing payload structure but wrap it in JSON:API format
%{
"type" => "events",
"id" => event["id"] || Ulid.generate(),
"id" => event["id"] || Ecto.ULID.generate(),
"attributes" => payload,
"relationships" => %{
"map" => %{

View File

@@ -248,6 +248,6 @@ defmodule WandererApp.ExternalEvents.MapEventRelay do
defp datetime_to_ulid(datetime) do
timestamp = DateTime.to_unix(datetime, :millisecond)
# Create a ULID with the timestamp (rest will be zeros for comparison)
Ulid.generate(timestamp)
Ecto.ULID.generate(timestamp)
end
end

View File

@@ -8,6 +8,19 @@ defmodule WandererApp.Map.Manager do
require Logger
alias WandererApp.Map.Server
alias WandererApp.Map.ServerSupervisor
alias WandererApp.Api.MapSystemSignature
@maps_start_per_second 10
@maps_start_interval 1000
@maps_queue :maps_queue
@garbage_collection_interval :timer.hours(1)
@check_maps_queue_interval :timer.seconds(1)
@signatures_cleanup_interval :timer.minutes(30)
@delete_after_minutes 30
@pings_cleanup_interval :timer.minutes(10)
@pings_expire_minutes 60
# Test-aware async task runner
defp safe_async_task(fun) do
@@ -25,20 +38,6 @@ defmodule WandererApp.Map.Manager do
end
end
alias WandererApp.Map.ServerSupervisor
alias WandererApp.Api.MapSystemSignature
@maps_start_per_second 5
@maps_start_interval 1000
@maps_queue :maps_queue
@garbage_collection_interval :timer.hours(1)
@check_maps_queue_interval :timer.seconds(1)
@signatures_cleanup_interval :timer.minutes(30)
@delete_after_minutes 30
@pings_cleanup_interval :timer.minutes(10)
@pings_expire_minutes 60
def start_map(map_id) when is_binary(map_id),
do: WandererApp.Queue.push_uniq(@maps_queue, map_id)
@@ -247,22 +246,29 @@ defmodule WandererApp.Map.Manager do
Logger.debug(fn -> "All maps started" end)
else
# In production, run async as normal
tasks =
for chunk <- chunks do
task =
Task.async(fn ->
chunk
|> Enum.map(&start_map_server/1)
end)
chunks
|> Task.async_stream(
fn chunk ->
chunk
|> Enum.map(&start_map_server/1)
:timer.sleep(@maps_start_interval)
end,
max_concurrency: System.schedulers_online(),
on_timeout: :kill_task,
timeout: :timer.seconds(60)
)
|> Enum.each(fn result ->
case result do
{:ok, _} ->
:ok
task
_ ->
:ok
end
end)
Logger.debug(fn -> "Waiting for maps to start" end)
Task.await_many(tasks)
Logger.debug(fn -> "All maps started" end)
Logger.info(fn -> "All maps started" end)
end
end

View File

@@ -373,6 +373,8 @@ defmodule WandererApp.Map.Server.CharactersImpl do
{:ok, character} =
WandererApp.Character.get_map_character(map_id, character_id, not_present: true)
WandererApp.Cache.delete("character:#{character.id}:tracking_paused")
add_character(%{map_id: map_id}, character, true)
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{

View File

@@ -39,7 +39,7 @@ defmodule WandererApp.SecurityAudit do
}
# Store in database
store_audit_entry(audit_entry)
# store_audit_entry(audit_entry)
# Send to telemetry for monitoring
emit_telemetry_event(audit_entry)
@@ -489,11 +489,11 @@ defmodule WandererApp.SecurityAudit do
defp store_audit_entry(audit_entry) do
# Handle async processing if enabled
if async_enabled?() do
WandererApp.SecurityAudit.AsyncProcessor.log_event(audit_entry)
else
do_store_audit_entry(audit_entry)
end
# if async_enabled?() do
# WandererApp.SecurityAudit.AsyncProcessor.log_event(audit_entry)
# else
# do_store_audit_entry(audit_entry)
# end
end
@doc false

View File

@@ -195,7 +195,7 @@ defmodule WandererApp.Ueberauth.Strategy.Eve do
tracking_pool = WandererApp.Character.TrackingConfigUtils.get_active_pool!()
base_options = [
redirect_uri: callback_url(conn),
redirect_uri: "#{WandererApp.Env.base_url()}/auth/eve/callback",
with_wallet: with_wallet,
is_admin?: is_admin?,
tracking_pool: tracking_pool

View File

@@ -52,11 +52,7 @@ defmodule WandererAppWeb.Api.EventsController do
defp establish_sse_connection(conn, map_id, api_key, params) do
# Parse event filter if provided
event_filter =
case Map.get(params, "events") do
nil -> :all
events -> EventFilter.parse(events)
end
event_filter = EventFilter.parse(Map.get(params, "events"))
# Parse format parameter
event_format = Map.get(params, "format", "legacy")
@@ -82,7 +78,7 @@ defmodule WandererAppWeb.Api.EventsController do
send_event(
conn,
%{
id: Ulid.generate(),
id: Ecto.ULID.generate(),
event: "connected",
data: %{
map_id: map_id,

View File

@@ -15,30 +15,6 @@ defmodule WandererAppWeb.Endpoint do
max_age: 24 * 60 * 60 * 180
]
# @impl SiteEncrypt
# def certification do
# SiteEncrypt.configure(
# client: :native,
# mode: :auto,
# days_to_renew: 30,
# domains: ["dev.wanderer.deadly-w.space"],
# emails: ["dmitriypopovsamara@gmail.com"],
# db_folder: System.get_env("SITE_ENCRYPT_DB", Path.join("tmp", "site_encrypt_db")),
# backup: Path.join(Path.join("tmp", "site_encrypt_db"), "site_encrypt_backup.tgz"),
# directory_url:
# case System.get_env("CERT_MODE", "local") do
# "local" ->
# {:internal, port: 4001}
# "staging" ->
# "https://acme-staging-v02.api.letsencrypt.org/directory"
# "production" ->
# "https://acme-v02.api.letsencrypt.org/directory"
# end
# )
# end
socket "/live", Phoenix.LiveView.Socket,
websocket: [compress: true, connect_info: [session: @session_options]]

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer"
@version "1.76.1"
@version "1.76.13"
def project do
[
@@ -105,7 +105,7 @@ defmodule WandererApp.MixProject do
{:ash_postgres, "~> 2.4"},
{:exsync, "~> 0.4", only: :dev},
{:nimble_csv, "~> 1.2.0"},
{:ulid, "~> 0.2.0"},
{:ecto_ulid_next, "~> 1.0.2"},
{:cachex, "~> 3.6"},
{:live_select, "~> 1.5"},
{:nebulex, "~> 2.6"},

View File

@@ -35,6 +35,7 @@
"earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
"ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"},
"ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"},
"ecto_ulid_next": {:hex, :ecto_ulid_next, "1.0.2", "8372f3c589c8fa50ea7b127dabe008528837b11781f65bfc72d96259d49b44c5", [:mix], [{:ecto, "~> 3.2", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "61c9c2c531f87ce7e2e9e57fc60d533fe97b3a62a43c21b632b0824f0773bcbe"},
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"error_tracker": {:hex, :error_tracker, "0.2.2", "7635f5ed6016df10d8e63348375acb2ca411e2f6f9703ee90cc2d4262af5faec", [:mix], [{:ecto, "~> 3.11", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, ">= 0.0.0", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, "~> 4.6", [hex: :phoenix_ecto, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "b975978f64d27373d3486d7de477a699e735f8c0b1c74a7370ecb80e7ae97903"},
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
@@ -43,6 +44,7 @@
"ex_check": {:hex, :ex_check, "0.14.0", "d6fbe0bcc51cf38fea276f5bc2af0c9ae0a2bb059f602f8de88709421dae4f0e", [:mix], [], "hexpm", "8a602e98c66e6a4be3a639321f1f545292042f290f91fa942a285888c6868af0"},
"ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"},
"ex_rated": {:hex, :ex_rated, "2.1.0", "d40e6fe35097b10222df2db7bb5dd801d57211bac65f29063de5f201c2a6aebc", [:mix], [{:ex2ms, "~> 1.5", [hex: :ex2ms, repo: "hexpm", optional: false]}], "hexpm", "936c155337253ed6474f06d941999dd3a9cf0fe767ec99a59f2d2989dc2cc13f"},
"ex_ulid": {:hex, :ex_ulid, "0.1.0", "e6e717c57344f6e500d0190ccb4edc862b985a3680f15834af992ec065d4dcff", [:mix], [], "hexpm", "a2befd477aebc4639563de7e233e175cacf8a8f42c8f6778c88d60c13bf20860"},
"excoveralls": {:hex, :excoveralls, "0.18.5", "e229d0a65982613332ec30f07940038fe451a2e5b29bce2a5022165f0c9b157e", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "523fe8a15603f86d64852aab2abe8ddbd78e68579c8525ae765facc5eae01562"},
"expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"},
"exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"},