fix: close failed location sessions and harden upgrade rehearsal
This commit is contained in:
parent
2159e6cda7
commit
f0512fab84
|
|
@ -1630,12 +1630,16 @@ export const Hooks = {
|
||||||
if (typeof window.WhoNeedHelpAndroid?.postMessage === "function") {
|
if (typeof window.WhoNeedHelpAndroid?.postMessage === "function") {
|
||||||
this.nativeError = () => {
|
this.nativeError = () => {
|
||||||
this.pushEvent("location-error", {})
|
this.pushEvent("location-error", {})
|
||||||
this.pushEvent("stop-tracking", {})
|
|
||||||
}
|
}
|
||||||
window.addEventListener("wnh:native-tracking-error", this.nativeError)
|
window.addEventListener("wnh:native-tracking-error", this.nativeError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!navigator.geolocation) {
|
||||||
|
this.pushEvent("location-error", {})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.watchId = navigator.geolocation.watchPosition(
|
this.watchId = navigator.geolocation.watchPosition(
|
||||||
position => this.queueLocation({
|
position => this.queueLocation({
|
||||||
latitude: position.coords.latitude,
|
latitude: position.coords.latitude,
|
||||||
|
|
|
||||||
|
|
@ -288,18 +288,19 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("location-error", _, socket) do
|
def handle_event("location-error", _, socket) do
|
||||||
|
socket =
|
||||||
|
case stop_tracking(socket) do
|
||||||
|
{:ok, stopped_socket} -> stopped_socket
|
||||||
|
{:error, _reason} -> socket
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, put_flash(socket, :error, gettext("The browser could not share your location."))}
|
{:noreply, put_flash(socket, :error, gettext("The browser could not share your location."))}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("stop-tracking", _, socket) do
|
def handle_event("stop-tracking", _, socket) do
|
||||||
case Tracking.stop_session(socket.assigns.current_scope, socket.assigns.assignment) do
|
case stop_tracking(socket) do
|
||||||
{:ok, _} ->
|
{:ok, stopped_socket} ->
|
||||||
{:noreply,
|
{:noreply, stopped_socket}
|
||||||
socket
|
|
||||||
|> assign(:tracking_active, false)
|
|
||||||
|> assign(:tracking_session_id, nil)
|
|
||||||
|> maybe_untrack_browser_presence()
|
|
||||||
|> maybe_stop_native_tracking()}
|
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:noreply, put_flash(socket, :error, message(reason))}
|
{:noreply, put_flash(socket, :error, message(reason))}
|
||||||
|
|
@ -470,6 +471,21 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
||||||
|
|
||||||
defp maybe_untrack_browser_presence(socket), do: socket
|
defp maybe_untrack_browser_presence(socket), do: socket
|
||||||
|
|
||||||
|
defp stop_tracking(socket) do
|
||||||
|
case Tracking.stop_session(socket.assigns.current_scope, socket.assigns.assignment) do
|
||||||
|
{:ok, _} ->
|
||||||
|
{:ok,
|
||||||
|
socket
|
||||||
|
|> assign(:tracking_active, false)
|
||||||
|
|> assign(:tracking_session_id, nil)
|
||||||
|
|> maybe_untrack_browser_presence()
|
||||||
|
|> maybe_stop_native_tracking()}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:error, reason}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp transition(socket, fun, success) do
|
defp transition(socket, fun, success) do
|
||||||
case fun.(socket.assigns.current_scope, socket.assigns.assignment.id) do
|
case fun.(socket.assigns.current_scope, socket.assigns.assignment.id) do
|
||||||
{:ok, _} -> {:noreply, put_flash(socket, :info, success)}
|
{:ok, _} -> {:noreply, put_flash(socket, :info, success)}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,15 @@ rehearsal_map_tile_url=$(
|
||||||
'. "$1"; printf %s "${MAP_TILE_URL:-https://tile.openstreetmap.org/{z}/{x}/{y}.png}"' \
|
'. "$1"; printf %s "${MAP_TILE_URL:-https://tile.openstreetmap.org/{z}/{x}/{y}.png}"' \
|
||||||
sh "$PUBLIC_ENV_FILE"
|
sh "$PUBLIC_ENV_FILE"
|
||||||
)
|
)
|
||||||
|
rehearsal_postgis_image=$(
|
||||||
|
sh -c '. "$1"; printf %s "${POSTGIS_IMAGE:?POSTGIS_IMAGE is missing from .env}"' \
|
||||||
|
sh "$PUBLIC_ENV_FILE"
|
||||||
|
)
|
||||||
|
rehearsal_socket_proxy_image=$(
|
||||||
|
sh -c \
|
||||||
|
'. "$1"; printf %s "${SOCKET_PROXY_IMAGE:?SOCKET_PROXY_IMAGE is missing from .env}"' \
|
||||||
|
sh "$PUBLIC_ENV_FILE"
|
||||||
|
)
|
||||||
|
|
||||||
set -a
|
set -a
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
|
|
@ -112,11 +121,17 @@ export PHX_HOST="$rehearsal_phx_host"
|
||||||
export PHX_SCHEME="$rehearsal_phx_scheme"
|
export PHX_SCHEME="$rehearsal_phx_scheme"
|
||||||
export PHX_URL_PORT="$rehearsal_phx_url_port"
|
export PHX_URL_PORT="$rehearsal_phx_url_port"
|
||||||
export MAP_TILE_URL="$rehearsal_map_tile_url"
|
export MAP_TILE_URL="$rehearsal_map_tile_url"
|
||||||
|
export POSTGIS_IMAGE="$rehearsal_postgis_image"
|
||||||
|
export SOCKET_PROXY_IMAGE="$rehearsal_socket_proxy_image"
|
||||||
export TRAEFIK_TRUSTED_IPS=127.0.0.1/32
|
export TRAEFIK_TRUSTED_IPS=127.0.0.1/32
|
||||||
export TRAEFIK_PROJECT_CONSTRAINT="$project"
|
export TRAEFIK_PROJECT_CONSTRAINT="$project"
|
||||||
export TRAEFIK_APP_NAME="wnh-upgrade-rehearsal-$compact_id"
|
export TRAEFIK_APP_NAME="wnh-upgrade-rehearsal-$compact_id"
|
||||||
export TRAEFIK_DOCKER_NETWORK="${project}_ingress"
|
export TRAEFIK_DOCKER_NETWORK="${project}_ingress"
|
||||||
export REHEARSAL_IMAGE="who-need-help:upgrade-rehearsal-$run_id"
|
export REHEARSAL_IMAGE="who-need-help:upgrade-rehearsal-$run_id"
|
||||||
|
# compose.yaml validates APP_IMAGE before applying the rehearsal override.
|
||||||
|
# Point both variables at the same unique image so config validation, the
|
||||||
|
# build, and cleanup all address only this rehearsal run.
|
||||||
|
export APP_IMAGE="$REHEARSAL_IMAGE"
|
||||||
|
|
||||||
compose() {
|
compose() {
|
||||||
docker compose \
|
docker compose \
|
||||||
|
|
@ -173,9 +188,9 @@ cleanup() {
|
||||||
}
|
}
|
||||||
trap cleanup EXIT HUP INT TERM
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
|
||||||
database_snapshot() {
|
database_table_list() {
|
||||||
target=$1
|
target=$1
|
||||||
tables=$(
|
|
||||||
compose exec -T db \
|
compose exec -T db \
|
||||||
psql --username "$POSTGRES_USER" --dbname "$rehearsal_db" \
|
psql --username "$POSTGRES_USER" --dbname "$rehearsal_db" \
|
||||||
--tuples-only --no-align \
|
--tuples-only --no-align \
|
||||||
|
|
@ -185,12 +200,17 @@ database_snapshot() {
|
||||||
WHERE schemaname = 'public'
|
WHERE schemaname = 'public'
|
||||||
AND tablename NOT IN ('oban_jobs', 'schema_migrations', 'spatial_ref_sys')
|
AND tablename NOT IN ('oban_jobs', 'schema_migrations', 'spatial_ref_sys')
|
||||||
ORDER BY tablename;
|
ORDER BY tablename;
|
||||||
"
|
" >"$target"
|
||||||
)
|
}
|
||||||
|
|
||||||
|
database_snapshot() {
|
||||||
|
target=$1
|
||||||
|
table_list=$2
|
||||||
|
|
||||||
: >"$target"
|
: >"$target"
|
||||||
|
|
||||||
for table in $tables; do
|
while IFS= read -r table; do
|
||||||
|
[ -n "$table" ] || continue
|
||||||
row_count=$(
|
row_count=$(
|
||||||
compose exec -T db \
|
compose exec -T db \
|
||||||
psql --username "$POSTGRES_USER" --dbname "$rehearsal_db" \
|
psql --username "$POSTGRES_USER" --dbname "$rehearsal_db" \
|
||||||
|
|
@ -198,7 +218,7 @@ database_snapshot() {
|
||||||
--command "SELECT count(*) FROM \"$table\";"
|
--command "SELECT count(*) FROM \"$table\";"
|
||||||
)
|
)
|
||||||
printf '%s %s\n' "$table" "$row_count" >>"$target"
|
printf '%s %s\n' "$table" "$row_count" >>"$target"
|
||||||
done
|
done <"$table_list"
|
||||||
}
|
}
|
||||||
|
|
||||||
compose config --quiet
|
compose config --quiet
|
||||||
|
|
@ -221,7 +241,10 @@ compose exec -T db \
|
||||||
|
|
||||||
compose exec -T db pg_restore --list <"$dump" >"$output_dir/pg-restore-list.txt"
|
compose exec -T db pg_restore --list <"$dump" >"$output_dir/pg-restore-list.txt"
|
||||||
|
|
||||||
database_snapshot "$output_dir/database-before.txt"
|
database_table_list "$output_dir/database-tables-before.txt"
|
||||||
|
database_snapshot \
|
||||||
|
"$output_dir/database-before.txt" \
|
||||||
|
"$output_dir/database-tables-before.txt"
|
||||||
compose exec -T db \
|
compose exec -T db \
|
||||||
psql --username "$POSTGRES_USER" --dbname "$rehearsal_db" \
|
psql --username "$POSTGRES_USER" --dbname "$rehearsal_db" \
|
||||||
--tuples-only --no-align \
|
--tuples-only --no-align \
|
||||||
|
|
@ -230,7 +253,9 @@ compose exec -T db \
|
||||||
|
|
||||||
compose up --detach --wait mailpit migrate proxy web worker
|
compose up --detach --wait mailpit migrate proxy web worker
|
||||||
|
|
||||||
database_snapshot "$output_dir/database-after.txt"
|
database_snapshot \
|
||||||
|
"$output_dir/database-after.txt" \
|
||||||
|
"$output_dir/database-tables-before.txt"
|
||||||
diff -u "$output_dir/database-before.txt" "$output_dir/database-after.txt" \
|
diff -u "$output_dir/database-before.txt" "$output_dir/database-after.txt" \
|
||||||
>"$output_dir/database-diff.txt"
|
>"$output_dir/database-diff.txt"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -924,6 +924,59 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
||||||
stop_live_view(helper_view)
|
stop_live_view(helper_view)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "browser location errors stop the empty tracking session", _context do
|
||||||
|
category = Catalog.seed_defaults()
|
||||||
|
requester = user_fixture(display_name: "Location requester")
|
||||||
|
helper = user_fixture(display_name: "Location helper")
|
||||||
|
|
||||||
|
{:ok, request} =
|
||||||
|
Help.create_request(user_scope_fixture(requester), request_attrs(category))
|
||||||
|
|
||||||
|
{:ok, assignment} = Help.accept_request(user_scope_fixture(helper), request.id)
|
||||||
|
|
||||||
|
helper_conn =
|
||||||
|
build_conn()
|
||||||
|
|> log_in_user(helper)
|
||||||
|
|
||||||
|
{:ok, helper_view, _html} = live(helper_conn, ~p"/requests/#{request.id}")
|
||||||
|
|
||||||
|
helper_view
|
||||||
|
|> element("button[phx-click='start-tracking']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert Repo.get_by!(WhoNeedHelp.Tracking.TrackingSession,
|
||||||
|
assignment_id: assignment.id,
|
||||||
|
user_id: helper.id,
|
||||||
|
active: true
|
||||||
|
)
|
||||||
|
|
||||||
|
html = render_hook(helper_view, "location-error", %{})
|
||||||
|
|
||||||
|
assert html =~ "The browser could not share your location."
|
||||||
|
assert has_element?(helper_view, "button[phx-click='start-tracking']")
|
||||||
|
refute has_element?(helper_view, "button[phx-click='stop-tracking']")
|
||||||
|
|
||||||
|
refute Repo.get_by(WhoNeedHelp.Tracking.TrackingSession,
|
||||||
|
assignment_id: assignment.id,
|
||||||
|
user_id: helper.id,
|
||||||
|
active: true
|
||||||
|
)
|
||||||
|
|
||||||
|
stopped_session =
|
||||||
|
Repo.get_by!(WhoNeedHelp.Tracking.TrackingSession,
|
||||||
|
assignment_id: assignment.id,
|
||||||
|
user_id: helper.id
|
||||||
|
)
|
||||||
|
|
||||||
|
assert stopped_session.ended_at
|
||||||
|
assert stopped_session.sample_count == 0
|
||||||
|
refute Repo.get_by(WhoNeedHelp.Tracking.Position,
|
||||||
|
tracking_session_id: stopped_session.id
|
||||||
|
)
|
||||||
|
|
||||||
|
stop_live_view(helper_view)
|
||||||
|
end
|
||||||
|
|
||||||
test "regular users cannot enter moderation", %{conn: conn} do
|
test "regular users cannot enter moderation", %{conn: conn} do
|
||||||
assert {:error, {:redirect, %{to: "/requests"}}} = live(conn, ~p"/moderation")
|
assert {:error, {:redirect, %{to: "/requests"}}} = live(conn, ~p"/moderation")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user