Add public search discovery metadata
This commit is contained in:
parent
5990a1935e
commit
d8177f38bd
|
|
@ -1,4 +1,5 @@
|
||||||
x-app-environment: &app-environment
|
x-app-environment: &app-environment
|
||||||
|
DEPLOYMENT_ENV: ${DEPLOYMENT_ENV:?Set DEPLOYMENT_ENV in .env}
|
||||||
APP_ROLE: web
|
APP_ROLE: web
|
||||||
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL in .env}
|
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL in .env}
|
||||||
DATABASE_SOCKET_DIR: ${DATABASE_SOCKET_DIR:-}
|
DATABASE_SOCKET_DIR: ${DATABASE_SOCKET_DIR:-}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ config :who_need_help, :scopes,
|
||||||
]
|
]
|
||||||
|
|
||||||
config :who_need_help,
|
config :who_need_help,
|
||||||
|
deployment_env: :development,
|
||||||
ecto_repos: [WhoNeedHelp.Repo],
|
ecto_repos: [WhoNeedHelp.Repo],
|
||||||
generators: [timestamp_type: :utc_datetime, binary_id: true],
|
generators: [timestamp_type: :utc_datetime, binary_id: true],
|
||||||
app_role: :web,
|
app_role: :web,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
|
deployment_env =
|
||||||
|
case System.get_env("DEPLOYMENT_ENV", "development") do
|
||||||
|
"development" ->
|
||||||
|
:development
|
||||||
|
|
||||||
|
"test" ->
|
||||||
|
:test
|
||||||
|
|
||||||
|
"production" ->
|
||||||
|
:production
|
||||||
|
|
||||||
|
other ->
|
||||||
|
raise "DEPLOYMENT_ENV must be development, test, or production; got #{inspect(other)}"
|
||||||
|
end
|
||||||
|
|
||||||
app_role =
|
app_role =
|
||||||
case System.get_env("APP_ROLE", "web") do
|
case System.get_env("APP_ROLE", "web") do
|
||||||
"web" -> :web
|
"web" -> :web
|
||||||
|
|
@ -46,6 +61,7 @@ rate_limit_policies =
|
||||||
end
|
end
|
||||||
|
|
||||||
config :who_need_help,
|
config :who_need_help,
|
||||||
|
deployment_env: deployment_env,
|
||||||
app_role: app_role,
|
app_role: app_role,
|
||||||
codex_session_id: System.get_env("CODEX_SESSION_ID", "not-configured"),
|
codex_session_id: System.get_env("CODEX_SESSION_ID", "not-configured"),
|
||||||
map_tile_url:
|
map_tile_url:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :who_need_help, :handover_secret, "isolated-test-handover-secret"
|
config :who_need_help, :handover_secret, "isolated-test-handover-secret"
|
||||||
|
config :who_need_help, :deployment_env, :test
|
||||||
config :who_need_help, :tracking_presence_cleanup_grace_ms, 100
|
config :who_need_help, :tracking_presence_cleanup_grace_ms, 100
|
||||||
# Unit tests opt in to individual policies inside the relevant test. This keeps
|
# Unit tests opt in to individual policies inside the relevant test. This keeps
|
||||||
# unrelated examples independent from shared counters and mirrors the explicit
|
# unrelated examples independent from shared counters and mirrors the explicit
|
||||||
|
|
|
||||||
|
|
@ -2322,3 +2322,33 @@ promoted.
|
||||||
`cf7bacdf61ffb171ebac85e32dfcf12bc6972d59`; its application, database, and
|
`cf7bacdf61ffb171ebac85e32dfcf12bc6972d59`; its application, database, and
|
||||||
Mailpit remained healthy, and public readiness remained `ready`. The remote
|
Mailpit remained healthy, and public readiness remained `ready`. The remote
|
||||||
repository and hackathon-test deployment were not mutated.
|
repository and hackathon-test deployment were not mutated.
|
||||||
|
|
||||||
|
# 2026-08-09 local public search-discovery verification
|
||||||
|
|
||||||
|
- The public home, Privacy Policy, Safety rules, and Terms of Service pages now
|
||||||
|
expose locale-aware canonical and reciprocal `en`/`uk`/`ru`/`x-default`
|
||||||
|
alternate links. Every other route defaults to `noindex, nofollow`, so
|
||||||
|
authenticated, support, legal, moderation, request, activity, profile, and
|
||||||
|
staff surfaces are not presented as public search results.
|
||||||
|
- `/sitemap.xml` contains exactly twelve absolute entries: the four public
|
||||||
|
pages in each of the three supported locales. It contains no account,
|
||||||
|
request, activity, support, legal, moderation, notification, or staff URL.
|
||||||
|
- `/robots.txt` is environment-aware. Production permits the public pages,
|
||||||
|
lists the private route families as disallowed, and advertises the absolute
|
||||||
|
sitemap URL. Development and test configurations return `Disallow: /`.
|
||||||
|
- Nineteen focused controller tests passed against an isolated temporary
|
||||||
|
PostgreSQL database. The complete isolated quality/security gate then passed
|
||||||
|
455 ExUnit tests, all fourteen browser map-clustering tests, and every
|
||||||
|
configured compiler, formatting, xref, Credo, Sobelow, Dialyzer,
|
||||||
|
dependency, image, Compose, Helm, migration, rollback, and observability
|
||||||
|
check. The Debian 13.6 runtime-image scan reported zero detected
|
||||||
|
vulnerabilities.
|
||||||
|
- The isolated quality unit
|
||||||
|
`codex-heavy-wnh-quality-seo-rerun-20260809-20260809-082004-916736.service`
|
||||||
|
exited with status `0` after 4 minutes 47 seconds and reported a 210.9 MiB
|
||||||
|
memory peak. Its run-scoped images, containers, networks, and volumes were
|
||||||
|
removed. Two older quality database volumes remain referenced by their own
|
||||||
|
stopped containers and were not changed by this verification.
|
||||||
|
- This verification changed only the local checkout. The public Git remote,
|
||||||
|
production deployment, and frozen hackathon test deployment were not
|
||||||
|
changed.
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ defmodule WhoNeedHelpWeb do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def static_paths,
|
def static_paths,
|
||||||
do: ~w(assets fonts images favicon.ico manifest.webmanifest offline.html robots.txt sw.js)
|
do: ~w(assets fonts images favicon.ico manifest.webmanifest offline.html sw.js)
|
||||||
|
|
||||||
def router do
|
def router do
|
||||||
quote do
|
quote do
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
<meta name="csrf-token" content={get_csrf_token()} />
|
<meta name="csrf-token" content={get_csrf_token()} />
|
||||||
<meta name="theme-color" content="#047b68" />
|
<meta name="theme-color" content="#047b68" />
|
||||||
<meta name="color-scheme" content="light dark" />
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
<meta name="robots" content={assigns[:page_robots] || "noindex, nofollow"} />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content={
|
content={
|
||||||
|
|
@ -25,6 +26,12 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<%= if canonical_url = assigns[:canonical_url] do %>
|
||||||
|
<link rel="canonical" href={canonical_url} />
|
||||||
|
<% end %>
|
||||||
|
<%= for {language, url} <- assigns[:language_alternates] || [] do %>
|
||||||
|
<link rel="alternate" hreflang={language} href={url} />
|
||||||
|
<% end %>
|
||||||
<.live_title
|
<.live_title
|
||||||
default="Who Need Help"
|
default="Who Need Help"
|
||||||
suffix={
|
suffix={
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,60 @@
|
||||||
defmodule WhoNeedHelpWeb.PageController do
|
defmodule WhoNeedHelpWeb.PageController do
|
||||||
use WhoNeedHelpWeb, :controller
|
use WhoNeedHelpWeb, :controller
|
||||||
|
|
||||||
|
alias WhoNeedHelpWeb.SearchMetadata
|
||||||
|
|
||||||
def home(conn, _params) do
|
def home(conn, _params) do
|
||||||
render(conn, :home,
|
assigns =
|
||||||
page_description:
|
SearchMetadata.public_page_assigns("/")
|
||||||
|
|> Keyword.put(
|
||||||
|
:page_description,
|
||||||
gettext(
|
gettext(
|
||||||
"Ask nearby volunteers for free medicine pickup or safe practical help, coordinate privately, and verify the handover."
|
"Ask nearby volunteers for free medicine pickup or safe practical help, coordinate privately, and verify the handover."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
render(conn, :home, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def privacy(conn, _params) do
|
def privacy(conn, _params) do
|
||||||
render(conn, :privacy,
|
assigns =
|
||||||
page_title: gettext("Privacy Policy"),
|
SearchMetadata.public_page_assigns("/privacy")
|
||||||
page_description:
|
|> Keyword.put(:page_title, gettext("Privacy Policy"))
|
||||||
|
|> Keyword.put(
|
||||||
|
:page_description,
|
||||||
gettext(
|
gettext(
|
||||||
"How Who Need Help processes account, Google sign-in, location, communication, and safety data."
|
"How Who Need Help processes account, Google sign-in, location, communication, and safety data."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
render(conn, :privacy, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def safety(conn, _params) do
|
def safety(conn, _params) do
|
||||||
render(conn, :safety,
|
assigns =
|
||||||
page_title: gettext("Safety rules"),
|
SearchMetadata.public_page_assigns("/safety")
|
||||||
page_description:
|
|> Keyword.put(:page_title, gettext("Safety rules"))
|
||||||
|
|> Keyword.put(
|
||||||
|
:page_description,
|
||||||
gettext(
|
gettext(
|
||||||
"Safety guidance for voluntary help, medicine pickup, roadside requests, location sharing, and in-person activities."
|
"Safety guidance for voluntary help, medicine pickup, roadside requests, location sharing, and in-person activities."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
render(conn, :safety, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def terms(conn, _params) do
|
def terms(conn, _params) do
|
||||||
render(conn, :terms,
|
assigns =
|
||||||
page_title: gettext("Terms of Service"),
|
SearchMetadata.public_page_assigns("/terms")
|
||||||
page_description:
|
|> Keyword.put(:page_title, gettext("Terms of Service"))
|
||||||
|
|> Keyword.put(
|
||||||
|
:page_description,
|
||||||
gettext(
|
gettext(
|
||||||
"The conditions for using Who Need Help to coordinate voluntary help and social activities."
|
"The conditions for using Who Need Help to coordinate voluntary help and social activities."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
render(conn, :terms, assigns)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
defmodule WhoNeedHelpWeb.SearchDocumentController do
|
||||||
|
use WhoNeedHelpWeb, :controller
|
||||||
|
|
||||||
|
alias WhoNeedHelpWeb.SearchMetadata
|
||||||
|
|
||||||
|
@public_paths ["/", "/privacy", "/safety", "/terms"]
|
||||||
|
@locales ~w(en uk ru)
|
||||||
|
@private_prefixes ~w(
|
||||||
|
/account/
|
||||||
|
/activities
|
||||||
|
/admin
|
||||||
|
/analytics
|
||||||
|
/auth
|
||||||
|
/categories/proposals
|
||||||
|
/legal
|
||||||
|
/mobile
|
||||||
|
/moderation
|
||||||
|
/notifications
|
||||||
|
/people
|
||||||
|
/profile
|
||||||
|
/reports
|
||||||
|
/requests
|
||||||
|
/support
|
||||||
|
/users
|
||||||
|
)
|
||||||
|
|
||||||
|
def robots(conn, _params) do
|
||||||
|
body =
|
||||||
|
case Application.fetch_env!(:who_need_help, :deployment_env) do
|
||||||
|
:production -> production_robots()
|
||||||
|
_non_production -> "User-agent: *\nDisallow: /\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("text/plain", "utf-8")
|
||||||
|
|> put_resp_header("cache-control", "public, max-age=3600")
|
||||||
|
|> send_resp(:ok, body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sitemap(conn, _params) do
|
||||||
|
entries =
|
||||||
|
for path <- @public_paths,
|
||||||
|
locale <- @locales do
|
||||||
|
sitemap_entry(path, locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
body =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" <>
|
||||||
|
"<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " <>
|
||||||
|
"xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\n" <>
|
||||||
|
Enum.join(entries, "") <>
|
||||||
|
"</urlset>\n"
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_resp_content_type("application/xml", "utf-8")
|
||||||
|
|> put_resp_header("cache-control", "public, max-age=3600")
|
||||||
|
|> send_resp(:ok, body)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp production_robots do
|
||||||
|
disallowed = Enum.map_join(@private_prefixes, "", &"Disallow: #{&1}\n")
|
||||||
|
|
||||||
|
"User-agent: *\nAllow: /\n" <>
|
||||||
|
disallowed <>
|
||||||
|
"Sitemap: #{SearchMetadata.absolute_url("/sitemap.xml")}\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sitemap_entry(path, locale) do
|
||||||
|
alternates =
|
||||||
|
Enum.map_join(@locales, "", fn alternate_locale ->
|
||||||
|
~s( <xhtml:link rel="alternate" hreflang="#{alternate_locale}" href="#{xml_escape(SearchMetadata.localized_url(path, alternate_locale))}" />\n)
|
||||||
|
end) <>
|
||||||
|
~s( <xhtml:link rel="alternate" hreflang="x-default" href="#{xml_escape(SearchMetadata.absolute_url(path))}" />\n)
|
||||||
|
|
||||||
|
" <url>\n" <>
|
||||||
|
" <loc>#{xml_escape(SearchMetadata.localized_url(path, locale))}</loc>\n" <>
|
||||||
|
alternates <>
|
||||||
|
" </url>\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp xml_escape(value) do
|
||||||
|
value
|
||||||
|
|> String.replace("&", "&")
|
||||||
|
|> String.replace("<", "<")
|
||||||
|
|> String.replace(">", ">")
|
||||||
|
|> String.replace("\"", """)
|
||||||
|
|> String.replace("'", "'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -26,7 +26,7 @@ defmodule WhoNeedHelpWeb.Endpoint do
|
||||||
from: :who_need_help,
|
from: :who_need_help,
|
||||||
gzip: not code_reloading?,
|
gzip: not code_reloading?,
|
||||||
only: WhoNeedHelpWeb.static_paths(),
|
only: WhoNeedHelpWeb.static_paths(),
|
||||||
only_matching: ~w(favicon manifest offline robots sw),
|
only_matching: ~w(favicon manifest offline sw),
|
||||||
raise_on_missing_only: code_reloading?
|
raise_on_missing_only: code_reloading?
|
||||||
|
|
||||||
# Code reloading can be explicitly enabled under the
|
# Code reloading can be explicitly enabled under the
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ defmodule WhoNeedHelpWeb.Router do
|
||||||
scope "/", WhoNeedHelpWeb do
|
scope "/", WhoNeedHelpWeb do
|
||||||
pipe_through :browser
|
pipe_through :browser
|
||||||
|
|
||||||
|
get "/robots.txt", SearchDocumentController, :robots
|
||||||
|
get "/sitemap.xml", SearchDocumentController, :sitemap
|
||||||
get "/", PageController, :home
|
get "/", PageController, :home
|
||||||
get "/feedback", FeedbackController, :show
|
get "/feedback", FeedbackController, :show
|
||||||
get "/privacy", PageController, :privacy
|
get "/privacy", PageController, :privacy
|
||||||
|
|
|
||||||
29
lib/who_need_help_web/search_metadata.ex
Normal file
29
lib/who_need_help_web/search_metadata.ex
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
defmodule WhoNeedHelpWeb.SearchMetadata do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
@locales ~w(en uk ru)
|
||||||
|
|
||||||
|
def public_page_assigns(path) when is_binary(path) do
|
||||||
|
locale = Gettext.get_locale(WhoNeedHelpWeb.Gettext)
|
||||||
|
|
||||||
|
[
|
||||||
|
page_robots: "index, follow",
|
||||||
|
canonical_url: localized_url(path, locale),
|
||||||
|
language_alternates:
|
||||||
|
Enum.map(@locales, &{&1, localized_url(path, &1)}) ++
|
||||||
|
[{"x-default", absolute_url(path)}]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def localized_url(path, "en"), do: absolute_url(path)
|
||||||
|
|
||||||
|
def localized_url(path, locale) when locale in @locales do
|
||||||
|
absolute_url(path) <> "?" <> URI.encode_query(%{"locale" => locale})
|
||||||
|
end
|
||||||
|
|
||||||
|
def localized_url(path, _unknown_locale), do: absolute_url(path)
|
||||||
|
|
||||||
|
def absolute_url(path) do
|
||||||
|
String.trim_trailing(WhoNeedHelpWeb.Endpoint.url(), "/") <> path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1610,7 +1610,7 @@ msgid "Safety policy, category approval, account moderation, public launch, and
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/components/layouts.ex:191
|
#: lib/who_need_help_web/components/layouts.ex:191
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:25
|
#: lib/who_need_help_web/controllers/page_controller.ex:36
|
||||||
#: lib/who_need_help_web/controllers/page_html/safety.html.heex:6
|
#: lib/who_need_help_web/controllers/page_html/safety.html.heex:6
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Safety rules"
|
msgid "Safety rules"
|
||||||
|
|
@ -1685,7 +1685,7 @@ msgstr ""
|
||||||
msgid "Signal updated."
|
msgid "Signal updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/components/layouts/root.html.heex:48
|
#: lib/who_need_help_web/components/layouts/root.html.heex:55
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Skip to main content"
|
msgid "Skip to main content"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -3395,7 +3395,7 @@ msgid "Privacy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/controllers/google_auth_html/complete.html.heex:107
|
#: lib/who_need_help_web/controllers/google_auth_html/complete.html.heex:107
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:15
|
#: lib/who_need_help_web/controllers/page_controller.ex:22
|
||||||
#: lib/who_need_help_web/controllers/page_html/privacy.html.heex:4
|
#: lib/who_need_help_web/controllers/page_html/privacy.html.heex:4
|
||||||
#: lib/who_need_help_web/controllers/page_html/terms.html.heex:75
|
#: lib/who_need_help_web/controllers/page_html/terms.html.heex:75
|
||||||
#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:79
|
#: lib/who_need_help_web/controllers/user_registration_html/new.html.heex:79
|
||||||
|
|
@ -5442,7 +5442,7 @@ msgstr ""
|
||||||
msgid "Restore request"
|
msgid "Restore request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:27
|
#: lib/who_need_help_web/controllers/page_controller.ex:39
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Safety guidance for voluntary help, medicine pickup, roadside requests, location sharing, and in-person activities."
|
msgid "Safety guidance for voluntary help, medicine pickup, roadside requests, location sharing, and in-person activities."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -5926,7 +5926,7 @@ msgstr ""
|
||||||
msgid "Account settings control profile, location visibility, direct messages, and connected Google sign-in. Live tracking is optional and can be stopped. You may submit a"
|
msgid "Account settings control profile, location visibility, direct messages, and connected Google sign-in. Live tracking is optional and can be stopped. You may submit a"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:7
|
#: lib/who_need_help_web/controllers/page_controller.ex:11
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Ask nearby volunteers for free medicine pickup or safe practical help, coordinate privately, and verify the handover."
|
msgid "Ask nearby volunteers for free medicine pickup or safe practical help, coordinate privately, and verify the handover."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -6001,7 +6001,7 @@ msgstr ""
|
||||||
msgid "Hosting, database, backup, transactional-email, and map-tile providers process the minimum data needed to provide their component of the service. External social-profile and thank-you links are controlled by their own operators and policies."
|
msgid "Hosting, database, backup, transactional-email, and map-tile providers process the minimum data needed to provide their component of the service. External social-profile and thank-you links are controlled by their own operators and policies."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:17
|
#: lib/who_need_help_web/controllers/page_controller.ex:25
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "How Who Need Help processes account, Google sign-in, location, communication, and safety data."
|
msgid "How Who Need Help processes account, Google sign-in, location, communication, and safety data."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -6071,13 +6071,13 @@ msgstr ""
|
||||||
msgid "Some safety, fraud, support, completed-help, backup, or legal records may need to remain, as explained in the Privacy Policy. Updated terms will be published here with a new effective date; material changes should be reviewed before continuing to use the service."
|
msgid "Some safety, fraud, support, completed-help, backup, or legal records may need to remain, as explained in the Privacy Policy. Updated terms will be published here with a new effective date; material changes should be reviewed before continuing to use the service."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:35
|
#: lib/who_need_help_web/controllers/page_controller.ex:50
|
||||||
#: lib/who_need_help_web/controllers/page_html/terms.html.heex:4
|
#: lib/who_need_help_web/controllers/page_html/terms.html.heex:4
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Terms of Service"
|
msgid "Terms of Service"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/controllers/page_controller.ex:37
|
#: lib/who_need_help_web/controllers/page_controller.ex:53
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "The conditions for using Who Need Help to coordinate voluntary help and social activities."
|
msgid "The conditions for using Who Need Help to coordinate voluntary help and social activities."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -6432,7 +6432,7 @@ msgstr ""
|
||||||
msgid "Waiting for Android notification permission and token…"
|
msgid "Waiting for Android notification permission and token…"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/who_need_help_web/components/layouts/root.html.heex:23
|
#: lib/who_need_help_web/components/layouts/root.html.heex:24
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Who Need Help connects adults who need urgent local help with nearby volunteers."
|
msgid "Who Need Help connects adults who need urgent local help with nearby volunteers."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
|
||||||
#
|
|
||||||
# To ban all spiders from the entire site uncomment the next two lines:
|
|
||||||
# User-agent: *
|
|
||||||
# Disallow: /
|
|
||||||
|
|
@ -5,6 +5,7 @@ defmodule WhoNeedHelpWeb.PageControllerTest do
|
||||||
conn = get(conn, ~p"/")
|
conn = get(conn, ~p"/")
|
||||||
html = html_response(conn, 200)
|
html = html_response(conn, 200)
|
||||||
document = LazyHTML.from_document(html)
|
document = LazyHTML.from_document(html)
|
||||||
|
endpoint_url = WhoNeedHelpWeb.Endpoint.url()
|
||||||
[content_security_policy] = get_resp_header(conn, "content-security-policy")
|
[content_security_policy] = get_resp_header(conn, "content-security-policy")
|
||||||
|
|
||||||
assert html =~ "Need help nearby? Ask the community."
|
assert html =~ "Need help nearby? Ask the community."
|
||||||
|
|
@ -18,6 +19,13 @@ defmodule WhoNeedHelpWeb.PageControllerTest do
|
||||||
assert html =~ "Before you create a request"
|
assert html =~ "Before you create a request"
|
||||||
assert html =~ ~r/<title[^>]*>Who Need Help<\/title>/
|
assert html =~ ~r/<title[^>]*>Who Need Help<\/title>/
|
||||||
refute html =~ "Who Need Help · Who Need Help"
|
refute html =~ "Who Need Help · Who Need Help"
|
||||||
|
assert html =~ ~s(<meta name="robots" content="index, follow")
|
||||||
|
assert html =~ ~s(<link rel="canonical" href="#{endpoint_url}/")
|
||||||
|
|
||||||
|
for locale <- ~w(en uk ru x-default) do
|
||||||
|
assert html =~ ~s(rel="alternate" hreflang="#{locale}")
|
||||||
|
end
|
||||||
|
|
||||||
assert content_security_policy =~ "default-src 'self'"
|
assert content_security_policy =~ "default-src 'self'"
|
||||||
assert content_security_policy =~ "script-src 'self'"
|
assert content_security_policy =~ "script-src 'self'"
|
||||||
refute content_security_policy =~ "script-src 'self' 'unsafe-inline'"
|
refute content_security_policy =~ "script-src 'self' 'unsafe-inline'"
|
||||||
|
|
@ -98,6 +106,13 @@ defmodule WhoNeedHelpWeb.PageControllerTest do
|
||||||
) == 1
|
) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "private and authentication pages default to noindex", %{conn: conn} do
|
||||||
|
html = conn |> get(~p"/users/log-in") |> html_response(200)
|
||||||
|
|
||||||
|
assert html =~ ~s(<meta name="robots" content="noindex, nofollow")
|
||||||
|
refute html =~ ~s(<link rel="canonical")
|
||||||
|
end
|
||||||
|
|
||||||
test "HTTPS content security policy permits only secure WebSockets", %{conn: conn} do
|
test "HTTPS content security policy permits only secure WebSockets", %{conn: conn} do
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
defmodule WhoNeedHelpWeb.SearchDocumentControllerTest do
|
||||||
|
use WhoNeedHelpWeb.ConnCase, async: false
|
||||||
|
|
||||||
|
test "sitemap contains only absolute localized public URLs", %{conn: conn} do
|
||||||
|
conn = get(conn, ~p"/sitemap.xml")
|
||||||
|
body = response(conn, 200)
|
||||||
|
endpoint_url = WhoNeedHelpWeb.Endpoint.url()
|
||||||
|
|
||||||
|
assert get_resp_header(conn, "content-type") == ["application/xml; charset=utf-8"]
|
||||||
|
assert length(Regex.scan(~r/<url>/, body)) == 12
|
||||||
|
assert body =~ "<loc>#{endpoint_url}/</loc>"
|
||||||
|
assert body =~ "<loc>#{endpoint_url}/privacy?locale=uk</loc>"
|
||||||
|
assert body =~ "<loc>#{endpoint_url}/safety?locale=ru</loc>"
|
||||||
|
assert body =~ ~s(hreflang="x-default" href="#{endpoint_url}/terms")
|
||||||
|
refute body =~ "/users/"
|
||||||
|
refute body =~ "/requests"
|
||||||
|
refute body =~ "/support"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "non-production robots blocks crawling", %{conn: conn} do
|
||||||
|
conn = get(conn, ~p"/robots.txt")
|
||||||
|
|
||||||
|
assert response(conn, 200) == "User-agent: *\nDisallow: /\n"
|
||||||
|
assert get_resp_header(conn, "content-type") == ["text/plain; charset=utf-8"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "production robots advertises sitemap and avoids private route families", %{conn: conn} do
|
||||||
|
previous = Application.fetch_env!(:who_need_help, :deployment_env)
|
||||||
|
Application.put_env(:who_need_help, :deployment_env, :production)
|
||||||
|
on_exit(fn -> Application.put_env(:who_need_help, :deployment_env, previous) end)
|
||||||
|
|
||||||
|
body = conn |> get(~p"/robots.txt") |> response(200)
|
||||||
|
|
||||||
|
assert body =~ "Allow: /\n"
|
||||||
|
assert body =~ "Disallow: /admin\n"
|
||||||
|
assert body =~ "Disallow: /requests\n"
|
||||||
|
assert body =~ "Disallow: /support\n"
|
||||||
|
assert body =~ "Sitemap: #{WhoNeedHelpWeb.Endpoint.url()}/sitemap.xml\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue
Block a user