fix: api and structure search fixes

This commit is contained in:
Guarzo
2025-11-12 07:07:39 +00:00
parent d4657b335f
commit 6091adb28e
4 changed files with 77 additions and 39 deletions

View File

@@ -30,9 +30,6 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
const { outCommand } = useMapRootState(); const { outCommand } = useMapRootState();
const [prevQuery, setPrevQuery] = useState('');
const [prevResults, setPrevResults] = useState<{ label: string; value: string }[]>([]);
useEffect(() => { useEffect(() => {
if (structure) { if (structure) {
setEditData(structure); setEditData(structure);
@@ -46,34 +43,24 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
// Searching corporation owners via auto-complete // Searching corporation owners via auto-complete
const searchOwners = useCallback( const searchOwners = useCallback(
async (e: { query: string }) => { async (e: { query: string }) => {
const newQuery = e.query.trim(); const query = e.query.trim();
if (!newQuery) { if (!query) {
setOwnerSuggestions([]); setOwnerSuggestions([]);
return; return;
} }
// If user typed more text but we have partial match in prevResults
if (newQuery.startsWith(prevQuery) && prevResults.length > 0) {
const filtered = prevResults.filter(item => item.label.toLowerCase().includes(newQuery.toLowerCase()));
setOwnerSuggestions(filtered);
return;
}
try { try {
// TODO fix it
const { results = [] } = await outCommand({ const { results = [] } = await outCommand({
type: OutCommand.getCorporationNames, type: OutCommand.getCorporationNames,
data: { search: newQuery }, data: { search: query },
}); });
setOwnerSuggestions(results); setOwnerSuggestions(results);
setPrevQuery(newQuery);
setPrevResults(results);
} catch (err) { } catch (err) {
console.error('Failed to fetch owners:', err); console.error('Failed to fetch owners:', err);
setOwnerSuggestions([]); setOwnerSuggestions([]);
} }
}, },
[prevQuery, prevResults, outCommand], [outCommand],
); );
const handleChange = (field: keyof StructureItem, val: string | Date) => { const handleChange = (field: keyof StructureItem, val: string | Date) => {
@@ -122,7 +109,6 @@ export const SystemStructuresDialog: React.FC<StructuresEditDialogProps> = ({
// fetch corporation ticker if we have an ownerId // fetch corporation ticker if we have an ownerId
if (editData.ownerId) { if (editData.ownerId) {
try { try {
// TODO fix it
const { ticker } = await outCommand({ const { ticker } = await outCommand({
type: OutCommand.getCorporationTicker, type: OutCommand.getCorporationTicker,
data: { corp_id: editData.ownerId }, data: { corp_id: editData.ownerId },

View File

@@ -5,9 +5,36 @@ defmodule WandererApp.Map.Operations.Signatures do
require Logger require Logger
alias WandererApp.Map.Operations alias WandererApp.Map.Operations
alias WandererApp.Api.{MapSystem, MapSystemSignature} alias WandererApp.Api.{Character, MapSystem, MapSystemSignature}
alias WandererApp.Map.Server alias WandererApp.Map.Server
# Private helper to validate character_eve_id from params
# If character_eve_id is provided in params, validates it exists in the system
# If not provided, falls back to the owner's character ID
@spec validate_character_eve_id(map(), String.t()) ::
{:ok, String.t()} | {:error, :invalid_character}
defp validate_character_eve_id(params, fallback_char_id) do
case Map.get(params, "character_eve_id") do
nil ->
# No character_eve_id provided, use fallback (owner's character)
{:ok, fallback_char_id}
provided_char_id when is_binary(provided_char_id) ->
# Validate the provided character_eve_id exists
case Character.by_eve_id(provided_char_id) do
{:ok, _character} ->
{:ok, provided_char_id}
_ ->
{:error, :invalid_character}
end
_ ->
# Invalid format
{:error, :invalid_character}
end
end
@spec list_signatures(String.t()) :: [map()] @spec list_signatures(String.t()) :: [map()]
def list_signatures(map_id) do def list_signatures(map_id) do
systems = Operations.list_systems(map_id) systems = Operations.list_systems(map_id)
@@ -42,10 +69,11 @@ defmodule WandererApp.Map.Operations.Signatures do
) )
when is_integer(solar_system_id) do when is_integer(solar_system_id) do
# Convert solar_system_id to system_id for internal use # Convert solar_system_id to system_id for internal use
with {:ok, system} <- MapSystem.by_map_id_and_solar_system_id(map_id, solar_system_id) do with {:ok, system} <- MapSystem.by_map_id_and_solar_system_id(map_id, solar_system_id),
{:ok, validated_char_id} <- validate_character_eve_id(params, char_id) do
attrs = attrs =
params params
|> Map.put("character_eve_id", char_id) |> Map.put("character_eve_id", validated_char_id)
|> Map.put("system_id", system.id) |> Map.put("system_id", system.id)
|> Map.delete("solar_system_id") |> Map.delete("solar_system_id")
@@ -54,7 +82,7 @@ defmodule WandererApp.Map.Operations.Signatures do
updated_signatures: [], updated_signatures: [],
removed_signatures: [], removed_signatures: [],
solar_system_id: solar_system_id, solar_system_id: solar_system_id,
character_id: char_id, character_id: validated_char_id,
user_id: user_id, user_id: user_id,
delete_connection_with_sigs: false delete_connection_with_sigs: false
}) do }) do
@@ -86,6 +114,10 @@ defmodule WandererApp.Map.Operations.Signatures do
{:error, :unexpected_error} {:error, :unexpected_error}
end end
else else
{:error, :invalid_character} ->
Logger.error("[create_signature] Invalid character_eve_id provided")
{:error, :invalid_character}
_ -> _ ->
Logger.error( Logger.error(
"[create_signature] System not found for solar_system_id: #{solar_system_id}" "[create_signature] System not found for solar_system_id: #{solar_system_id}"
@@ -112,7 +144,8 @@ defmodule WandererApp.Map.Operations.Signatures do
params params
) do ) do
with {:ok, sig} <- MapSystemSignature.by_id(sig_id), with {:ok, sig} <- MapSystemSignature.by_id(sig_id),
{:ok, system} <- MapSystem.by_id(sig.system_id) do {:ok, system} <- MapSystem.by_id(sig.system_id),
{:ok, validated_char_id} <- validate_character_eve_id(params, char_id) do
base = %{ base = %{
"eve_id" => sig.eve_id, "eve_id" => sig.eve_id,
"name" => sig.name, "name" => sig.name,
@@ -120,7 +153,7 @@ defmodule WandererApp.Map.Operations.Signatures do
"group" => sig.group, "group" => sig.group,
"type" => sig.type, "type" => sig.type,
"custom_info" => sig.custom_info, "custom_info" => sig.custom_info,
"character_eve_id" => char_id, "character_eve_id" => validated_char_id,
"description" => sig.description, "description" => sig.description,
"linked_system_id" => sig.linked_system_id "linked_system_id" => sig.linked_system_id
} }
@@ -133,7 +166,7 @@ defmodule WandererApp.Map.Operations.Signatures do
updated_signatures: [attrs], updated_signatures: [attrs],
removed_signatures: [], removed_signatures: [],
solar_system_id: system.solar_system_id, solar_system_id: system.solar_system_id,
character_id: char_id, character_id: validated_char_id,
user_id: user_id, user_id: user_id,
delete_connection_with_sigs: false delete_connection_with_sigs: false
}) })
@@ -151,6 +184,10 @@ defmodule WandererApp.Map.Operations.Signatures do
_ -> {:ok, attrs} _ -> {:ok, attrs}
end end
else else
{:error, :invalid_character} ->
Logger.error("[update_signature] Invalid character_eve_id provided")
{:error, :invalid_character}
err -> err ->
Logger.error("[update_signature] Unexpected error: #{inspect(err)}") Logger.error("[update_signature] Unexpected error: #{inspect(err)}")
{:error, :unexpected_error} {:error, :unexpected_error}

View File

@@ -441,18 +441,19 @@ defmodule WandererAppWeb.MapSystemAPIController do
) )
def show(%{assigns: %{map_id: map_id}} = conn, %{"id" => id}) do def show(%{assigns: %{map_id: map_id}} = conn, %{"id" => id}) do
with {:ok, system_uuid} <- APIUtils.validate_uuid(id), # Look up by solar_system_id (EVE Online integer ID)
{:ok, system} <- WandererApp.Api.MapSystem.by_id(system_uuid) do case APIUtils.parse_int(id) do
# Verify the system belongs to the requested map {:ok, solar_system_id} ->
if system.map_id == map_id do case Operations.get_system(map_id, solar_system_id) do
{:ok, system} ->
APIUtils.respond_data(conn, APIUtils.map_system_to_json(system)) APIUtils.respond_data(conn, APIUtils.map_system_to_json(system))
else
{:error, :not_found} ->
{:error, :not_found} {:error, :not_found}
end end
else
{:error, %Ash.Error.Query.NotFound{}} -> {:error, :not_found} {:error, _} ->
{:error, _} -> {:error, :not_found} {:error, :not_found}
error -> error
end end
end end
@@ -472,8 +473,22 @@ defmodule WandererAppWeb.MapSystemAPIController do
) )
def create(conn, params) do def create(conn, params) do
systems = Map.get(params, "systems", []) # Support both batch format {"systems": [...], "connections": [...]}
connections = Map.get(params, "connections", []) # and single system format {"solar_system_id": ..., ...}
{systems, connections} =
cond do
Map.has_key?(params, "systems") ->
# Batch format
{Map.get(params, "systems", []), Map.get(params, "connections", [])}
Map.has_key?(params, "solar_system_id") or Map.has_key?(params, :solar_system_id) ->
# Single system format - wrap it in an array
{[params], []}
true ->
# Empty request
{[], []}
end
case Operations.upsert_systems_and_connections(conn, systems, connections) do case Operations.upsert_systems_and_connections(conn, systems, connections) do
{:ok, result} -> {:ok, result} ->

View File

@@ -595,7 +595,7 @@ defmodule WandererAppWeb.Router do
# while maintaining 100% backward compatibility with existing /api/* routes # while maintaining 100% backward compatibility with existing /api/* routes
# #
scope "/api/v1" do scope "/api/v1" do
pipe_through :api_v1 pipe_through [:api_v1, :api_map]
# Custom combined endpoints # Custom combined endpoints
get "/maps/:map_id/systems_and_connections", get "/maps/:map_id/systems_and_connections",