mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-10-30 22:17:19 +00:00
Compare commits
79 Commits
tracking-c
...
v1.70.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c064baf8a | ||
|
|
9e11b10d74 | ||
|
|
2fc45e00b4 | ||
|
|
45eb08fc3a | ||
|
|
fbcfae0200 | ||
|
|
9c4ce013ec | ||
|
|
5dba7c12f0 | ||
|
|
db793c80c8 | ||
|
|
aa4a3f1aa9 | ||
|
|
3424639309 | ||
|
|
0f309a29ba | ||
|
|
e13b8846b8 | ||
|
|
d5ea4d6129 | ||
|
|
9d50bfedbd | ||
|
|
b03410083c | ||
|
|
a314b1e448 | ||
|
|
e8a51a85c4 | ||
|
|
d4074f966c | ||
|
|
1413b41824 | ||
|
|
379c1edec3 | ||
|
|
58b5bade9e | ||
|
|
71aee4cd3e | ||
|
|
10bab0cfa1 | ||
|
|
698350b0f7 | ||
|
|
a97cf25031 | ||
|
|
8302d088bd | ||
|
|
64788e73de | ||
|
|
114fd471e8 | ||
|
|
b24a3120d3 | ||
|
|
c5f93b3d0a | ||
|
|
79290e4721 | ||
|
|
984e126f23 | ||
|
|
cd1ad31aed | ||
|
|
1e3f6cf9e7 | ||
|
|
9c6ccd9a8a | ||
|
|
681ba21d39 | ||
|
|
aef62189ee | ||
|
|
09f70ac817 | ||
|
|
1eacb22143 | ||
|
|
8524bad377 | ||
|
|
9d899243d1 | ||
|
|
9acf20a639 | ||
|
|
71ef6b2e82 | ||
|
|
5e34d95dd2 | ||
|
|
25a809c064 | ||
|
|
f760498150 | ||
|
|
328301a375 | ||
|
|
f28e7ebbbb | ||
|
|
bfa84af71e | ||
|
|
42cd1ba976 | ||
|
|
88cba866fd | ||
|
|
af2876a84b | ||
|
|
c5b15bfa78 | ||
|
|
85f00a63c2 | ||
|
|
05f427bcd7 | ||
|
|
69f4c41534 | ||
|
|
30b9239a8b | ||
|
|
2061a83c59 | ||
|
|
24e723de07 | ||
|
|
5a927e5ba5 | ||
|
|
10fafcf59f | ||
|
|
be87591801 | ||
|
|
086d4378d3 | ||
|
|
e982275905 | ||
|
|
77c02703e9 | ||
|
|
0ef27d4f95 | ||
|
|
5edc27744e | ||
|
|
02ff887fee | ||
|
|
3a30eeb59f | ||
|
|
79af8fb601 | ||
|
|
f9f00faa0e | ||
|
|
a3c41e84e4 | ||
|
|
7f21f33351 | ||
|
|
568f682cee | ||
|
|
901c4c8ca4 | ||
|
|
3dbba97f9c | ||
|
|
3475620267 | ||
|
|
8936a5e5d8 | ||
|
|
719e34f9bc |
199
CHANGELOG.md
199
CHANGELOG.md
@@ -2,6 +2,205 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.70.1](https://github.com/wanderer-industries/wanderer/compare/v1.70.0...v1.70.1) (2025-06-14)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* resolve api issue with custom name
|
||||
|
||||
## [v1.70.0](https://github.com/wanderer-industries/wanderer/compare/v1.69.1...v1.70.0) (2025-06-11)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Fix admin page error
|
||||
|
||||
## [v1.69.1](https://github.com/wanderer-industries/wanderer/compare/v1.69.0...v1.69.1) (2025-06-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.69.0](https://github.com/wanderer-industries/wanderer/compare/v1.68.6...v1.69.0) (2025-06-11)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Added multiple tracking pools support
|
||||
|
||||
## [v1.68.6](https://github.com/wanderer-industries/wanderer/compare/v1.68.5...v1.68.6) (2025-06-10)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.68.5](https://github.com/wanderer-industries/wanderer/compare/v1.68.4...v1.68.5) (2025-06-10)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed updating map options
|
||||
|
||||
## [v1.68.4](https://github.com/wanderer-industries/wanderer/compare/v1.68.3...v1.68.4) (2025-06-10)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.68.3](https://github.com/wanderer-industries/wanderer/compare/v1.68.2...v1.68.3) (2025-06-09)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.68.2](https://github.com/wanderer-industries/wanderer/compare/v1.68.1...v1.68.2) (2025-06-09)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed character auth with wallet (on characters page)
|
||||
|
||||
## [v1.68.1](https://github.com/wanderer-industries/wanderer/compare/v1.68.0...v1.68.1) (2025-06-09)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed auth from welcome page if invites disabled
|
||||
|
||||
## [v1.68.0](https://github.com/wanderer-industries/wanderer/compare/v1.67.5...v1.68.0) (2025-06-09)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Added invites store support
|
||||
|
||||
## [v1.67.5](https://github.com/wanderer-industries/wanderer/compare/v1.67.4...v1.67.5) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Added back ARM docker image build
|
||||
|
||||
## [v1.67.4](https://github.com/wanderer-industries/wanderer/compare/v1.67.3...v1.67.4) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed issue with system splash updates
|
||||
|
||||
## [v1.67.3](https://github.com/wanderer-industries/wanderer/compare/v1.67.2...v1.67.3) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed issue with system splash updates
|
||||
|
||||
## [v1.67.2](https://github.com/wanderer-industries/wanderer/compare/v1.67.1...v1.67.2) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.67.1](https://github.com/wanderer-industries/wanderer/compare/v1.67.0...v1.67.1) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.67.0](https://github.com/wanderer-industries/wanderer/compare/v1.66.25...v1.67.0) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
### Features:
|
||||
|
||||
* Core: Added support for WANDERER_CHARACTER_TRACKING_PAUSE_DISABLED env variable to pause inactive character trackers
|
||||
|
||||
## [v1.66.25](https://github.com/wanderer-industries/wanderer/compare/v1.66.24...v1.66.25) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Disabled kills fetching based on env settings
|
||||
|
||||
## [v1.66.24](https://github.com/wanderer-industries/wanderer/compare/v1.66.23...v1.66.24) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.23](https://github.com/wanderer-industries/wanderer/compare/v1.66.22...v1.66.23) (2025-06-08)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.22](https://github.com/wanderer-industries/wanderer/compare/v1.66.21...v1.66.22) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.21](https://github.com/wanderer-industries/wanderer/compare/v1.66.20...v1.66.21) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Fixed kills fetching based on env settings
|
||||
|
||||
## [v1.66.20](https://github.com/wanderer-industries/wanderer/compare/v1.66.19...v1.66.20) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.19](https://github.com/wanderer-industries/wanderer/compare/v1.66.18...v1.66.19) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Added check for offline characters timeouts
|
||||
|
||||
## [v1.66.18](https://github.com/wanderer-industries/wanderer/compare/v1.66.17...v1.66.18) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.66.17](https://github.com/wanderer-industries/wanderer/compare/v1.66.16...v1.66.17) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Increased tracking pause timeout for offline characters up to 10 hours
|
||||
|
||||
## [v1.66.16](https://github.com/wanderer-industries/wanderer/compare/v1.66.15...v1.66.16) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Core: Increased tracking pause timeout for offline characters up to 10 hours
|
||||
|
||||
## [v1.66.15](https://github.com/wanderer-industries/wanderer/compare/v1.66.14...v1.66.15) (2025-06-07)
|
||||
|
||||
|
||||
|
||||
@@ -57,8 +57,8 @@ export const Characters = ({ data }: CharactersProps) => {
|
||||
<>
|
||||
<span
|
||||
className={clsx(
|
||||
'absolute top-[2px] left-[2px] w-[9px] h-[9px]',
|
||||
'text-yellow-500 text-[9px] rounded-[1px] z-10 hover:hidden',
|
||||
'absolute flex flex-col p-[2px] top-[0px] left-[0px] w-[35px] h-[35px]',
|
||||
'text-yellow-500 text-[9px] z-10 bg-gray-800/40',
|
||||
'pi',
|
||||
PrimeIcons.PAUSE,
|
||||
)}
|
||||
|
||||
@@ -117,7 +117,12 @@ config :wanderer_app,
|
||||
admins: admins,
|
||||
corp_id: System.get_env("WANDERER_CORP_ID", "-1") |> String.to_integer(),
|
||||
corp_wallet: System.get_env("WANDERER_CORP_WALLET", ""),
|
||||
corp_wallet_eve_id: System.get_env("WANDERER_CORP_WALLET_EVE_ID", "-1"),
|
||||
public_api_disabled: public_api_disabled,
|
||||
active_tracking_pool: System.get_env("WANDERER_ACTIVE_TRACKING_POOL", "default"),
|
||||
character_tracking_pause_disabled:
|
||||
System.get_env("WANDERER_CHARACTER_TRACKING_PAUSE_DISABLED", "true")
|
||||
|> String.to_existing_atom(),
|
||||
character_api_disabled:
|
||||
System.get_env("WANDERER_CHARACTER_API_DISABLED", "true") |> String.to_existing_atom(),
|
||||
zkill_preload_disabled:
|
||||
@@ -170,11 +175,31 @@ config :ueberauth, WandererApp.Ueberauth.Strategy.Eve.OAuth,
|
||||
client_id: {WandererApp.Ueberauth, :client_id},
|
||||
client_secret: {WandererApp.Ueberauth, :client_secret},
|
||||
client_id_default: System.get_env("EVE_CLIENT_ID", "<EVE_CLIENT_ID>"),
|
||||
client_id_1: System.get_env("EVE_CLIENT_ID_1", ""),
|
||||
client_id_2: System.get_env("EVE_CLIENT_ID_2", ""),
|
||||
client_id_3: System.get_env("EVE_CLIENT_ID_3", ""),
|
||||
client_id_4: System.get_env("EVE_CLIENT_ID_4", ""),
|
||||
client_id_5: System.get_env("EVE_CLIENT_ID_5", ""),
|
||||
client_id_6: System.get_env("EVE_CLIENT_ID_6", ""),
|
||||
client_id_7: System.get_env("EVE_CLIENT_ID_7", ""),
|
||||
client_id_8: System.get_env("EVE_CLIENT_ID_8", ""),
|
||||
client_id_9: System.get_env("EVE_CLIENT_ID_9", ""),
|
||||
client_id_10: System.get_env("EVE_CLIENT_ID_10", ""),
|
||||
client_id_with_wallet:
|
||||
System.get_env("EVE_CLIENT_WITH_WALLET_ID", "<EVE_CLIENT_WITH_WALLET_ID>"),
|
||||
client_id_with_corp_wallet:
|
||||
System.get_env("EVE_CLIENT_WITH_CORP_WALLET_ID", "<EVE_CLIENT_WITH_CORP_WALLET_ID>"),
|
||||
client_secret_default: System.get_env("EVE_CLIENT_SECRET", "<EVE_CLIENT_SECRET>"),
|
||||
client_secret_1: System.get_env("EVE_CLIENT_SECRET_1", ""),
|
||||
client_secret_2: System.get_env("EVE_CLIENT_SECRET_2", ""),
|
||||
client_secret_3: System.get_env("EVE_CLIENT_SECRET_3", ""),
|
||||
client_secret_4: System.get_env("EVE_CLIENT_SECRET_4", ""),
|
||||
client_secret_5: System.get_env("EVE_CLIENT_SECRET_5", ""),
|
||||
client_secret_6: System.get_env("EVE_CLIENT_SECRET_6", ""),
|
||||
client_secret_7: System.get_env("EVE_CLIENT_SECRET_7", ""),
|
||||
client_secret_8: System.get_env("EVE_CLIENT_SECRET_8", ""),
|
||||
client_secret_9: System.get_env("EVE_CLIENT_SECRET_9", ""),
|
||||
client_secret_10: System.get_env("EVE_CLIENT_SECRET_10", ""),
|
||||
client_secret_with_wallet:
|
||||
System.get_env("EVE_CLIENT_WITH_WALLET_SECRET", "<EVE_CLIENT_WITH_WALLET_SECRET>"),
|
||||
client_secret_with_corp_wallet:
|
||||
|
||||
@@ -29,5 +29,6 @@ defmodule WandererApp.Api do
|
||||
resource WandererApp.Api.CorpWalletTransaction
|
||||
resource WandererApp.Api.License
|
||||
resource WandererApp.Api.MapPing
|
||||
resource WandererApp.Api.MapInvite
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule WandererApp.Api.Character do
|
||||
define(:update_alliance, action: :update_alliance)
|
||||
define(:update_wallet_balance, action: :update_wallet_balance)
|
||||
define(:mark_as_deleted, action: :mark_as_deleted)
|
||||
define(:last_active, action: :last_active)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
@@ -47,7 +48,8 @@ defmodule WandererApp.Api.Character do
|
||||
:access_token,
|
||||
:refresh_token,
|
||||
:expires_at,
|
||||
:scopes
|
||||
:scopes,
|
||||
:tracking_pool
|
||||
]
|
||||
|
||||
defaults [:create, :read, :destroy]
|
||||
@@ -72,6 +74,12 @@ defmodule WandererApp.Api.Character do
|
||||
filter(expr(user_id == ^arg(:user_id) and deleted == false))
|
||||
end
|
||||
|
||||
read :last_active do
|
||||
argument(:from, :utc_datetime, allow_nil?: false)
|
||||
|
||||
filter(expr(updated_at > ^arg(:from)))
|
||||
end
|
||||
|
||||
update :assign do
|
||||
accept []
|
||||
require_atomic? false
|
||||
@@ -85,7 +93,7 @@ defmodule WandererApp.Api.Character do
|
||||
|
||||
update :update do
|
||||
require_atomic? false
|
||||
accept([:name, :access_token, :refresh_token, :expires_at, :scopes])
|
||||
accept([:name, :access_token, :refresh_token, :expires_at, :scopes, :tracking_pool])
|
||||
|
||||
change(set_attribute(:deleted, false))
|
||||
end
|
||||
@@ -198,6 +206,7 @@ defmodule WandererApp.Api.Character do
|
||||
attribute :alliance_name, :string
|
||||
attribute :alliance_ticker, :string
|
||||
attribute :eve_wallet_balance, :float
|
||||
attribute :tracking_pool, :string
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
|
||||
96
lib/wanderer_app/api/map_invite.ex
Normal file
96
lib/wanderer_app/api/map_invite.ex
Normal file
@@ -0,0 +1,96 @@
|
||||
defmodule WandererApp.Api.MapInvite do
|
||||
@moduledoc false
|
||||
|
||||
use Ash.Resource,
|
||||
domain: WandererApp.Api,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
postgres do
|
||||
repo(WandererApp.Repo)
|
||||
table("map_invites_v1")
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define(:new, action: :new)
|
||||
define(:read, action: :read)
|
||||
define(:destroy, action: :destroy)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
action: :read
|
||||
)
|
||||
|
||||
define(:by_map,
|
||||
action: :by_map
|
||||
)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:token
|
||||
]
|
||||
|
||||
defaults [:read, :update, :destroy]
|
||||
|
||||
create :new do
|
||||
accept [
|
||||
:map_id,
|
||||
:token,
|
||||
:type,
|
||||
:valid_until
|
||||
]
|
||||
|
||||
primary?(true)
|
||||
|
||||
argument :map_id, :uuid, allow_nil?: true
|
||||
|
||||
change manage_relationship(:map_id, :map, on_lookup: :relate, on_no_match: nil)
|
||||
end
|
||||
|
||||
read :by_map do
|
||||
argument(:map_id, :string, allow_nil?: false)
|
||||
|
||||
filter(expr(map_id == ^arg(:map_id)))
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
attribute :token, :string do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
attribute :type, :atom do
|
||||
default "user"
|
||||
|
||||
constraints(
|
||||
one_of: [
|
||||
:user,
|
||||
:admin
|
||||
]
|
||||
)
|
||||
|
||||
allow_nil?(false)
|
||||
end
|
||||
|
||||
attribute :valid_until, :utc_datetime do
|
||||
allow_nil? true
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :map, WandererApp.Api.Map do
|
||||
attribute_writable? true
|
||||
end
|
||||
end
|
||||
|
||||
postgres do
|
||||
references do
|
||||
reference :map, on_delete: :delete
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -27,6 +27,7 @@ defmodule WandererApp.Application do
|
||||
}
|
||||
},
|
||||
WandererApp.Cache,
|
||||
Supervisor.child_spec({Cachex, name: :esi_auth_cache}, id: :esi_auth_cache_worker),
|
||||
Supervisor.child_spec({Cachex, name: :api_cache}, id: :api_cache_worker),
|
||||
Supervisor.child_spec({Cachex, name: :system_static_info_cache},
|
||||
id: :system_static_info_cache_worker
|
||||
@@ -40,6 +41,7 @@ defmodule WandererApp.Application do
|
||||
Supervisor.child_spec({Cachex, name: :tracked_characters},
|
||||
id: :tracked_characters_cache_worker
|
||||
),
|
||||
WandererApp.Esi.InitClientsTask,
|
||||
WandererApp.Scheduler,
|
||||
{Registry, keys: :unique, name: WandererApp.MapRegistry},
|
||||
{Registry, keys: :unique, name: WandererApp.Character.TrackerRegistry},
|
||||
@@ -47,17 +49,16 @@ defmodule WandererApp.Application do
|
||||
child_spec: DynamicSupervisor, name: WandererApp.Map.DynamicSupervisors},
|
||||
{PartitionSupervisor,
|
||||
child_spec: DynamicSupervisor, name: WandererApp.Character.DynamicSupervisors},
|
||||
WandererApp.Zkb.Supervisor,
|
||||
WandererApp.Server.ServerStatusTracker,
|
||||
WandererApp.Server.TheraDataFetcher,
|
||||
{WandererApp.Character.TrackerPoolSupervisor, []},
|
||||
WandererApp.Character.TrackerManager,
|
||||
WandererApp.Map.Manager,
|
||||
WandererApp.Map.ZkbDataFetcher,
|
||||
WandererAppWeb.Presence,
|
||||
WandererAppWeb.Endpoint
|
||||
] ++
|
||||
maybe_start_corp_wallet_tracker(WandererApp.Env.map_subscriptions_enabled?())
|
||||
maybe_start_corp_wallet_tracker(WandererApp.Env.map_subscriptions_enabled?()) ++
|
||||
maybe_start_zkb(WandererApp.Env.zkill_preload_disabled?())
|
||||
|
||||
opts = [strategy: :one_for_one, name: WandererApp.Supervisor]
|
||||
|
||||
@@ -78,6 +79,12 @@ defmodule WandererApp.Application do
|
||||
:ok
|
||||
end
|
||||
|
||||
defp maybe_start_zkb(false),
|
||||
do: [WandererApp.Zkb.Supervisor, WandererApp.Map.ZkbDataFetcher]
|
||||
|
||||
defp maybe_start_zkb(_),
|
||||
do: []
|
||||
|
||||
defp maybe_start_corp_wallet_tracker(true),
|
||||
do: [
|
||||
WandererApp.StartCorpWalletTrackerTask
|
||||
|
||||
@@ -179,20 +179,24 @@ defmodule WandererApp.Character do
|
||||
end
|
||||
|
||||
def search(character_id, opts \\ []) do
|
||||
{:ok, %{access_token: access_token, eve_id: eve_id} = _character} =
|
||||
get_character(character_id)
|
||||
get_character(character_id)
|
||||
|> case do
|
||||
{:ok, %{access_token: access_token, eve_id: eve_id} = _character} ->
|
||||
case WandererApp.Esi.search(eve_id |> String.to_integer(),
|
||||
access_token: access_token,
|
||||
character_id: character_id,
|
||||
refresh_token?: true,
|
||||
params: opts[:params]
|
||||
) do
|
||||
{:ok, result} ->
|
||||
{:ok, result |> prepare_search_results()}
|
||||
|
||||
case WandererApp.Esi.search(eve_id |> String.to_integer(),
|
||||
access_token: access_token,
|
||||
character_id: character_id,
|
||||
refresh_token?: true,
|
||||
params: opts[:params]
|
||||
) do
|
||||
{:ok, result} ->
|
||||
{:ok, result |> prepare_search_results()}
|
||||
error ->
|
||||
Logger.warning("#{__MODULE__} failed search: #{inspect(error)}")
|
||||
{:ok, []}
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.warning("#{__MODULE__} failed search: #{inspect(error)}")
|
||||
{:ok, []}
|
||||
end
|
||||
end
|
||||
@@ -203,12 +207,27 @@ defmodule WandererApp.Character do
|
||||
|
||||
def can_track_wallet?(_), do: false
|
||||
|
||||
def can_track_corp_wallet?(%{scopes: scopes} = _character) when not is_nil(scopes) do
|
||||
scopes |> String.split(" ") |> Enum.member?(@read_corp_wallet_scope)
|
||||
end
|
||||
def can_track_corp_wallet?(%{scopes: scopes} = _character)
|
||||
when not is_nil(scopes),
|
||||
do: scopes |> String.split(" ") |> Enum.member?(@read_corp_wallet_scope)
|
||||
|
||||
def can_track_corp_wallet?(_), do: false
|
||||
|
||||
@decorate cacheable(
|
||||
cache: WandererApp.Cache,
|
||||
key: "can_pause_tracking-#{character_id}"
|
||||
)
|
||||
def can_pause_tracking?(character_id) do
|
||||
case get_character(character_id) do
|
||||
{:ok, character} when not is_nil(character) ->
|
||||
not WandererApp.Env.character_tracking_pause_disabled?() &&
|
||||
not can_track_corp_wallet?(character)
|
||||
|
||||
_ ->
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def get_ship(%{ship: ship_type_id, ship_name: ship_name} = _character)
|
||||
when not is_nil(ship_type_id) and is_integer(ship_type_id) do
|
||||
ship_type_id
|
||||
|
||||
@@ -33,7 +33,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
status: binary()
|
||||
}
|
||||
|
||||
@pause_tracking_timeout :timer.minutes(60 * 24)
|
||||
@pause_tracking_timeout :timer.minutes(60 * 10)
|
||||
@offline_timeout :timer.minutes(5)
|
||||
@online_error_timeout :timer.minutes(2)
|
||||
@ship_error_timeout :timer.minutes(2)
|
||||
@@ -61,7 +61,11 @@ defmodule WandererApp.Character.Tracker do
|
||||
WandererApp.Cache.lookup!("character:#{character_id}:last_online_time")
|
||||
|> case do
|
||||
nil ->
|
||||
pause_tracking(character_id)
|
||||
WandererApp.Cache.insert(
|
||||
"character:#{character_id}:last_online_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
|
||||
:ok
|
||||
|
||||
last_online_time ->
|
||||
@@ -106,7 +110,8 @@ defmodule WandererApp.Character.Tracker do
|
||||
end
|
||||
|
||||
defp pause_tracking(character_id) do
|
||||
if not WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused") do
|
||||
if WandererApp.Character.can_pause_tracking?(character_id) &&
|
||||
not WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused") do
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_error_time")
|
||||
WandererApp.Cache.delete("character:#{character_id}:ship_error_time")
|
||||
@@ -182,8 +187,6 @@ defmodule WandererApp.Character.Tracker do
|
||||
"character:#{character_id}:last_online_time",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
else
|
||||
WandererApp.Cache.delete("character:#{character_id}:last_online_time")
|
||||
end
|
||||
|
||||
WandererApp.Cache.delete("character:#{character_id}:online_forbidden")
|
||||
@@ -230,9 +233,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to update_online: #{inspect(:error_limited)}"
|
||||
)
|
||||
Logger.warning(".")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:online_forbidden",
|
||||
@@ -319,9 +320,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to get_character_info: #{inspect(:error_limited)}"
|
||||
)
|
||||
Logger.warning(".")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:info_forbidden",
|
||||
@@ -398,7 +397,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning("#{__MODULE__} failed to update_ship: #{inspect(:error_limited)}")
|
||||
Logger.warning(".")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:ship_forbidden",
|
||||
@@ -464,8 +463,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
) do
|
||||
case WandererApp.Character.get_character(character_id) do
|
||||
{:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) ->
|
||||
(WandererApp.Cache.has_key?("character:#{character_id}:location_forbidden") ||
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused"))
|
||||
WandererApp.Cache.has_key?("character:#{character_id}:tracking_paused")
|
||||
|> case do
|
||||
true ->
|
||||
{:error, :skipped}
|
||||
@@ -496,9 +494,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :skipped}
|
||||
|
||||
{:error, :error_limited, headers} ->
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to update_location: #{inspect(:error_limited)}"
|
||||
)
|
||||
Logger.warning(".")
|
||||
|
||||
reset_timeout = get_reset_timeout(headers, @location_limit_ttl)
|
||||
|
||||
@@ -593,9 +589,7 @@ defmodule WandererApp.Character.Tracker do
|
||||
{:error, :error_limited, headers} ->
|
||||
reset_timeout = get_reset_timeout(headers)
|
||||
|
||||
Logger.warning(
|
||||
"#{__MODULE__} failed to update_wallet: #{inspect(:error_limited)}"
|
||||
)
|
||||
Logger.warning(".")
|
||||
|
||||
WandererApp.Cache.put(
|
||||
"character:#{character_id}:wallet_forbidden",
|
||||
|
||||
@@ -14,7 +14,8 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
|
||||
@garbage_collection_interval :timer.minutes(15)
|
||||
@untrack_characters_interval :timer.minutes(1)
|
||||
@inactive_character_timeout :timer.minutes(5)
|
||||
@inactive_character_timeout :timer.minutes(10)
|
||||
@untrack_character_timeout :timer.minutes(10)
|
||||
|
||||
@logger Application.compile_env(:wanderer_app, :logger)
|
||||
|
||||
@@ -107,32 +108,49 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
} = track_settings
|
||||
) do
|
||||
if track do
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"character_untrack_queue",
|
||||
[],
|
||||
fn untrack_queue ->
|
||||
untrack_queue
|
||||
|> Enum.reject(fn {m_id, c_id} -> m_id == map_id and c_id == character_id end)
|
||||
end
|
||||
)
|
||||
remove_from_untrack_queue(map_id, character_id)
|
||||
|
||||
{:ok, character_state} =
|
||||
WandererApp.Character.Tracker.update_settings(character_id, track_settings)
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, character_state)
|
||||
else
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"character_untrack_queue",
|
||||
[{map_id, character_id}],
|
||||
fn untrack_queue ->
|
||||
[{map_id, character_id} | untrack_queue] |> Enum.uniq()
|
||||
end
|
||||
)
|
||||
add_to_untrack_queue(map_id, character_id)
|
||||
end
|
||||
|
||||
state
|
||||
end
|
||||
|
||||
def add_to_untrack_queue(map_id, character_id) do
|
||||
if not WandererApp.Cache.has_key?("#{map_id}:#{character_id}:untrack_requested") do
|
||||
WandererApp.Cache.insert(
|
||||
"#{map_id}:#{character_id}:untrack_requested",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
end
|
||||
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"character_untrack_queue",
|
||||
[{map_id, character_id}],
|
||||
fn untrack_queue ->
|
||||
[{map_id, character_id} | untrack_queue] |> Enum.uniq()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def remove_from_untrack_queue(map_id, character_id) do
|
||||
WandererApp.Cache.delete("#{map_id}:#{character_id}:untrack_requested")
|
||||
|
||||
WandererApp.Cache.insert_or_update(
|
||||
"character_untrack_queue",
|
||||
[],
|
||||
fn untrack_queue ->
|
||||
untrack_queue
|
||||
|> Enum.reject(fn {m_id, c_id} -> m_id == map_id and c_id == character_id end)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def get_characters(
|
||||
state,
|
||||
_opts \\ []
|
||||
@@ -208,10 +226,28 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
) do
|
||||
Process.send_after(self(), :untrack_characters, @untrack_characters_interval)
|
||||
|
||||
WandererApp.Cache.get_and_remove!("character_untrack_queue", [])
|
||||
WandererApp.Cache.lookup!("character_untrack_queue", [])
|
||||
|> Task.async_stream(
|
||||
fn {map_id, character_id} ->
|
||||
if not character_is_present(map_id, character_id) do
|
||||
untrack_timeout_reached =
|
||||
if WandererApp.Cache.has_key?("#{map_id}:#{character_id}:untrack_requested") do
|
||||
untrack_requested =
|
||||
WandererApp.Cache.lookup!(
|
||||
"#{map_id}:#{character_id}:untrack_requested",
|
||||
DateTime.utc_now()
|
||||
)
|
||||
|
||||
duration = DateTime.diff(DateTime.utc_now(), untrack_requested, :millisecond)
|
||||
duration >= @untrack_character_timeout
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
Logger.debug(fn -> "Untrack timeout reached: #{inspect(untrack_timeout_reached)}" end)
|
||||
|
||||
if untrack_timeout_reached do
|
||||
remove_from_untrack_queue(map_id, character_id)
|
||||
|
||||
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:solar_system_id")
|
||||
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:station_id")
|
||||
WandererApp.Cache.delete("map:#{map_id}:character:#{character_id}:structure_id")
|
||||
@@ -235,6 +271,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do
|
||||
})
|
||||
|
||||
WandererApp.Character.update_character_state(character_id, character_state)
|
||||
WandererApp.Map.Server.Impl.broadcast!(map_id, :untrack_character, character_id)
|
||||
end
|
||||
end,
|
||||
max_concurrency: System.schedulers_online(),
|
||||
|
||||
@@ -18,7 +18,10 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
|
||||
@update_location_interval :timer.seconds(1)
|
||||
@update_online_interval :timer.seconds(5)
|
||||
@check_online_errors_interval :timer.seconds(30)
|
||||
@check_offline_characters_interval :timer.minutes(2)
|
||||
@check_online_errors_interval :timer.minutes(1)
|
||||
@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_wallet_interval :timer.minutes(1)
|
||||
@@ -130,7 +133,10 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
)
|
||||
|
||||
Process.send_after(self(), :update_online, 100)
|
||||
Process.send_after(self(), :check_online_errors, @check_online_errors_interval)
|
||||
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(), :update_location, 300)
|
||||
Process.send_after(self(), :update_ship, 500)
|
||||
Process.send_after(self(), :update_info, 1500)
|
||||
@@ -217,6 +223,50 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_offline_characters,
|
||||
%{
|
||||
characters: characters
|
||||
} =
|
||||
state
|
||||
) do
|
||||
Process.send_after(self(), :check_offline_characters, @check_offline_characters_interval)
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
if WandererApp.Character.can_pause_tracking?(character_id) do
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_offline,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, _result} -> :ok
|
||||
{:error, reason} -> @logger.error("Error in check_offline: #{inspect(reason)}")
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Tracker Pool] check_offline => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_online_errors,
|
||||
%{
|
||||
@@ -257,6 +307,86 @@ defmodule WandererApp.Character.TrackerPool do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_ship_errors,
|
||||
%{
|
||||
characters: characters
|
||||
} =
|
||||
state
|
||||
) do
|
||||
Process.send_after(self(), :check_ship_errors, @check_ship_errors_interval)
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_ship_errors,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, _result} -> :ok
|
||||
{:error, reason} -> @logger.error("Error in check_ship_errors: #{inspect(reason)}")
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Tracker Pool] check_ship_errors => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:check_location_errors,
|
||||
%{
|
||||
characters: characters
|
||||
} =
|
||||
state
|
||||
) do
|
||||
Process.send_after(self(), :check_location_errors, @check_location_errors_interval)
|
||||
|
||||
try do
|
||||
characters
|
||||
|> Task.async_stream(
|
||||
fn character_id ->
|
||||
WandererApp.TaskWrapper.start_link(
|
||||
WandererApp.Character.Tracker,
|
||||
:check_location_errors,
|
||||
[
|
||||
character_id
|
||||
]
|
||||
)
|
||||
end,
|
||||
timeout: :timer.seconds(15),
|
||||
max_concurrency: System.schedulers_online(),
|
||||
on_timeout: :kill_task
|
||||
)
|
||||
|> Enum.each(fn
|
||||
{:ok, _result} -> :ok
|
||||
{:error, reason} -> @logger.error("Error in check_location_errors: #{inspect(reason)}")
|
||||
end)
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("""
|
||||
[Tracker Pool] check_location_errors => exception: #{Exception.message(e)}
|
||||
#{Exception.format_stacktrace(__STACKTRACE__)}
|
||||
""")
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
:update_location,
|
||||
%{
|
||||
|
||||
@@ -54,7 +54,8 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
|
||||
{:ok, latest_transactions} = WandererApp.Api.CorpWalletTransaction.latest()
|
||||
|
||||
case WandererApp.Character.can_track_corp_wallet?(character) do
|
||||
case character.eve_id == WandererApp.Env.corp_wallet_eve_id() &&
|
||||
WandererApp.Character.can_track_corp_wallet?(character) do
|
||||
true ->
|
||||
Process.send_after(self(), :update_corp_wallets, 500)
|
||||
Process.send_after(self(), :check_wallets, 500)
|
||||
@@ -163,7 +164,7 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
{:error, :forbidden}
|
||||
|
||||
{:error, :error_limited, _headers} ->
|
||||
Logger.warning("#{__MODULE__} failed to get_wallet_journal: error_limited")
|
||||
Logger.warning(".")
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
@@ -192,7 +193,7 @@ defmodule WandererApp.Character.TransactionsTracker.Impl do
|
||||
{:error, :forbidden}
|
||||
|
||||
{:error, :error_limited, _headers} ->
|
||||
Logger.warning("#{__MODULE__} failed to update_corp_wallets: error_limited")
|
||||
Logger.warning(".")
|
||||
{:error, :error_limited}
|
||||
|
||||
{:error, error} ->
|
||||
|
||||
@@ -16,6 +16,13 @@ defmodule WandererApp.Env do
|
||||
def invites, do: get_key(:invites, false)
|
||||
def map_subscriptions_enabled?, do: get_key(:map_subscriptions_enabled, false)
|
||||
def public_api_disabled?, do: get_key(:public_api_disabled, false)
|
||||
|
||||
@decorate cacheable(
|
||||
cache: WandererApp.Cache,
|
||||
key: "active_tracking_pool"
|
||||
)
|
||||
def active_tracking_pool, do: get_key(:active_tracking_pool, "default")
|
||||
def character_tracking_pause_disabled?, do: get_key(:character_tracking_pause_disabled, true)
|
||||
def character_api_disabled?, do: get_key(:character_api_disabled, false)
|
||||
def zkill_preload_disabled?, do: get_key(:zkill_preload_disabled, false)
|
||||
def wallet_tracking_enabled?, do: get_key(:wallet_tracking_enabled, false)
|
||||
@@ -23,6 +30,7 @@ defmodule WandererApp.Env do
|
||||
def admin_username, do: get_key(:admin_username)
|
||||
def admin_password, do: get_key(:admin_password)
|
||||
def corp_wallet, do: get_key(:corp_wallet, "")
|
||||
def corp_wallet_eve_id, do: get_key(:corp_wallet_eve_id, "-1")
|
||||
def corp_eve_id, do: get_key(:corp_id, -1)
|
||||
def subscription_settings, do: get_key(:subscription_settings)
|
||||
|
||||
|
||||
@@ -536,7 +536,6 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:error, :not_found}
|
||||
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("#{path} error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited, headers}
|
||||
|
||||
{:ok, %{status: status} = _error} when status in [401, 403] ->
|
||||
@@ -593,7 +592,6 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:error, :forbidden}
|
||||
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("#{url} error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited, headers}
|
||||
|
||||
{:ok, %{status: status}} ->
|
||||
@@ -632,7 +630,6 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
{:error, :forbidden}
|
||||
|
||||
{:ok, %{status: 420, headers: headers} = _error} ->
|
||||
Logger.warning("#{url} error_limited error: #{inspect(headers)}")
|
||||
{:error, :error_limited, headers}
|
||||
|
||||
{:ok, %{status: status}} ->
|
||||
@@ -674,13 +671,20 @@ defmodule WandererApp.Esi.ApiClient do
|
||||
end
|
||||
|
||||
defp refresh_token(character_id) do
|
||||
{:ok, %{expires_at: expires_at, refresh_token: refresh_token, scopes: scopes} = character} =
|
||||
{:ok,
|
||||
%{
|
||||
expires_at: expires_at,
|
||||
refresh_token: refresh_token,
|
||||
scopes: scopes,
|
||||
tracking_pool: tracking_pool
|
||||
} = character} =
|
||||
WandererApp.Character.get_character(character_id)
|
||||
|
||||
refresh_token_result =
|
||||
WandererApp.Ueberauth.Strategy.Eve.OAuth.get_refresh_token([],
|
||||
with_wallet: WandererApp.Character.can_track_wallet?(character),
|
||||
is_admin?: WandererApp.Character.can_track_corp_wallet?(character),
|
||||
tracking_pool: tracking_pool,
|
||||
token: %OAuth2.AccessToken{refresh_token: refresh_token}
|
||||
)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ defmodule WandererApp.StartCorpWalletTrackerTask do
|
||||
|
||||
user_hash ->
|
||||
user_hash
|
||||
|> _get_user_characters()
|
||||
|> get_user_characters()
|
||||
|> maybe_start_corp_wallet_tracker()
|
||||
end
|
||||
end
|
||||
@@ -25,7 +25,8 @@ defmodule WandererApp.StartCorpWalletTrackerTask do
|
||||
admin_character =
|
||||
user_characters
|
||||
|> Enum.find(fn character ->
|
||||
WandererApp.Character.can_track_corp_wallet?(character)
|
||||
character.eve_id == WandererApp.Env.corp_wallet_eve_id() &&
|
||||
WandererApp.Character.can_track_corp_wallet?(character)
|
||||
end)
|
||||
|
||||
if not is_nil(admin_character) do
|
||||
@@ -41,12 +42,12 @@ defmodule WandererApp.StartCorpWalletTrackerTask do
|
||||
|
||||
def maybe_start_corp_wallet_tracker(_), do: :ok
|
||||
|
||||
defp _get_user_characters(user_hash) when not is_nil(user_hash) and is_binary(user_hash) do
|
||||
defp get_user_characters(user_hash) when not is_nil(user_hash) and is_binary(user_hash) do
|
||||
case WandererApp.Api.User.by_hash(user_hash, load: :characters) do
|
||||
{:ok, user} -> {:ok, user.characters}
|
||||
{:error, _} -> {:ok, []}
|
||||
end
|
||||
end
|
||||
|
||||
defp _get_user_characters(_), do: {:ok, []}
|
||||
defp get_user_characters(_), do: {:ok, []}
|
||||
end
|
||||
|
||||
@@ -294,14 +294,16 @@ defmodule WandererApp.Map do
|
||||
map_id
|
||||
|> update_map(%{characters_limit: characters_limit, hubs_limit: hubs_limit})
|
||||
|
||||
map
|
||||
map_id
|
||||
|> get_map!()
|
||||
end
|
||||
|
||||
def update_options!(%{map_id: map_id} = map, options) do
|
||||
map_id
|
||||
|> update_map(%{options: options})
|
||||
|
||||
map
|
||||
map_id
|
||||
|> get_map!()
|
||||
end
|
||||
|
||||
def add_systems!(map, []), do: map
|
||||
|
||||
@@ -76,6 +76,11 @@ defmodule WandererApp.Map.Operations do
|
||||
{:ok, map()} | {:skip, :exists} | {:error, String.t()}
|
||||
defdelegate create_connection(map_id, attrs, char_id), to: Connections
|
||||
|
||||
@doc "Create a connection from a Plug.Conn"
|
||||
@spec create_connection(Plug.Conn.t(), map()) ::
|
||||
{:ok, :created} | {:skip, :exists} | {:error, atom()}
|
||||
defdelegate create_connection(conn, attrs), to: Connections
|
||||
|
||||
@doc "Update a connection"
|
||||
@spec update_connection(String.t(), String.t(), map()) ::
|
||||
{:ok, map()} | {:error, String.t()}
|
||||
|
||||
@@ -212,7 +212,7 @@ defmodule WandererApp.Map.Server do
|
||||
do:
|
||||
map_id
|
||||
|> map_pid!
|
||||
|> GenServer.call({&Impl.update_subscription_settings/2, [settings]})
|
||||
|> GenServer.cast({&Impl.update_subscription_settings/2, [settings]})
|
||||
|
||||
def delete_connection(map_id, connection_info) when is_binary(map_id),
|
||||
do:
|
||||
|
||||
@@ -5,9 +5,10 @@ defmodule WandererApp.Map.Operations.Connections do
|
||||
"""
|
||||
|
||||
require Logger
|
||||
alias WandererApp.Map.Server.{ConnectionsImpl, Server}
|
||||
alias WandererApp.Map.Server
|
||||
alias Ash.Error.Invalid
|
||||
alias WandererApp.MapConnectionRepo
|
||||
alias WandererApp.CachedInfo
|
||||
|
||||
# Connection type constants
|
||||
@connection_type_wormhole 0
|
||||
@@ -20,7 +21,7 @@ defmodule WandererApp.Map.Operations.Connections do
|
||||
@xlarge_ship_size 3
|
||||
|
||||
# System class constants
|
||||
@c1_system_class "C1"
|
||||
@c1_system_class 1
|
||||
|
||||
@doc """
|
||||
Creates a connection between two systems, applying special rules for C1 wormholes.
|
||||
@@ -34,8 +35,8 @@ defmodule WandererApp.Map.Operations.Connections do
|
||||
defp do_create(attrs, map_id, char_id) do
|
||||
with {:ok, source} <- parse_int(attrs["solar_system_source"], "solar_system_source"),
|
||||
{:ok, target} <- parse_int(attrs["solar_system_target"], "solar_system_target"),
|
||||
{:ok, src_info} <- ConnectionsImpl.get_system_static_info(source),
|
||||
{:ok, tgt_info} <- ConnectionsImpl.get_system_static_info(target) do
|
||||
{:ok, src_info} <- CachedInfo.get_system_static_info(source),
|
||||
{:ok, tgt_info} <- CachedInfo.get_system_static_info(target) do
|
||||
build_and_add_connection(attrs, map_id, char_id, src_info, tgt_info)
|
||||
else
|
||||
{:error, reason} -> handle_precondition_error(reason, attrs)
|
||||
@@ -45,21 +46,28 @@ defmodule WandererApp.Map.Operations.Connections do
|
||||
end
|
||||
|
||||
defp build_and_add_connection(attrs, map_id, char_id, src_info, tgt_info) do
|
||||
info = %{
|
||||
solar_system_source_id: src_info.solar_system_id,
|
||||
solar_system_target_id: tgt_info.solar_system_id,
|
||||
character_id: char_id,
|
||||
type: parse_type(attrs["type"]),
|
||||
ship_size_type: resolve_ship_size(attrs, src_info, tgt_info)
|
||||
}
|
||||
Logger.debug("[Connections] build_and_add_connection called with src_info: #{inspect(src_info)}, tgt_info: #{inspect(tgt_info)}")
|
||||
|
||||
case Server.add_connection(map_id, info) do
|
||||
:ok -> {:ok, :created}
|
||||
{:ok, []} -> log_warn_and(:inconsistent_state, info)
|
||||
{:error, %Invalid{errors: errs}} = err ->
|
||||
if Enum.any?(errs, &is_unique_constraint_error?/1), do: {:skip, :exists}, else: err
|
||||
{:error, _} = err -> Logger.error("[add_connection] #{inspect(err)}"); {:error, :server_error}
|
||||
other -> Logger.error("[add_connection] unexpected: #{inspect(other)}"); {:error, :unexpected_error}
|
||||
# Guard against nil info
|
||||
if is_nil(src_info) or is_nil(tgt_info) do
|
||||
{:error, :invalid_system_info}
|
||||
else
|
||||
info = %{
|
||||
solar_system_source_id: src_info.solar_system_id,
|
||||
solar_system_target_id: tgt_info.solar_system_id,
|
||||
character_id: char_id,
|
||||
type: parse_type(attrs["type"]),
|
||||
ship_size_type: resolve_ship_size(attrs, src_info, tgt_info)
|
||||
}
|
||||
|
||||
case Server.add_connection(map_id, info) do
|
||||
:ok -> {:ok, :created}
|
||||
{:ok, []} -> log_warn_and(:inconsistent_state, info)
|
||||
{:error, %Invalid{errors: errs}} = err ->
|
||||
if Enum.any?(errs, &is_unique_constraint_error?/1), do: {:skip, :exists}, else: err
|
||||
{:error, _} = err -> Logger.error("[add_connection] #{inspect(err)}"); {:error, :server_error}
|
||||
other -> Logger.error("[add_connection] unexpected: #{inspect(other)}"); {:error, :unexpected_error}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -86,19 +86,24 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
end)
|
||||
end
|
||||
|
||||
def untrack_characters(map_id, character_ids),
|
||||
do:
|
||||
character_ids
|
||||
|> Enum.each(fn character_id ->
|
||||
if is_character_map_active?(map_id, character_id) do
|
||||
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: false
|
||||
})
|
||||
def untrack_characters(map_id, character_ids) do
|
||||
character_ids
|
||||
|> Enum.each(fn character_id ->
|
||||
is_character_map_active?(map_id, character_id)
|
||||
|> untrack_character(map_id, character_id)
|
||||
end)
|
||||
end
|
||||
|
||||
Impl.broadcast!(map_id, :untrack_character, character_id)
|
||||
end
|
||||
end)
|
||||
def untrack_character(true, map_id, character_id) do
|
||||
WandererApp.Character.TrackerManager.update_track_settings(character_id, %{
|
||||
map_id: map_id,
|
||||
track: false
|
||||
})
|
||||
end
|
||||
|
||||
def untrack_character(_is_character_map_active, _map_id, character_id) do
|
||||
:ok
|
||||
end
|
||||
|
||||
def is_character_map_active?(map_id, character_id) do
|
||||
case WandererApp.Character.get_character_state(character_id) do
|
||||
@@ -219,12 +224,11 @@ defmodule WandererApp.Map.Server.CharactersImpl do
|
||||
Task.start_link(fn ->
|
||||
character_updates =
|
||||
maybe_update_online(map_id, character_id) ++
|
||||
maybe_update_tracking_status(map_id, character_id)
|
||||
|
||||
maybe_update_location(map_id, character_id) ++
|
||||
maybe_update_ship(map_id, character_id) ++
|
||||
maybe_update_alliance(map_id, character_id) ++
|
||||
maybe_update_corporation(map_id, character_id)
|
||||
maybe_update_tracking_status(map_id, character_id) ++
|
||||
maybe_update_location(map_id, character_id) ++
|
||||
maybe_update_ship(map_id, character_id) ++
|
||||
maybe_update_alliance(map_id, character_id) ++
|
||||
maybe_update_corporation(map_id, character_id)
|
||||
|
||||
character_updates
|
||||
|> Enum.filter(fn update -> update != :skip end)
|
||||
|
||||
@@ -30,7 +30,7 @@ defmodule WandererApp.Map.Server.Impl do
|
||||
|
||||
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
|
||||
@backup_state_timeout :timer.minutes(1)
|
||||
@update_presence_timeout :timer.seconds(1)
|
||||
@update_presence_timeout :timer.seconds(5)
|
||||
@update_characters_timeout :timer.seconds(1)
|
||||
@update_tracked_characters_timeout :timer.seconds(1)
|
||||
|
||||
|
||||
@@ -20,17 +20,9 @@ defmodule WandererApp.Ueberauth.Strategy.Eve do
|
||||
is_admin? = Map.get(params, "admin", "false") in ~w(true 1)
|
||||
invite_token = Map.get(params, "invite", nil)
|
||||
|
||||
invite_token_valid =
|
||||
case WandererApp.Env.invites() do
|
||||
true ->
|
||||
case invite_token do
|
||||
nil -> false
|
||||
token -> WandererApp.Cache.lookup!("invite_#{token}", false)
|
||||
end
|
||||
{invite_token_valid, invite_type} = check_invite_valid(invite_token)
|
||||
|
||||
_ ->
|
||||
true
|
||||
end
|
||||
is_admin? = is_admin? || invite_type == :admin
|
||||
|
||||
case invite_token_valid do
|
||||
true ->
|
||||
@@ -200,10 +192,13 @@ defmodule WandererApp.Ueberauth.Strategy.Eve do
|
||||
end
|
||||
|
||||
defp oauth_client_options_from_conn(conn, with_wallet, is_admin?) do
|
||||
tracking_pool = WandererApp.Env.active_tracking_pool()
|
||||
|
||||
base_options = [
|
||||
redirect_uri: callback_url(conn),
|
||||
with_wallet: with_wallet,
|
||||
is_admin?: is_admin?
|
||||
is_admin?: is_admin?,
|
||||
tracking_pool: tracking_pool
|
||||
]
|
||||
|
||||
request_options = conn.private[:ueberauth_request_options].options
|
||||
@@ -218,4 +213,33 @@ defmodule WandererApp.Ueberauth.Strategy.Eve do
|
||||
defp option(conn, key) do
|
||||
Keyword.get(options(conn), key, Keyword.get(default_options(), key))
|
||||
end
|
||||
|
||||
defp check_invite_valid(invite_token) do
|
||||
case invite_token do
|
||||
token when not is_nil(token) and token != "" ->
|
||||
check_token_valid(token)
|
||||
|
||||
_ ->
|
||||
{not WandererApp.Env.invites(), :user}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_token_valid(token) do
|
||||
WandererApp.Cache.lookup!("invite_#{token}", false)
|
||||
|> case do
|
||||
true -> {true, :user}
|
||||
_ -> check_map_token_valid(token)
|
||||
end
|
||||
end
|
||||
|
||||
def check_map_token_valid(token) do
|
||||
{:ok, invites} = WandererApp.Api.MapInvite.read()
|
||||
|
||||
invites
|
||||
|> Enum.find(fn invite -> invite.token == token end)
|
||||
|> case do
|
||||
nil -> {false, nil}
|
||||
invite -> {true, invite.type}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
84
lib/wanderer_app/ueberauth/strategy/eve/init_configs_task.ex
Normal file
84
lib/wanderer_app/ueberauth/strategy/eve/init_configs_task.ex
Normal file
@@ -0,0 +1,84 @@
|
||||
defmodule WandererApp.Esi.InitClientsTask do
|
||||
use Task
|
||||
|
||||
require Logger
|
||||
|
||||
def start_link(arg) do
|
||||
Task.start_link(__MODULE__, :run, [arg])
|
||||
end
|
||||
|
||||
def run(_arg) do
|
||||
Logger.info("starting")
|
||||
|
||||
Cachex.put(
|
||||
:esi_auth_cache,
|
||||
:active_config,
|
||||
"config_#{WandererApp.Env.active_tracking_pool()}"
|
||||
)
|
||||
|
||||
cache_clients()
|
||||
end
|
||||
|
||||
defp cache_clients() do
|
||||
config = Application.get_env(:ueberauth, WandererApp.Ueberauth.Strategy.Eve.OAuth, [])
|
||||
|
||||
cache_client("default", %{
|
||||
client_id: config[:client_id_default],
|
||||
client_secret: config[:client_secret_default]
|
||||
})
|
||||
|
||||
Enum.each(1..10, fn index ->
|
||||
client_id = config["client_id_#{index}" |> String.to_atom()]
|
||||
client_secret = config["client_secret_#{index}" |> String.to_atom()]
|
||||
|
||||
if client_id != "" && client_secret != "" do
|
||||
cache_client(index, %{
|
||||
client_id: client_id,
|
||||
client_secret: client_secret
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp cache_client(id, config) do
|
||||
config_uuid = UUID.uuid4()
|
||||
|
||||
config =
|
||||
config
|
||||
|> Map.merge(%{
|
||||
id: id,
|
||||
uuid: config_uuid
|
||||
})
|
||||
|
||||
Cachex.put(
|
||||
:esi_auth_cache,
|
||||
"config_#{id}",
|
||||
config
|
||||
)
|
||||
|
||||
Cachex.put(
|
||||
:esi_auth_cache,
|
||||
config_uuid,
|
||||
config
|
||||
)
|
||||
|
||||
# Cachex.put(
|
||||
# :esi_auth_cache,
|
||||
# "config_uuid_#{id}",
|
||||
# config_uuid
|
||||
# )
|
||||
|
||||
configs_total_count =
|
||||
if id == "default" do
|
||||
0
|
||||
else
|
||||
id
|
||||
end
|
||||
|
||||
Cachex.put(
|
||||
:esi_auth_cache,
|
||||
"configs_total_count",
|
||||
configs_total_count
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -2,26 +2,50 @@ defmodule WandererApp.Ueberauth do
|
||||
@moduledoc false
|
||||
|
||||
def client_id(opts \\ []) do
|
||||
config = _get_config()
|
||||
config = get_config()
|
||||
tracking_pool = Keyword.get(opts, :tracking_pool)
|
||||
|
||||
cond do
|
||||
Keyword.get(opts, :is_admin?) -> config[:client_id_with_corp_wallet]
|
||||
Keyword.get(opts, :with_wallet) -> config[:client_id_with_wallet]
|
||||
not is_nil(tracking_pool) -> get_settings(tracking_pool)[:client_id]
|
||||
true -> config[:client_id_default]
|
||||
end
|
||||
end
|
||||
|
||||
def client_secret(opts \\ []) do
|
||||
config = _get_config()
|
||||
config = get_config()
|
||||
tracking_pool = Keyword.get(opts, :tracking_pool)
|
||||
|
||||
cond do
|
||||
Keyword.get(opts, :is_admin?) -> config[:client_secret_with_corp_wallet]
|
||||
Keyword.get(opts, :with_wallet) -> config[:client_secret_with_wallet]
|
||||
not is_nil(tracking_pool) -> get_settings(tracking_pool)[:client_secret]
|
||||
true -> config[:client_secret_default]
|
||||
end
|
||||
end
|
||||
|
||||
defp _get_config() do
|
||||
defp get_settings(nil) do
|
||||
{:ok, esi_config} =
|
||||
Cachex.get(
|
||||
:esi_auth_cache,
|
||||
"config_default"
|
||||
)
|
||||
|
||||
esi_config
|
||||
end
|
||||
|
||||
defp get_settings(tracking_pool) do
|
||||
{:ok, esi_config} =
|
||||
Cachex.get(
|
||||
:esi_auth_cache,
|
||||
"config_#{tracking_pool}"
|
||||
)
|
||||
|
||||
esi_config
|
||||
end
|
||||
|
||||
defp get_config() do
|
||||
Application.get_env(:ueberauth, WandererApp.Ueberauth.Strategy.Eve.OAuth, [])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,62 +62,68 @@ defmodule WandererApp.Zkb.KillsProvider.Fetcher do
|
||||
Returns `{:ok, kills, updated_state}` on success, or `{:error, reason, updated_state}`.
|
||||
"""
|
||||
def fetch_kills_for_system(system_id, since_hours, state, opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit, nil)
|
||||
force? = Keyword.get(opts, :force, false)
|
||||
zkill_preload_disabled = WandererApp.Env.zkill_preload_disabled?()
|
||||
|
||||
log_prefix = "[Fetcher] fetch_kills_for_system => system=#{system_id}"
|
||||
if not zkill_preload_disabled do
|
||||
limit = Keyword.get(opts, :limit, nil)
|
||||
force? = Keyword.get(opts, :force, false)
|
||||
|
||||
# Check the "recently fetched" cache if not forced
|
||||
if not force? and KillsCache.recently_fetched?(system_id) do
|
||||
cached_kills = KillsCache.fetch_cached_kills(system_id)
|
||||
final = maybe_take(cached_kills, limit)
|
||||
log_prefix = "[Fetcher] fetch_kills_for_system => system=#{system_id}"
|
||||
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, recently_fetched?=true => returning #{length(final)} cached kills"
|
||||
end)
|
||||
# Check the "recently fetched" cache if not forced
|
||||
if not force? and KillsCache.recently_fetched?(system_id) do
|
||||
cached_kills = KillsCache.fetch_cached_kills(system_id)
|
||||
final = maybe_take(cached_kills, limit)
|
||||
|
||||
{:ok, final, state}
|
||||
else
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, hours=#{since_hours}, limit=#{inspect(limit)}, force=#{force?}"
|
||||
end)
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, recently_fetched?=true => returning #{length(final)} cached kills"
|
||||
end)
|
||||
|
||||
cutoff_dt = hours_ago(since_hours)
|
||||
{:ok, final, state}
|
||||
else
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, hours=#{since_hours}, limit=#{inspect(limit)}, force=#{force?}"
|
||||
end)
|
||||
|
||||
result =
|
||||
retry with:
|
||||
exponential_backoff(300)
|
||||
|> randomize()
|
||||
|> cap(5_000)
|
||||
|> expiry(120_000) do
|
||||
case do_multi_page_fetch(system_id, cutoff_dt, 1, 0, limit, state) do
|
||||
{:ok, new_st, total_fetched} ->
|
||||
# Mark system as fully fetched (to prevent repeated calls).
|
||||
KillsCache.put_full_fetched_timestamp(system_id)
|
||||
final_kills = KillsCache.fetch_cached_kills(system_id) |> maybe_take(limit)
|
||||
cutoff_dt = hours_ago(since_hours)
|
||||
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, total_fetched=#{total_fetched}, final_cached=#{length(final_kills)}, calls_count=#{new_st.calls_count}"
|
||||
end)
|
||||
result =
|
||||
retry with:
|
||||
exponential_backoff(300)
|
||||
|> randomize()
|
||||
|> cap(5_000)
|
||||
|> expiry(120_000) do
|
||||
case do_multi_page_fetch(system_id, cutoff_dt, 1, 0, limit, state) do
|
||||
{:ok, new_st, total_fetched} ->
|
||||
# Mark system as fully fetched (to prevent repeated calls).
|
||||
KillsCache.put_full_fetched_timestamp(system_id)
|
||||
final_kills = KillsCache.fetch_cached_kills(system_id) |> maybe_take(limit)
|
||||
|
||||
{:ok, final_kills, new_st}
|
||||
Logger.debug(fn ->
|
||||
"#{log_prefix}, total_fetched=#{total_fetched}, final_cached=#{length(final_kills)}, calls_count=#{new_st.calls_count}"
|
||||
end)
|
||||
|
||||
{:error, :rate_limited, _new_st} ->
|
||||
raise ":rate_limited"
|
||||
{:ok, final_kills, new_st}
|
||||
|
||||
{:error, reason, _new_st} ->
|
||||
raise "#{log_prefix}, reason=#{inspect(reason)}"
|
||||
{:error, :rate_limited, _new_st} ->
|
||||
raise ":rate_limited"
|
||||
|
||||
{:error, reason, _new_st} ->
|
||||
raise "#{log_prefix}, reason=#{inspect(reason)}"
|
||||
end
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, kills, new_st} ->
|
||||
{:ok, kills, new_st}
|
||||
|
||||
error ->
|
||||
Logger.error("#{log_prefix}, EXHAUSTED => error=#{inspect(error)}")
|
||||
{:error, error, state}
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, kills, new_st} ->
|
||||
{:ok, kills, new_st}
|
||||
|
||||
error ->
|
||||
Logger.error("#{log_prefix}, EXHAUSTED => error=#{inspect(error)}")
|
||||
{:error, error, state}
|
||||
end
|
||||
else
|
||||
raise ":kills_disabled"
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
|
||||
@@ -14,7 +14,8 @@ defmodule WandererAppWeb.AuthController do
|
||||
access_token: auth.credentials.token,
|
||||
refresh_token: auth.credentials.refresh_token,
|
||||
expires_at: auth.credentials.expires_at,
|
||||
scopes: auth.credentials.scopes
|
||||
scopes: auth.credentials.scopes,
|
||||
tracking_pool: WandererApp.Env.active_tracking_pool()
|
||||
}
|
||||
|
||||
%{
|
||||
@@ -29,7 +30,8 @@ defmodule WandererAppWeb.AuthController do
|
||||
access_token: auth.credentials.token,
|
||||
refresh_token: auth.credentials.refresh_token,
|
||||
expires_at: auth.credentials.expires_at,
|
||||
scopes: auth.credentials.scopes
|
||||
scopes: auth.credentials.scopes,
|
||||
tracking_pool: WandererApp.Env.active_tracking_pool()
|
||||
}
|
||||
|
||||
{:ok, character} =
|
||||
|
||||
@@ -266,6 +266,10 @@ defmodule WandererAppWeb.MapConnectionAPIController do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: reason})
|
||||
{:error, :precondition_failed, _reason} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: "Invalid request parameters"})
|
||||
_other ->
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule WandererAppWeb.MapSystemAPIController do
|
||||
solar_system_id: %Schema{type: :integer, description: "EVE solar system ID"},
|
||||
solar_system_name: %Schema{type: :string, description: "EVE solar system name"},
|
||||
region_name: %Schema{type: :string, description: "EVE region name"},
|
||||
custom_name: %Schema{type: :string, nullable: true, description: "Custom name for the system"},
|
||||
position_x: %Schema{type: :integer, description: "X coordinate"},
|
||||
position_y: %Schema{type: :integer, description: "Y coordinate"},
|
||||
status: %Schema{
|
||||
@@ -137,6 +138,7 @@ defmodule WandererAppWeb.MapSystemAPIController do
|
||||
solar_system_id: 30_000_142,
|
||||
solar_system_name: "Jita",
|
||||
region_name: "The Forge",
|
||||
custom_name: "Trade Hub Central",
|
||||
position_x: 100.5,
|
||||
position_y: 200.3,
|
||||
status: "active",
|
||||
@@ -179,6 +181,7 @@ defmodule WandererAppWeb.MapSystemAPIController do
|
||||
solar_system_id: 30_000_142,
|
||||
solar_system_name: "Jita",
|
||||
region_name: "The Forge",
|
||||
custom_name: "Trade Hub Central",
|
||||
position_x: 100.5,
|
||||
position_y: 200.3,
|
||||
status: "active",
|
||||
|
||||
@@ -262,13 +262,18 @@ defmodule WandererAppWeb.Helpers.APIUtils do
|
||||
|
||||
@spec map_system_to_json(struct()) :: map()
|
||||
def map_system_to_json(system) do
|
||||
original = get_original_name(system.solar_system_id)
|
||||
|
||||
# Determine the actual custom_name: if name differs from original, use it as custom_name
|
||||
actual_custom_name = if system.name != original and system.name not in [nil, ""], do: system.name, else: system.custom_name
|
||||
|
||||
base =
|
||||
Map.take(system, ~w(
|
||||
id map_id solar_system_id custom_name temporary_name description tag labels
|
||||
id map_id solar_system_id temporary_name description tag labels
|
||||
locked visible status position_x position_y inserted_at updated_at
|
||||
)a)
|
||||
|> Map.put(:custom_name, actual_custom_name)
|
||||
|
||||
original = get_original_name(system.solar_system_id)
|
||||
name = pick_name(system)
|
||||
|
||||
base
|
||||
@@ -283,11 +288,15 @@ defmodule WandererAppWeb.Helpers.APIUtils do
|
||||
end
|
||||
end
|
||||
|
||||
defp pick_name(%{temporary_name: t, custom_name: c, solar_system_id: id}) do
|
||||
defp pick_name(%{temporary_name: t, custom_name: c, name: n, solar_system_id: id} = system) do
|
||||
original = get_original_name(id)
|
||||
|
||||
cond do
|
||||
t not in [nil, ""] -> t
|
||||
c not in [nil, ""] -> c
|
||||
true -> get_original_name(id)
|
||||
# If name differs from original, it's a custom name
|
||||
n not in [nil, ""] and n != original -> n
|
||||
true -> original
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@ defmodule WandererAppWeb.AdminLive do
|
||||
corp_wallet_character =
|
||||
socket.assigns.current_user.characters
|
||||
|> Enum.find(fn character ->
|
||||
WandererApp.Character.can_track_corp_wallet?(character)
|
||||
character.eve_id == WandererApp.Env.corp_wallet_eve_id() &&
|
||||
WandererApp.Character.can_track_corp_wallet?(character)
|
||||
end)
|
||||
|
||||
Phoenix.PubSub.subscribe(
|
||||
@@ -58,10 +59,10 @@ defmodule WandererAppWeb.AdminLive do
|
||||
socket
|
||||
|> assign(
|
||||
active_map_subscriptions: active_map_subscriptions,
|
||||
show_invites?: WandererApp.Env.invites(),
|
||||
user_character_ids: user_character_ids,
|
||||
user_id: user_id,
|
||||
invite_link: nil,
|
||||
tracker_stats: [],
|
||||
map_subscriptions_enabled?: WandererApp.Env.map_subscriptions_enabled?(),
|
||||
restrict_maps_creation?: WandererApp.Env.restrict_maps_creation?()
|
||||
)}
|
||||
@@ -77,21 +78,6 @@ defmodule WandererAppWeb.AdminLive do
|
||||
{:noreply, apply_action(socket, socket.assigns.live_action, params, uri)}
|
||||
end
|
||||
|
||||
def handle_event("generate-invite-link", _params, socket) do
|
||||
uuid = UUID.uuid4(:default)
|
||||
WandererApp.Cache.put("invite_#{uuid}", true, ttl: @invite_link_ttl)
|
||||
|
||||
invite_link =
|
||||
socket.assigns.uri
|
||||
|> Map.put(:path, "/welcome")
|
||||
|> Map.put(:query, URI.encode_query(%{invite: uuid}))
|
||||
|> URI.to_string()
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(invite_link: invite_link)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("update-eve-db-data", _params, socket) do
|
||||
WandererApp.EveDataService.update_eve_data()
|
||||
@@ -224,6 +210,58 @@ defmodule WandererAppWeb.AdminLive do
|
||||
|> push_navigate(to: ~p"/maps/new")}
|
||||
end
|
||||
|
||||
def handle_event("validate", %{"form" => params}, socket) do
|
||||
form = AshPhoenix.Form.validate(socket.assigns.form, params)
|
||||
{:noreply, assign(socket, form: form)}
|
||||
end
|
||||
|
||||
def handle_event("generate-invite-link", _params, socket) do
|
||||
token = UUID.uuid4()
|
||||
new_params = Map.put(socket.assigns.form.params || %{}, "token", token)
|
||||
form = AshPhoenix.Form.validate(socket.assigns.form, new_params)
|
||||
|
||||
invite_link =
|
||||
socket.assigns.uri
|
||||
|> get_invite_link(token)
|
||||
|
||||
{:noreply, assign(socket, form: form, invite_link: invite_link)}
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
"add_invite_link",
|
||||
%{"form" => %{"type" => type, "valid_until" => valid_until}},
|
||||
socket
|
||||
) do
|
||||
%{
|
||||
type: type |> String.to_existing_atom(),
|
||||
valid_until: get_valid_until(valid_until),
|
||||
token: UUID.uuid4(),
|
||||
map_id: nil
|
||||
}
|
||||
|> WandererApp.Api.MapInvite.new()
|
||||
|> case do
|
||||
{:ok, _invite} ->
|
||||
{:noreply, socket |> push_patch(to: ~p"/admin")}
|
||||
|
||||
error ->
|
||||
{:noreply, socket |> put_flash(:error, "Failed to add invite. Try again.")}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
"delete-invite",
|
||||
%{"id" => id},
|
||||
socket
|
||||
) do
|
||||
id
|
||||
|> WandererApp.Api.MapInvite.by_id!()
|
||||
|> WandererApp.Api.MapInvite.destroy!()
|
||||
|
||||
{:ok, invites} = WandererApp.Api.MapInvite.read()
|
||||
|
||||
{:noreply, socket |> assign(:invites, invites)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event(event, body, socket) do
|
||||
Logger.warning(fn -> "unhandled event: #{event} #{inspect(body)}" end)
|
||||
@@ -255,6 +293,10 @@ defmodule WandererAppWeb.AdminLive do
|
||||
end
|
||||
|
||||
defp apply_action(socket, :index, _params, uri) do
|
||||
{:ok, invites} = WandererApp.Api.MapInvite.read()
|
||||
|
||||
{:ok, tracker_stats} = load_tracker_stats()
|
||||
|
||||
socket
|
||||
|> assign(:active_page, :admin)
|
||||
|> assign(:uri, URI.parse(uri))
|
||||
@@ -268,6 +310,113 @@ defmodule WandererAppWeb.AdminLive do
|
||||
])
|
||||
|> assign(:form, to_form(%{"amount" => 500_000_000}))
|
||||
|> assign(:unlink_character_form, to_form(%{}))
|
||||
|> assign(:invites, invites)
|
||||
|> assign(:tracker_stats, tracker_stats)
|
||||
end
|
||||
|
||||
defp apply_action(socket, :add_invite_link, _params, uri) do
|
||||
socket
|
||||
|> assign(:active_page, :admin)
|
||||
|> assign(:uri, URI.parse(uri))
|
||||
|> assign(:page_title, "Add Invite Link")
|
||||
|> assign(:invite_types, [%{label: "User", id: :user}, %{label: "Admin", id: :admin}])
|
||||
|> assign(:valid_types, [
|
||||
%{label: "1D", id: 1},
|
||||
%{label: "1W", id: 7},
|
||||
%{label: "1M", id: 30},
|
||||
%{label: "1Y", id: 365}
|
||||
])
|
||||
|> assign(:unlink_character_form, to_form(%{}))
|
||||
|> assign(:character_search_options, [])
|
||||
|> assign(:amounts, [
|
||||
%{label: "500M", value: 500_000_000},
|
||||
%{label: "1B", value: 1_000_000_000},
|
||||
%{label: "5B", value: 5_000_000_000},
|
||||
%{label: "10B", value: 10_000_000_000}
|
||||
])
|
||||
|> assign(:form, to_form(%{"amount" => 500_000_000}))
|
||||
|> assign(:invite_token, UUID.uuid4())
|
||||
|> assign(
|
||||
:form,
|
||||
AshPhoenix.Form.for_create(WandererApp.Api.MapInvite, :new,
|
||||
forms: [
|
||||
auto?: true
|
||||
]
|
||||
)
|
||||
|> to_form()
|
||||
)
|
||||
|> assign(:invites, [])
|
||||
end
|
||||
|
||||
defp load_tracker_stats() do
|
||||
{:ok, characters} =
|
||||
WandererApp.Api.Character.last_active(%{
|
||||
from:
|
||||
DateTime.utc_now()
|
||||
|> DateTime.add(-1 * 60 * 24 * 7, :minute)
|
||||
})
|
||||
|
||||
admins_count =
|
||||
characters |> Enum.filter(&WandererApp.Character.can_track_corp_wallet?/1) |> Enum.count()
|
||||
|
||||
with_wallets_count =
|
||||
characters
|
||||
|> Enum.filter(
|
||||
&(WandererApp.Character.can_track_wallet?(&1) and
|
||||
not WandererApp.Character.can_track_corp_wallet?(&1))
|
||||
)
|
||||
|> Enum.count()
|
||||
|
||||
default_count =
|
||||
characters
|
||||
|> Enum.filter(
|
||||
&(is_nil(&1.tracking_pool) and not WandererApp.Character.can_track_wallet?(&1) and
|
||||
not WandererApp.Character.can_track_corp_wallet?(&1))
|
||||
)
|
||||
|> Enum.count()
|
||||
|
||||
result = [
|
||||
%{title: "Admins", value: admins_count},
|
||||
%{title: "With Wallet", value: with_wallets_count},
|
||||
%{title: "Default", value: default_count}
|
||||
]
|
||||
|
||||
{:ok, pools_count} =
|
||||
Cachex.get(
|
||||
:esi_auth_cache,
|
||||
"configs_total_count"
|
||||
)
|
||||
|
||||
result =
|
||||
if not is_nil(pools_count) && pools_count != 0 do
|
||||
pools =
|
||||
1..pools_count
|
||||
|> Enum.map(fn pool_id ->
|
||||
pools_character_count =
|
||||
characters
|
||||
|> Enum.filter(
|
||||
&(&1.tracking_pool == "#{pool_id}" and
|
||||
not WandererApp.Character.can_track_wallet?(&1) and
|
||||
not WandererApp.Character.can_track_corp_wallet?(&1))
|
||||
)
|
||||
|> Enum.count()
|
||||
|
||||
%{title: "Pool #{pool_id}", value: pools_character_count}
|
||||
end)
|
||||
|
||||
result ++ pools
|
||||
else
|
||||
result
|
||||
end
|
||||
|
||||
{:ok, result}
|
||||
end
|
||||
|
||||
defp get_invite_link(uri, token) do
|
||||
uri
|
||||
|> Map.put(:path, "/auth/eve")
|
||||
|> Map.put(:query, URI.encode_query(%{invite: token}))
|
||||
|> URI.to_string()
|
||||
end
|
||||
|
||||
defp search(search) do
|
||||
@@ -290,11 +439,29 @@ defmodule WandererAppWeb.AdminLive do
|
||||
</div>
|
||||
</div>
|
||||
<span :if={@option.value == :loading} <span class="loading loading-spinner loading-xs"></span>
|
||||
<%= @option.label %>
|
||||
{@option.label}
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp get_valid_until("1") do
|
||||
DateTime.utc_now() |> DateTime.add(24 * 3600, :second)
|
||||
end
|
||||
|
||||
defp get_valid_until("7") do
|
||||
DateTime.utc_now() |> DateTime.add(24 * 3600 * 7, :second)
|
||||
end
|
||||
|
||||
defp get_valid_until("30") do
|
||||
DateTime.utc_now() |> DateTime.add(24 * 3600 * 30, :second)
|
||||
end
|
||||
|
||||
defp get_valid_until("365") do
|
||||
DateTime.utc_now() |> DateTime.add(24 * 3600 * 365, :second)
|
||||
end
|
||||
|
||||
defp get_valid_until(_), do: get_valid_until("1")
|
||||
|
||||
def search_member_icon_url(%{character: true} = option),
|
||||
do: member_icon_url(%{eve_character_id: option.value})
|
||||
|
||||
|
||||
@@ -112,35 +112,78 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :if={@show_invites?} class="card dark:bg-zinc-800 dark:border-zinc-600">
|
||||
<div class="card dark:bg-zinc-800 dark:border-zinc-600">
|
||||
<div class="card-body">
|
||||
<div class="col-span-6">
|
||||
<span class="text-gray-400 dark:text-gray-400">Invite Link</span>
|
||||
<span class="text-gray-400 dark:text-gray-400">Invites</span>
|
||||
<h4 class="my-4 font-medium text-gray-800 text-4xl dark:text-gray-100">
|
||||
<.button class="btn btn-primary" phx-click="generate-invite-link">
|
||||
Generate
|
||||
</.button>
|
||||
|
||||
<div :if={not is_nil(@invite_link)} class="join">
|
||||
<input
|
||||
class="input input-bordered join-item"
|
||||
readonly
|
||||
type="text"
|
||||
value={@invite_link}
|
||||
/>
|
||||
<.link class="btn mt-2 w-full btn-neutral rounded-none" patch={~p"/admin/invite"}>
|
||||
<.icon name="hero-plus-solid" class="w-6 h-6" />
|
||||
<h3 class="card-title text-center text-md">New Invite</h3>
|
||||
</.link>
|
||||
</h4>
|
||||
<.table
|
||||
id="invites"
|
||||
rows={@invites}
|
||||
class="!max-h-[40vh] !overflow-y-auto"
|
||||
>
|
||||
<:col :let={invite} label="Link">
|
||||
<div class="flex items-center">
|
||||
<div class="w-[200px] no-wrap truncate"><%= get_invite_link(@uri, invite.token) %></div>
|
||||
<.button
|
||||
phx-hook="CopyToClipboard"
|
||||
id="copy-to-clipboard"
|
||||
class="copy-link btn join-item rounded-r-full"
|
||||
data-url={@invite_link}
|
||||
class="copy-link btn btn-neutral rounded-none"
|
||||
data-url={get_invite_link(@uri, invite.token)}
|
||||
>
|
||||
Copy
|
||||
<div class="absolute w-[100px] !mr-[-170px] link-copied hidden">
|
||||
Link copied
|
||||
</div>
|
||||
</.button>
|
||||
</div>
|
||||
</h4>
|
||||
</div>
|
||||
</:col>
|
||||
<:col :let={invite} label="Type">
|
||||
<%= invite.type %>
|
||||
</:col>
|
||||
<:col :let={invite} label="Valid Until">
|
||||
<div>
|
||||
<p class="mb-0 text-xs text-gray-600 dark:text-zinc-100 whitespace-nowrap">
|
||||
<.local_time id={invite.id} at={invite.valid_until} />
|
||||
</p>
|
||||
</div>
|
||||
</:col>
|
||||
<:action :let={invite}>
|
||||
<.button
|
||||
phx-click="delete-invite"
|
||||
phx-value-id={invite.id}
|
||||
data={[confirm: "Please confirm to delete invite!"]}
|
||||
class="hover:text-white"
|
||||
>
|
||||
<.icon name="hero-trash-solid" class="w-4 h-4" />
|
||||
</.button>
|
||||
</:action>
|
||||
</.table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card dark:bg-zinc-800 dark:border-zinc-600">
|
||||
<div class="card-body">
|
||||
<div class="col-span-6">
|
||||
<span class="text-gray-400 dark:text-gray-400">Tracking Pools</span>
|
||||
<.table
|
||||
id="tracking_pools"
|
||||
rows={@tracker_stats}
|
||||
class="!max-h-[40vh] !overflow-y-auto"
|
||||
>
|
||||
<:col :let={stat} label="Pool">
|
||||
<div class="w-[200px] no-wrap truncate">{stat.title}</div>
|
||||
</:col>
|
||||
<:col :let={stat} label="Count">
|
||||
{stat.value}
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -261,4 +304,40 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<.modal
|
||||
:if={@live_action in [:add_invite_link]}
|
||||
title={"New Invite"}
|
||||
class="!w-[500px]"
|
||||
id="add_invite_link_modal"
|
||||
show
|
||||
on_cancel={JS.patch(~p"/admin")}
|
||||
>
|
||||
|
||||
<.form :let={f} for={@form} phx-change="validate" phx-submit={@live_action}>
|
||||
<.input
|
||||
type="select"
|
||||
field={f[:type]}
|
||||
class="select h-8 min-h-[10px] !pt-1 !pb-1 text-sm bg-neutral-900"
|
||||
wrapper_class="mt-2"
|
||||
label="Type"
|
||||
options={Enum.map(@invite_types, fn invite_type -> {invite_type.label, invite_type.id} end)}
|
||||
/>
|
||||
|
||||
<.input
|
||||
type="select"
|
||||
field={f[:valid_until]}
|
||||
class="select h-8 min-h-[10px] !pt-1 !pb-1 text-sm bg-neutral-900"
|
||||
wrapper_class="mt-2"
|
||||
label="Valid"
|
||||
options={Enum.map(@valid_types, fn valid_type -> {valid_type.label, valid_type.id} end)}
|
||||
/>
|
||||
|
||||
<!-- API Key Section with grid layout -->
|
||||
<div class="modal-action">
|
||||
<.button class="mt-2" type="submit" phx-disable-with="Saving...">
|
||||
<%= (@live_action == :add_invite_link && "Add") || "Save" %>
|
||||
</.button>
|
||||
</div>
|
||||
</.form>
|
||||
</.modal>
|
||||
</main>
|
||||
|
||||
@@ -5,6 +5,8 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
|
||||
alias BetterNumber, as: Number
|
||||
|
||||
@active_tracking_pool 1
|
||||
|
||||
def mount(_params, %{"user_id" => user_id} = _session, socket)
|
||||
when not is_nil(user_id) do
|
||||
{:ok, characters} = WandererApp.Api.Character.active_by_user(%{user_id: user_id})
|
||||
@@ -56,17 +58,25 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
@impl true
|
||||
def handle_event("authorize", form, socket) do
|
||||
track_wallet = form |> Map.get("track_wallet", false)
|
||||
token = UUID.uuid4(:default)
|
||||
WandererApp.Cache.put("invite_#{token}", true, ttl: :timer.minutes(30))
|
||||
|
||||
{:noreply, socket |> push_navigate(to: ~p"/auth/eve?invite=#{token}&w=#{track_wallet}")}
|
||||
{:ok, esi_config} =
|
||||
Cachex.get(
|
||||
:esi_auth_cache,
|
||||
"config_#{WandererApp.Env.active_tracking_pool()}"
|
||||
)
|
||||
|
||||
WandererApp.Cache.put("invite_#{esi_config.uuid}", true, ttl: :timer.minutes(30))
|
||||
|
||||
{:noreply,
|
||||
socket |> push_navigate(to: ~p"/auth/eve?invite=#{esi_config.uuid}&w=#{track_wallet}")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("delete", %{"character_id" => character_id}, socket) do
|
||||
WandererApp.Character.TrackerManager.stop_tracking(character_id)
|
||||
|
||||
{:ok, map_user_settings} = WandererApp.Api.MapCharacterSettings.tracked_by_character(%{character_id: character_id})
|
||||
{:ok, map_user_settings} =
|
||||
WandererApp.Api.MapCharacterSettings.tracked_by_character(%{character_id: character_id})
|
||||
|
||||
map_user_settings
|
||||
|> Enum.each(fn settings ->
|
||||
@@ -87,6 +97,15 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
{:noreply, socket |> assign(characters: characters |> Enum.map(&map_ui_character/1))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event(
|
||||
"validate",
|
||||
params,
|
||||
socket
|
||||
) do
|
||||
{:noreply, assign(socket, form: params)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("show_table", %{"value" => "on"}, socket) do
|
||||
{:noreply, socket |> assign(mode: :table)}
|
||||
@@ -148,7 +167,7 @@ defmodule WandererAppWeb.CharactersLive do
|
||||
socket
|
||||
|> assign(:active_page, :characters)
|
||||
|> assign(:page_title, "Authorize Character - Characters")
|
||||
|> assign(:form, to_form(%{"track_wallet" => false}))
|
||||
|> assign(:form, to_form(%{}))
|
||||
end
|
||||
|
||||
defp map_ui_character(character) do
|
||||
|
||||
@@ -242,10 +242,13 @@
|
||||
show
|
||||
on_cancel={JS.patch(~p"/characters")}
|
||||
>
|
||||
<.form :let={f} for={@form} phx-submit="authorize">
|
||||
<div :if={@wallet_tracking_enabled?} class="pb-2 -mt-8">
|
||||
<div class="flex flex-col gap-3">
|
||||
|
||||
<.form :let={f} for={@form} phx-submit="authorize" phx-change="validate">
|
||||
<div :if={@wallet_tracking_enabled?} class="pb-2">
|
||||
<.input
|
||||
type="checkbox"
|
||||
|
||||
field={f[:track_wallet]}
|
||||
label="Access to character wallet information"
|
||||
/>
|
||||
@@ -254,5 +257,6 @@
|
||||
<.button type="submit">AUTHORIZE</.button>
|
||||
</div>
|
||||
</.form>
|
||||
</div>
|
||||
</.modal>
|
||||
</main>
|
||||
|
||||
@@ -11,7 +11,7 @@ defmodule WandererAppWeb.ServerStatusLive do
|
||||
"server_status"
|
||||
)
|
||||
|
||||
{:ok, socket |> assign(server_online: false)}
|
||||
{:ok, socket |> assign(server_online: true)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
||||
@@ -230,7 +230,11 @@ defmodule WandererAppWeb.Router do
|
||||
delete "/connections", MapConnectionAPIController, :delete
|
||||
delete "/systems", MapSystemAPIController, :delete
|
||||
resources "/systems", MapSystemAPIController, only: [:index, :show, :create, :update, :delete]
|
||||
resources "/connections", MapConnectionAPIController, only: [:index, :show, :create, :update, :delete], param: "id"
|
||||
|
||||
resources "/connections", MapConnectionAPIController,
|
||||
only: [:index, :show, :create, :update, :delete],
|
||||
param: "id"
|
||||
|
||||
resources "/structures", MapSystemStructureAPIController, except: [:new, :edit]
|
||||
get "/structure-timers", MapSystemStructureAPIController, :structure_timers
|
||||
resources "/signatures", MapSystemSignatureAPIController, except: [:new, :edit]
|
||||
@@ -238,8 +242,6 @@ defmodule WandererAppWeb.Router do
|
||||
get "/tracked-characters", MapAPIController, :show_tracked_characters
|
||||
end
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Other API routes
|
||||
#
|
||||
@@ -359,6 +361,7 @@ defmodule WandererAppWeb.Router do
|
||||
WandererAppWeb.Nav
|
||||
] do
|
||||
live("/", AdminLive, :index)
|
||||
live("/invite", AdminLive, :add_invite_link)
|
||||
end
|
||||
|
||||
error_tracker_dashboard("/errors",
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
|
||||
@version "1.66.15"
|
||||
@version "1.70.1"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
40
priv/repo/migrations/20250608220906_add_map_invites.exs
Normal file
40
priv/repo/migrations/20250608220906_add_map_invites.exs
Normal file
@@ -0,0 +1,40 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddMapInvites 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
|
||||
create table(:map_invites_v1, primary_key: false) do
|
||||
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
|
||||
add :token, :text
|
||||
add :valid_until, :utc_datetime
|
||||
|
||||
add :inserted_at, :utc_datetime_usec,
|
||||
null: false,
|
||||
default: fragment("(now() AT TIME ZONE 'utc')")
|
||||
|
||||
add :updated_at, :utc_datetime_usec,
|
||||
null: false,
|
||||
default: fragment("(now() AT TIME ZONE 'utc')")
|
||||
|
||||
add :map_id,
|
||||
references(:maps_v1,
|
||||
column: :id,
|
||||
name: "map_invites_v1_map_id_fkey",
|
||||
type: :uuid,
|
||||
prefix: "public",
|
||||
on_delete: :delete_all
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
drop constraint(:map_invites_v1, "map_invites_v1_map_id_fkey")
|
||||
|
||||
drop table(:map_invites_v1)
|
||||
end
|
||||
end
|
||||
21
priv/repo/migrations/20250608221356_add_map_invite_type.exs
Normal file
21
priv/repo/migrations/20250608221356_add_map_invite_type.exs
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddMapInviteType 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(:map_invites_v1) do
|
||||
add :type, :text, null: false, default: "user"
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:map_invites_v1) do
|
||||
remove :type
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddCharacterTrackingPool 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(:character_v1) do
|
||||
add :tracking_pool, :text
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:character_v1) do
|
||||
remove :tracking_pool
|
||||
end
|
||||
end
|
||||
end
|
||||
343
priv/resource_snapshots/repo/character_v1/20250611074436.json
Normal file
343
priv/resource_snapshots/repo/character_v1/20250611074436.json
Normal file
@@ -0,0 +1,343 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_item_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "tracking_pool",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"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": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_eve_wallet_balance",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_location",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_solar_system_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_structure_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_station_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_access_token",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_refresh_token",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "4C6974D764592ED3279CFD3FF85CE89B146C7E16628F0A4278E319A4A5E5FD8C",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "character_v1_unique_eve_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_eve_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "valid_until",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"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": "map_invites_v1_map_id_fkey",
|
||||
"on_delete": "delete",
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "B4122C666C9DCF20E420209F604765AB1A3C4979D4134916F7EF9292162B250C",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_invites_v1"
|
||||
}
|
||||
108
priv/resource_snapshots/repo/map_invites_v1/20250608221356.json
Normal file
108
priv/resource_snapshots/repo/map_invites_v1/20250608221356.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "\"user\"",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "valid_until",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"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": "map_invites_v1_map_id_fkey",
|
||||
"on_delete": "delete",
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "99662C7392D745B0B2D22445A3A703DC2287EBBA185BB7818D58F472B5D033D3",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_invites_v1"
|
||||
}
|
||||
Reference in New Issue
Block a user