diff --git a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresDialog/SystemStructuresDialog.tsx b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresDialog/SystemStructuresDialog.tsx index 597ba637..f39c41cc 100644 --- a/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresDialog/SystemStructuresDialog.tsx +++ b/assets/js/hooks/Mapper/components/mapInterface/widgets/SystemStructures/SystemStructuresDialog/SystemStructuresDialog.tsx @@ -30,9 +30,6 @@ export const SystemStructuresDialog: React.FC = ({ const { outCommand } = useMapRootState(); - const [prevQuery, setPrevQuery] = useState(''); - const [prevResults, setPrevResults] = useState<{ label: string; value: string }[]>([]); - useEffect(() => { if (structure) { setEditData(structure); @@ -46,34 +43,24 @@ export const SystemStructuresDialog: React.FC = ({ // Searching corporation owners via auto-complete const searchOwners = useCallback( async (e: { query: string }) => { - const newQuery = e.query.trim(); - if (!newQuery) { + const query = e.query.trim(); + if (!query) { setOwnerSuggestions([]); 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 { - // TODO fix it const { results = [] } = await outCommand({ type: OutCommand.getCorporationNames, - data: { search: newQuery }, + data: { search: query }, }); setOwnerSuggestions(results); - setPrevQuery(newQuery); - setPrevResults(results); } catch (err) { console.error('Failed to fetch owners:', err); setOwnerSuggestions([]); } }, - [prevQuery, prevResults, outCommand], + [outCommand], ); const handleChange = (field: keyof StructureItem, val: string | Date) => { @@ -122,7 +109,6 @@ export const SystemStructuresDialog: React.FC = ({ // fetch corporation ticker if we have an ownerId if (editData.ownerId) { try { - // TODO fix it const { ticker } = await outCommand({ type: OutCommand.getCorporationTicker, data: { corp_id: editData.ownerId }, diff --git a/lib/wanderer_app/map/operations/signatures.ex b/lib/wanderer_app/map/operations/signatures.ex index 1909177f..7fdbcd30 100644 --- a/lib/wanderer_app/map/operations/signatures.ex +++ b/lib/wanderer_app/map/operations/signatures.ex @@ -5,9 +5,36 @@ defmodule WandererApp.Map.Operations.Signatures do require Logger alias WandererApp.Map.Operations - alias WandererApp.Api.{MapSystem, MapSystemSignature} + alias WandererApp.Api.{Character, MapSystem, MapSystemSignature} 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()] def list_signatures(map_id) do systems = Operations.list_systems(map_id) @@ -42,10 +69,11 @@ defmodule WandererApp.Map.Operations.Signatures do ) when is_integer(solar_system_id) do # 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 = params - |> Map.put("character_eve_id", char_id) + |> Map.put("character_eve_id", validated_char_id) |> Map.put("system_id", system.id) |> Map.delete("solar_system_id") @@ -54,7 +82,7 @@ defmodule WandererApp.Map.Operations.Signatures do updated_signatures: [], removed_signatures: [], solar_system_id: solar_system_id, - character_id: char_id, + character_id: validated_char_id, user_id: user_id, delete_connection_with_sigs: false }) do @@ -86,6 +114,10 @@ defmodule WandererApp.Map.Operations.Signatures do {:error, :unexpected_error} end else + {:error, :invalid_character} -> + Logger.error("[create_signature] Invalid character_eve_id provided") + {:error, :invalid_character} + _ -> Logger.error( "[create_signature] System not found for solar_system_id: #{solar_system_id}" @@ -112,7 +144,8 @@ defmodule WandererApp.Map.Operations.Signatures do params ) do 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 = %{ "eve_id" => sig.eve_id, "name" => sig.name, @@ -120,7 +153,7 @@ defmodule WandererApp.Map.Operations.Signatures do "group" => sig.group, "type" => sig.type, "custom_info" => sig.custom_info, - "character_eve_id" => char_id, + "character_eve_id" => validated_char_id, "description" => sig.description, "linked_system_id" => sig.linked_system_id } @@ -133,7 +166,7 @@ defmodule WandererApp.Map.Operations.Signatures do updated_signatures: [attrs], removed_signatures: [], solar_system_id: system.solar_system_id, - character_id: char_id, + character_id: validated_char_id, user_id: user_id, delete_connection_with_sigs: false }) @@ -151,6 +184,10 @@ defmodule WandererApp.Map.Operations.Signatures do _ -> {:ok, attrs} end else + {:error, :invalid_character} -> + Logger.error("[update_signature] Invalid character_eve_id provided") + {:error, :invalid_character} + err -> Logger.error("[update_signature] Unexpected error: #{inspect(err)}") {:error, :unexpected_error} diff --git a/lib/wanderer_app_web/controllers/map_system_api_controller.ex b/lib/wanderer_app_web/controllers/map_system_api_controller.ex index e184c50b..94f53284 100644 --- a/lib/wanderer_app_web/controllers/map_system_api_controller.ex +++ b/lib/wanderer_app_web/controllers/map_system_api_controller.ex @@ -441,18 +441,19 @@ defmodule WandererAppWeb.MapSystemAPIController do ) def show(%{assigns: %{map_id: map_id}} = conn, %{"id" => id}) do - with {:ok, system_uuid} <- APIUtils.validate_uuid(id), - {:ok, system} <- WandererApp.Api.MapSystem.by_id(system_uuid) do - # Verify the system belongs to the requested map - if system.map_id == map_id do - APIUtils.respond_data(conn, APIUtils.map_system_to_json(system)) - else + # Look up by solar_system_id (EVE Online integer ID) + case APIUtils.parse_int(id) do + {:ok, solar_system_id} -> + case Operations.get_system(map_id, solar_system_id) do + {:ok, system} -> + APIUtils.respond_data(conn, APIUtils.map_system_to_json(system)) + + {:error, :not_found} -> + {:error, :not_found} + end + + {:error, _} -> {:error, :not_found} - end - else - {:error, %Ash.Error.Query.NotFound{}} -> {:error, :not_found} - {:error, _} -> {:error, :not_found} - error -> error end end @@ -472,8 +473,22 @@ defmodule WandererAppWeb.MapSystemAPIController do ) def create(conn, params) do - systems = Map.get(params, "systems", []) - connections = Map.get(params, "connections", []) + # Support both batch format {"systems": [...], "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 {:ok, result} -> diff --git a/lib/wanderer_app_web/router.ex b/lib/wanderer_app_web/router.ex index 3ca6c05f..4fab1eb4 100644 --- a/lib/wanderer_app_web/router.ex +++ b/lib/wanderer_app_web/router.ex @@ -595,7 +595,7 @@ defmodule WandererAppWeb.Router do # while maintaining 100% backward compatibility with existing /api/* routes # scope "/api/v1" do - pipe_through :api_v1 + pipe_through [:api_v1, :api_map] # Custom combined endpoints get "/maps/:map_id/systems_and_connections",