Merge commit '974d2109e2afbc066a2470ddea3f1c5e3ad44945' into redesign

This commit is contained in:
Luna D. 2024-06-06 22:11:43 +02:00
commit 44e99997e0
No known key found for this signature in database
GPG key ID: 4B1C63448394F688
25 changed files with 405 additions and 101 deletions

View file

@ -87,11 +87,12 @@ function pickAndResize(elem) {
} }
const muted = store.get('unmute_videos') ? '' : 'muted'; const muted = store.get('unmute_videos') ? '' : 'muted';
const autoplay = elem.classList.contains('hidden') ? '' : 'autoplay'; // Fix for spoilered image pages
if (imageFormat === 'mp4') { if (imageFormat === 'mp4') {
elem.classList.add('full-height'); elem.classList.add('full-height');
elem.insertAdjacentHTML('afterbegin', elem.insertAdjacentHTML('afterbegin',
`<video controls autoplay loop ${muted} playsinline preload="auto" id="image-display" `<video controls ${autoplay} loop ${muted} playsinline preload="auto" id="image-display"
width="${imageWidth}" height="${imageHeight}"> width="${imageWidth}" height="${imageHeight}">
<source src="${uris.webm}" type="video/webm"> <source src="${uris.webm}" type="video/webm">
<source src="${uris.mp4}" type="video/mp4"> <source src="${uris.mp4}" type="video/mp4">
@ -104,7 +105,7 @@ function pickAndResize(elem) {
} }
else if (imageFormat === 'webm') { else if (imageFormat === 'webm') {
elem.insertAdjacentHTML('afterbegin', elem.insertAdjacentHTML('afterbegin',
`<video controls autoplay loop ${muted} playsinline id="image-display"> `<video controls ${autoplay} loop ${muted} playsinline id="image-display">
<source src="${uri}" type="video/webm"> <source src="${uri}" type="video/webm">
<source src="${uri.replace(/webm$/, 'mp4')}" type="video/mp4"> <source src="${uri.replace(/webm$/, 'mp4')}" type="video/mp4">
<p class="block block--fixed block--warning"> <p class="block block--fixed block--warning">

View file

@ -369,6 +369,19 @@ describe('Image utils', () => {
expect(mockShowElement).toHaveClass(spoilerPendingClass); expect(mockShowElement).toHaveClass(spoilerPendingClass);
}); });
it('should play the video if it is present', () => {
const mockElement = document.createElement('div');
const { mockShowElement } = createImageShowElement(mockElement);
const mockVideo = document.createElement('video');
mockShowElement.appendChild(mockVideo);
const playSpy = vi.spyOn(mockVideo, 'play').mockReturnValue(Promise.resolve());
showBlock(mockElement);
expect(playSpy).toHaveBeenCalledTimes(1);
});
it('should not throw if image-filtered element is missing', () => { it('should not throw if image-filtered element is missing', () => {
const mockElement = document.createElement('div'); const mockElement = document.createElement('div');
createImageShowElement(mockElement); createImageShowElement(mockElement);

View file

@ -64,9 +64,15 @@ export function showThumb(img: HTMLDivElement) {
export function showBlock(img: HTMLDivElement) { export function showBlock(img: HTMLDivElement) {
img.querySelector('.image-filtered')?.classList.add('hidden'); img.querySelector('.image-filtered')?.classList.add('hidden');
const imageShowClasses = img.querySelector('.image-show')?.classList; const imageShowClasses = img.querySelector('.image-show')?.classList;
if (imageShowClasses) { if (imageShowClasses) {
imageShowClasses.remove('hidden'); imageShowClasses.remove('hidden');
imageShowClasses.add('spoiler-pending'); imageShowClasses.add('spoiler-pending');
const vidEl = img.querySelector('video');
if (vidEl) {
vidEl.play();
}
} }
} }

View file

@ -15,7 +15,7 @@ config :philomena,
anonymous_name_salt: System.fetch_env!("ANONYMOUS_NAME_SALT"), anonymous_name_salt: System.fetch_env!("ANONYMOUS_NAME_SALT"),
hcaptcha_secret_key: System.fetch_env!("HCAPTCHA_SECRET_KEY"), hcaptcha_secret_key: System.fetch_env!("HCAPTCHA_SECRET_KEY"),
hcaptcha_site_key: System.fetch_env!("HCAPTCHA_SITE_KEY"), hcaptcha_site_key: System.fetch_env!("HCAPTCHA_SITE_KEY"),
elasticsearch_url: System.get_env("ELASTICSEARCH_URL", "http://localhost:9200"), opensearch_url: System.get_env("OPENSEARCH_URL", "https://admin:admin@localhost:9200"),
advert_file_root: System.fetch_env!("ADVERT_FILE_ROOT"), advert_file_root: System.fetch_env!("ADVERT_FILE_ROOT"),
avatar_file_root: System.fetch_env!("AVATAR_FILE_ROOT"), avatar_file_root: System.fetch_env!("AVATAR_FILE_ROOT"),
badge_file_root: System.fetch_env!("BADGE_FILE_ROOT"), badge_file_root: System.fetch_env!("BADGE_FILE_ROOT"),
@ -90,6 +90,10 @@ config :philomena, :s3_secondary_options,
config :philomena, :s3_secondary_bucket, System.get_env("ALT_S3_BUCKET") config :philomena, :s3_secondary_bucket, System.get_env("ALT_S3_BUCKET")
# Don't bail on OpenSearch's self-signed certificate
config :elastix,
httpoison_options: [ssl: [verify: :verify_none]]
config :ex_aws, :hackney_opts, config :ex_aws, :hackney_opts,
timeout: 180_000, timeout: 180_000,
recv_timeout: 180_000, recv_timeout: 180_000,

View file

@ -1,7 +1,11 @@
version: '3' version: '3'
volumes: volumes:
postgres_data: {} postgres_data: {}
elastic_data: {} opensearch_data: {}
app_cargo_data: {}
app_build_data: {}
app_deps_data: {}
app_native_data: {}
services: services:
app: app:
@ -27,8 +31,8 @@ services:
- IMAGE_URL_ROOT=/img - IMAGE_URL_ROOT=/img
- BADGE_URL_ROOT=/badge-img - BADGE_URL_ROOT=/badge-img
- TAG_URL_ROOT=/tag-img - TAG_URL_ROOT=/tag-img
- ELASTICSEARCH_URL=http://elasticsearch:9200 - OPENSEARCH_URL=http://opensearch:9200
- REDIS_HOST=redis - REDIS_HOST=valkey
- DATABASE_URL=ecto://postgres:postgres@postgres/philomena_dev - DATABASE_URL=ecto://postgres:postgres@postgres/philomena_dev
- CDN_HOST=localhost - CDN_HOST=localhost
- MAILER_ADDRESS=noreply@philomena.local - MAILER_ADDRESS=noreply@philomena.local
@ -44,10 +48,14 @@ services:
tty: true tty: true
volumes: volumes:
- .:/srv/philomena - .:/srv/philomena
- app_cargo_data:/srv/philomena/.cargo
- app_build_data:/srv/philomena/_build
- app_deps_data:/srv/philomena/deps
- app_native_data:/srv/philomena/priv/native
depends_on: depends_on:
- postgres - postgres
- elasticsearch - opensearch
- redis - valkey
ports: ports:
- '5173:5173' - '5173:5173'
@ -60,21 +68,20 @@ services:
logging: logging:
driver: "none" driver: "none"
elasticsearch: opensearch:
image: elasticsearch:7.9.3 image: opensearchproject/opensearch:2.14.0
volumes: volumes:
- elastic_data:/usr/share/elasticsearch/data - opensearch_data:/usr/share/opensearch/data
- ./docker/opensearch/opensearch.yml:/usr/share/opensearch/config/opensearch.yml
logging: logging:
driver: "none" driver: "none"
environment:
- discovery.type=single-node
ulimits: ulimits:
nofile: nofile:
soft: 65536 soft: 65536
hard: 65536 hard: 65536
redis: valkey:
image: redis:7.2.4-alpine image: valkey/valkey:7.2.5-alpine
logging: logging:
driver: "none" driver: "none"

View file

@ -34,11 +34,11 @@ npm install
# Always install mix dependencies # Always install mix dependencies
(cd /srv/philomena && mix deps.get) (cd /srv/philomena && mix deps.get)
# Sleep to allow Elasticsearch to finish initializing # Sleep to allow OpenSearch to finish initializing
# if it's not done doing whatever it does yet # if it's not done doing whatever it does yet
echo -n "Waiting for Elasticsearch" echo -n "Waiting for OpenSearch"
until wget -qO - elasticsearch:9200; do until wget --no-check-certificate -qO - http://opensearch:9200; do
echo -n "." echo -n "."
sleep 2 sleep 2
done done

View file

@ -9,7 +9,7 @@ export MIX_ENV=test
# if it's not done doing whatever it does yet # if it's not done doing whatever it does yet
echo -n "Waiting for Elasticsearch" echo -n "Waiting for Elasticsearch"
until wget -qO - elasticsearch:9200; do until wget -qO - opensearch:9200; do
echo -n "." echo -n "."
sleep 2 sleep 2
done done

View file

@ -0,0 +1,15 @@
---
cluster.name: docker-cluster
# Bind to all interfaces because we don't know what IP address Docker will assign to us.
network.host: 0.0.0.0
# Setting network.host to a non-loopback address enables the annoying bootstrap checks. "Single-node" mode disables them again.
discovery.type: single-node
# Disable security. We don't need it for dev environment.
# Also, whoever thought it's a GREAT IDEA TO ENFORCE SECURITY FEATURES
# BY DEFAULT IN A FREAKING DOCKER CONTAINER should be forced to play
# the password game every time they would like to create a new account
# anywhere whatsoever.
plugins.security.disabled: true

View file

@ -0,0 +1,233 @@
defmodule Mix.Tasks.ConvertToVerifiedRoutes do
@moduledoc """
Replaces routes with verified routes.
Forked from
https://gist.github.com/andreaseriksson/e454b9244a734310d4ab74d8595f98cd
https://gist.github.com/jiegillet/e6357c82e36a848ad59295eb3d5a1135
This requires all routes to consistently be aliased with
alias PhilomenaWeb.Router.Helpers, as: Routes
Run with
mix convert_to_verified_routes
"""
use Mix.Task
@regex ~r/(Routes\.)([a-zA-Z0-9_]+)(path|url)\(/
@web_module PhilomenaWeb
def run(_) do
Path.wildcard("test/**/*.ex*")
|> Enum.concat(Path.wildcard("lib/**/*.ex*"))
|> Enum.concat(Path.wildcard("lib/**/*.eex*"))
|> Enum.concat(Path.wildcard("lib/**/*.slime"))
|> Enum.sort()
|> Enum.reject(&String.contains?(&1, "convert_to_verified_routes.ex"))
|> Enum.filter(&(&1 |> File.read!() |> String.contains?("Routes.")))
|> Enum.each(&format_file/1)
:ok
end
def format_file(filename) do
Mix.shell().info(filename)
formatted_content =
filename
|> File.read!()
|> format_string()
File.write!(filename, [formatted_content])
end
def format_string(source) do
case Regex.run(@regex, source, capture: :first, return: :index) do
[{index, length}] ->
# Compute full length of expression
length = nibble_expression(source, index, length)
# Convert to verified route format
route = format_route(String.slice(source, index, length))
# Split string around expression
prefix = String.slice(source, 0, index)
suffix = String.slice(source, index + length, String.length(source))
# Insert verified route and rerun
format_string("#{prefix}#{route}#{suffix}")
_ ->
source
end
end
defp nibble_expression(source, index, length) do
if index + length > String.length(source) do
raise "Failed to match route expression"
end
case Code.string_to_quoted(String.slice(source, index, length)) do
{:ok, _macro} ->
length
_ ->
nibble_expression(source, index, length + 1)
end
end
defp format_route(route) do
ast =
Code.string_to_quoted!(route,
literal_encoder: &{:ok, {:__block__, &2, [&1]}},
unescape: false,
token_metadata: true
)
ast
|> Macro.prewalk(&replace_route/1)
|> Code.quoted_to_algebra(escape: false)
|> Inspect.Algebra.format(:infinity)
end
defp decode_literal(literal) when is_binary(literal) or is_integer(literal) do
{:ok, literal}
end
defp decode_literal({:__block__, _, [literal]}) do
{:ok, literal}
end
defp decode_literal(node), do: {:error, node}
defp encode_literal(literal) do
{:__block__, [], [literal]}
end
# Routes.url(MyAppWeb.Endpoint)
defp replace_route({{:., _, [{:__aliases__, _, [:Routes]}, :url]}, _, [_conn_or_endpoint]}) do
{:url, [], [{:sigil_p, [delimiter: "\""], [{:<<>>, [], ["/"]}, []]}]}
end
# Routes.static_path(conn, "/images/favicon.ico")
defp replace_route({{:., _, [{:__aliases__, _, [:Routes]}, :static_path]}, _, args}) do
[_conn_or_endpoint, path] = args
case decode_literal(path) do
{:ok, path} -> {:sigil_p, [delimiter: "\""], [{:<<>>, [], [path]}, []]}
_ -> {:sigil_p, [delimiter: "\""], [path, []]}
end
end
# Routes.static_url(conn, "/images/favicon.ico")
defp replace_route({{:., _, [{:__aliases__, _, [:Routes]}, :static_url]}, _, args}) do
[_conn_or_endpoint, path] = args
sigil =
case decode_literal(path) do
{:ok, path} -> {:sigil_p, [delimiter: "\""], [{:<<>>, [], [path]}, []]}
_ -> {:sigil_p, [delimiter: "\""], [path, []]}
end
{:url, [], [sigil]}
end
# Routes.some_path(conn, :action, "en", query_params)
defp replace_route(
{{:., _, [{:__aliases__, _, [:Routes]}, path_name]}, _, [_ | _] = args} = node
) do
[_conn_or_endpoint, action | params] = args
action =
case decode_literal(action) do
{:ok, action} -> action
_ -> action
end
path_name = "#{path_name}"
case find_verified_route(path_name, action, params) do
:ok -> node
route -> route
end
end
defp replace_route(node), do: node
defp find_verified_route(path_name, action, arguments) do
# pleaaaase don't have a route named Routes.product_url_path(conn, :index)
trimmed_path = path_name |> String.trim_trailing("_path") |> String.trim_trailing("_url")
route =
Phoenix.Router.routes(@web_module.Router)
|> Enum.find(fn %{helper: helper, plug_opts: plug_opts} ->
plug_opts == action && is_binary(helper) && trimmed_path == helper
end)
case route do
%{path: path} ->
{path_bits, query_params} =
path
|> String.split("/", trim: true)
|> replace_path_variables(arguments, [])
path_bits =
path_bits
|> Enum.flat_map(fn bit -> ["/", bit] end)
|> format_for_sigil_binary_args(query_params)
sigil = {:sigil_p, [delimiter: "\""], [{:<<>>, [], path_bits}, []]}
if String.ends_with?(path_name, "_url") do
{:url, [], [sigil]}
else
sigil
end
_ ->
Mix.shell().error(
"Could not find route #{path_name}, with action #{inspect(action)} and arguments #{inspect(arguments)}"
)
end
end
defp replace_path_variables([], arguments, path_bits) do
{Enum.reverse(path_bits), arguments}
end
defp replace_path_variables(path, [], path_bits) do
{Enum.reverse(path_bits) ++ path, []}
end
# conceptually /post/:post_id -> /post/#{id}
defp replace_path_variables([path_piece | rest], [arg | args], path_bits) do
if String.starts_with?(path_piece, ":") do
replace_path_variables(rest, args, [arg | path_bits])
else
replace_path_variables(rest, [arg | args], [path_piece | path_bits])
end
end
defp format_for_sigil_binary_args(path_bits, [_ | _] = query_params) do
format_for_sigil_binary_args(path_bits ++ ["?" | query_params], [])
end
defp format_for_sigil_binary_args(path_bits, []) do
path_bits
|> Enum.map(&decode_literal/1)
|> Enum.map(fn
{:ok, bit} when is_binary(bit) ->
bit
{:ok, bit} when is_atom(bit) or is_integer(bit) ->
to_string(bit)
{_, bit} ->
{:"::", [],
[
{{:., [], [Kernel, :to_string]}, [from_interpolation: true], [encode_literal(bit)]},
{:binary, [], Elixir}
]}
end)
end
end

View file

@ -30,7 +30,7 @@ defmodule Philomena.Elasticsearch do
defp index_for(Filter), do: FilterIndex defp index_for(Filter), do: FilterIndex
defp elastic_url do defp elastic_url do
Application.get_env(:philomena, :elasticsearch_url) Application.get_env(:philomena, :opensearch_url)
end end
def create_index!(module) do def create_index!(module) do

View file

@ -5,6 +5,7 @@ defmodule Philomena.Images.Thumbnailer do
alias Philomena.DuplicateReports alias Philomena.DuplicateReports
alias Philomena.ImageIntensities alias Philomena.ImageIntensities
alias Philomena.ImagePurgeWorker
alias Philomena.Images.Image alias Philomena.Images.Image
alias Philomena.Processors alias Philomena.Processors
alias Philomena.Analyzers alias Philomena.Analyzers
@ -103,8 +104,14 @@ defmodule Philomena.Images.Thumbnailer do
defp apply_change(image, {:intensities, intensities}), defp apply_change(image, {:intensities, intensities}),
do: ImageIntensities.create_image_intensity(image, intensities) do: ImageIntensities.create_image_intensity(image, intensities)
defp apply_change(image, {:replace_original, new_file}), defp apply_change(image, {:replace_original, new_file}) do
do: upload_file(image, new_file, "full.#{image.image_format}") full = "full.#{image.image_format}"
upload_file(image, new_file, full)
Exq.enqueue(Exq, "indexing", ImagePurgeWorker, [
Path.join(image_url_base(image, nil), full)
])
end
defp apply_change(image, {:thumbnails, thumbnails}), defp apply_change(image, {:thumbnails, thumbnails}),
do: Enum.map(thumbnails, &apply_thumbnail(image, &1)) do: Enum.map(thumbnails, &apply_thumbnail(image, &1))

View file

@ -29,6 +29,8 @@ defmodule Philomena.Search.Parser do
__data__: nil __data__: nil
] ]
@max_clause_count 512
def parser(options) do def parser(options) do
parser = struct(Parser, options) parser = struct(Parser, options)
@ -305,14 +307,16 @@ defmodule Philomena.Search.Parser do
# Flattens the child of a disjunction or conjunction to improve performance. # Flattens the child of a disjunction or conjunction to improve performance.
defp flatten_disjunction_child(this_child, %{bool: %{should: next_child}} = child) defp flatten_disjunction_child(this_child, %{bool: %{should: next_child}} = child)
when child == %{bool: %{should: next_child}} and is_list(next_child), when child == %{bool: %{should: next_child}} and is_list(next_child) and
length(next_child) <= @max_clause_count,
do: %{bool: %{should: [this_child | next_child]}} do: %{bool: %{should: [this_child | next_child]}}
defp flatten_disjunction_child(this_child, next_child), defp flatten_disjunction_child(this_child, next_child),
do: %{bool: %{should: [this_child, next_child]}} do: %{bool: %{should: [this_child, next_child]}}
defp flatten_conjunction_child(this_child, %{bool: %{must: next_child}} = child) defp flatten_conjunction_child(this_child, %{bool: %{must: next_child}} = child)
when child == %{bool: %{must: next_child}} and is_list(next_child), when child == %{bool: %{must: next_child}} and is_list(next_child) and
length(next_child) <= @max_clause_count,
do: %{bool: %{must: [this_child | next_child]}} do: %{bool: %{must: [this_child | next_child]}}
defp flatten_conjunction_child(this_child, next_child), defp flatten_conjunction_child(this_child, next_child),

View file

@ -213,12 +213,12 @@ defmodule Philomena.Users do
|> Ecto.Multi.delete_all(:tokens, UserToken.user_and_contexts_query(user, [context])) |> Ecto.Multi.delete_all(:tokens, UserToken.user_and_contexts_query(user, [context]))
end end
@doc """ @doc ~S"""
Delivers the update email instructions to the given user. Delivers the update email instructions to the given user.
## Examples ## Examples
iex> deliver_update_email_instructions(user, current_email, &Routes.user_update_email_url(conn, :edit, &1)) iex> deliver_update_email_instructions(user, current_email, &url(~p"/registrations/email/#{&1})")
{:ok, %{to: ..., body: ...}} {:ok, %{to: ..., body: ...}}
""" """
@ -263,12 +263,12 @@ defmodule Philomena.Users do
|> Repo.update() |> Repo.update()
end end
@doc """ @doc ~S"""
Delivers the unlock instructions to the given user. Delivers the unlock instructions to the given user.
## Examples ## Examples
iex> deliver_user_unlock_instructions(user, &Routes.unlock_url(conn, :show, &1)) iex> deliver_user_unlock_instructions(user, &url(~p"/unlocks/#{&1}"))
{:ok, %{to: ..., body: ...}} {:ok, %{to: ..., body: ...}}
""" """
@ -379,15 +379,15 @@ defmodule Philomena.Users do
## Confirmation ## Confirmation
@doc """ @doc ~S"""
Delivers the confirmation email instructions to the given user. Delivers the confirmation email instructions to the given user.
## Examples ## Examples
iex> deliver_user_confirmation_instructions(user, &Routes.user_confirmation_url(conn, :confirm, &1)) iex> deliver_user_confirmation_instructions(user, &url(~p"/confirmations/#{&1}"))
{:ok, %{to: ..., body: ...}} {:ok, %{to: ..., body: ...}}
iex> deliver_user_confirmation_instructions(confirmed_user, &Routes.user_confirmation_url(conn, :confirm, &1)) iex> deliver_user_confirmation_instructions(confirmed_user, &url(~p"/confirmations/#{&1}"))
{:error, :already_confirmed} {:error, :already_confirmed}
""" """
@ -426,12 +426,12 @@ defmodule Philomena.Users do
## Reset password ## Reset password
@doc """ @doc ~S"""
Delivers the reset password email to the given user. Delivers the reset password email to the given user.
## Examples ## Examples
iex> deliver_user_reset_password_instructions(user, &Routes.user_reset_password_url(conn, :edit, &1)) iex> deliver_user_reset_password_instructions(user, &url(~p"/passwords/#{&1}/edit"))
{:ok, %{to: ..., body: ...}} {:ok, %{to: ..., body: ...}}
""" """

View file

@ -17,6 +17,8 @@ defmodule PhilomenaWeb do
and import those modules here. and import those modules here.
""" """
def static_paths, do: ~w(assets favicon.ico favicon.svg robots.txt)
def controller do def controller do
quote do quote do
use Phoenix.Controller, namespace: PhilomenaWeb use Phoenix.Controller, namespace: PhilomenaWeb
@ -26,6 +28,8 @@ defmodule PhilomenaWeb do
import Canary.Plugs import Canary.Plugs
import PhilomenaWeb.ModerationLogPlug, only: [moderation_log: 2] import PhilomenaWeb.ModerationLogPlug, only: [moderation_log: 2]
alias PhilomenaWeb.Router.Helpers, as: Routes alias PhilomenaWeb.Router.Helpers, as: Routes
unquote(verified_routes())
end end
end end
@ -47,6 +51,8 @@ defmodule PhilomenaWeb do
# Wrong way around for convenience # Wrong way around for convenience
import PhilomenaWeb.AppView import PhilomenaWeb.AppView
unquote(verified_routes())
end end
end end
@ -65,6 +71,15 @@ defmodule PhilomenaWeb do
end end
end end
def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
endpoint: PhilomenaWeb.Endpoint,
router: PhilomenaWeb.Router,
statics: PhilomenaWeb.static_paths()
end
end
@doc """ @doc """
When used, dispatch to the appropriate controller/view/etc. When used, dispatch to the appropriate controller/view/etc.
""" """

View file

@ -39,7 +39,7 @@ defmodule PhilomenaWeb.ChannelController do
if user, do: Channels.clear_notification(channel, user) if user, do: Channels.clear_notification(channel, user)
redirect(conn, external: url(channel)) redirect(conn, external: channel_url(channel))
end end
def new(conn, _params) do def new(conn, _params) do
@ -101,15 +101,15 @@ defmodule PhilomenaWeb.ChannelController do
defp maybe_show_nsfw(query, true), do: query defp maybe_show_nsfw(query, true), do: query
defp maybe_show_nsfw(query, _falsy), do: where(query, [c], c.nsfw == false) defp maybe_show_nsfw(query, _falsy), do: where(query, [c], c.nsfw == false)
defp url(%{type: "LivestreamChannel", short_name: short_name}), defp channel_url(%{type: "LivestreamChannel", short_name: short_name}),
do: "http://www.livestream.com/#{short_name}" do: "http://www.livestream.com/#{short_name}"
defp url(%{type: "PicartoChannel", short_name: short_name}), defp channel_url(%{type: "PicartoChannel", short_name: short_name}),
do: "https://picarto.tv/#{short_name}" do: "https://picarto.tv/#{short_name}"
defp url(%{type: "PiczelChannel", short_name: short_name}), defp channel_url(%{type: "PiczelChannel", short_name: short_name}),
do: "https://piczel.tv/watch/#{short_name}" do: "https://piczel.tv/watch/#{short_name}"
defp url(%{type: "TwitchChannel", short_name: short_name}), defp channel_url(%{type: "TwitchChannel", short_name: short_name}),
do: "https://www.twitch.tv/#{short_name}" do: "https://www.twitch.tv/#{short_name}"
end end

View file

@ -7,6 +7,8 @@ defmodule PhilomenaWeb.TorPlug do
plug PhilomenaWeb.TorPlug plug PhilomenaWeb.TorPlug
""" """
alias PhilomenaWeb.Router.Helpers, as: Routes alias PhilomenaWeb.Router.Helpers, as: Routes
use PhilomenaWeb, :verified_routes
alias Phoenix.Controller alias Phoenix.Controller
alias Plug.Conn alias Plug.Conn

View file

@ -8,6 +8,7 @@ defmodule PhilomenaWeb.TotpPlug do
""" """
alias PhilomenaWeb.Router.Helpers, as: Routes alias PhilomenaWeb.Router.Helpers, as: Routes
use PhilomenaWeb, :verified_routes
@doc false @doc false
@spec init(any()) :: any() @spec init(any()) :: any()

View file

@ -24,7 +24,7 @@ defmodule PhilomenaWeb.UserAttributionPlug do
attributes = [ attributes = [
ip: remote_ip, ip: remote_ip,
fingerprint: fingerprint(conn, conn.path_info), fingerprint: fingerprint(conn, conn.path_info),
referrer: conn.assigns.referrer, referrer: referrer(conn.assigns.referrer),
user: user, user: user,
user_agent: user_agent(conn) user_agent: user_agent(conn)
] ]
@ -47,4 +47,7 @@ defmodule PhilomenaWeb.UserAttributionPlug do
defp fingerprint(conn, _) do defp fingerprint(conn, _) do
conn.cookies["_ses"] conn.cookies["_ses"]
end end
defp referrer(nil), do: nil
defp referrer(r), do: String.slice(r, 0, 255)
end end

View file

@ -6,7 +6,7 @@
strong strong
= link("This image is blocked by your current filter - click here to display it anyway", to: "#", data: [click_unfilter: @image.id]) = link("This image is blocked by your current filter - click here to display it anyway", to: "#", data: [click_unfilter: @image.id])
p p
= img_tag(Routes.static_path(PhilomenaWeb.Endpoint, "/images/tagblocked.svg"), width: 250, height: 250, data: [click_unfilter: @image.id]) = img_tag(static_path(@conn, "/images/tagblocked.svg"), width: 250, height: 250, data: [click_unfilter: @image.id])
span.filter-explanation span.filter-explanation
=< link("your current filter", to: Routes.filter_path(@conn, :show, @conn.assigns.current_filter), class: "filter-link") =< link("your current filter", to: Routes.filter_path(@conn, :show, @conn.assigns.current_filter), class: "filter-link")
' . ' .

View file

@ -15,8 +15,8 @@ html lang="en"
link rel="stylesheet" href=stylesheet_path(@conn, @current_user) link rel="stylesheet" href=stylesheet_path(@conn, @current_user)
= if is_nil(@current_user) do = if is_nil(@current_user) do
link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)"
link rel="icon" href="/favicon.ico" type="image/x-icon" link rel="icon" href=~p"/favicon.ico" type="image/x-icon"
link rel="icon" href="/favicon.svg" type="image/svg+xml" link rel="icon" href=~p"/favicon.svg" type="image/svg+xml"
meta name="generator" content="philomena" meta name="generator" content="philomena"
meta name="theme-color" content="#618fc3" meta name="theme-color" content="#618fc3"
meta name="format-detection" content="telephone=no" meta name="format-detection" content="telephone=no"
@ -26,7 +26,7 @@ html lang="en"
script type="module" src="http://localhost:5173/@vite/client" script type="module" src="http://localhost:5173/@vite/client"
script type="module" src="http://localhost:5173/js/app.ts" script type="module" src="http://localhost:5173/js/app.ts"
- else - else
script type="text/javascript" src=Routes.static_path(@conn, "/js/app.js") async="async" script type="text/javascript" src=~p"/js/app.js" async="async"
= render PhilomenaWeb.LayoutView, "_opengraph.html", assigns = render PhilomenaWeb.LayoutView, "_opengraph.html", assigns
body data-theme=theme_name(@current_user) data-vite-reload=to_string(vite_reload?()) body data-theme=theme_name(@current_user) data-vite-reload=to_string(vite_reload?())
= render PhilomenaWeb.LayoutView, "_burger.html", assigns = render PhilomenaWeb.LayoutView, "_burger.html", assigns

View file

@ -7,6 +7,8 @@ defmodule PhilomenaWeb.UserAuth do
alias PhilomenaWeb.UserIpUpdater alias PhilomenaWeb.UserIpUpdater
alias PhilomenaWeb.UserFingerprintUpdater alias PhilomenaWeb.UserFingerprintUpdater
use PhilomenaWeb, :verified_routes
# Make the remember me cookie valid for 365 days. # Make the remember me cookie valid for 365 days.
# If you want bump or reduce this value, also change # If you want bump or reduce this value, also change
# the token expiry itself in UserToken. # the token expiry itself in UserToken.

View file

@ -88,13 +88,13 @@ defmodule PhilomenaWeb.LayoutView do
"light-cyan", "light-cyan",
"light-grey" "light-grey"
], ],
do: Routes.static_path(conn, "/css/#{theme}.css") do: static_path(conn, "/css/#{theme}.css")
def stylesheet_path(conn, _user), def stylesheet_path(_conn, _user),
do: Routes.static_path(conn, "/css/dark-blue.css") do: ~p"/css/dark-blue.css"
def light_stylesheet_path(conn), def light_stylesheet_path(_conn),
do: Routes.static_path(conn, "/css/light-blue.css") do: ~p"/css/light-blue.css"
def theme_name(%{theme: theme}), do: theme def theme_name(%{theme: theme}), do: theme
def theme_name(_user), do: "default" def theme_name(_user), do: "default"

View file

@ -1,52 +1,52 @@
defmodule PhilomenaWeb.SettingView do defmodule PhilomenaWeb.SettingView do
use PhilomenaWeb, :view use PhilomenaWeb, :view
def theme_options(conn) do def theme_options do
[ [
[ [
key: "Red", key: "Red",
value: "dark-red", value: "dark-red",
data: [theme_path: Routes.static_path(conn, "/css/dark-red.css")] data: [theme_path: ~p"/css/dark-red.css"]
], ],
[ [
key: "Orange", key: "Orange",
value: "dark-orange", value: "dark-orange",
data: [theme_path: Routes.static_path(conn, "/css/dark-orange.css")] data: [theme_path: ~p"/css/dark-orange.css"]
], ],
[ [
key: "Yellow", key: "Yellow",
value: "dark-yellow", value: "dark-yellow",
data: [theme_path: Routes.static_path(conn, "/css/dark-yellow.css")] data: [theme_path: ~p"/css/dark-yellow.css"]
], ],
[ [
key: "Green", key: "Green",
value: "dark-green", value: "dark-green",
data: [theme_path: Routes.static_path(conn, "/css/dark-green.css")] data: [theme_path: ~p"/css/dark-green.css"]
], ],
[ [
key: "Blue", key: "Blue",
value: "dark-blue", value: "dark-blue",
data: [theme_path: Routes.static_path(conn, "/css/dark-blue.css")] data: [theme_path: ~p"/css/dark-blue.css"]
], ],
[ [
key: "Purple", key: "Purple",
value: "dark-purple", value: "dark-purple",
data: [theme_path: Routes.static_path(conn, "/css/dark-purple.css")] data: [theme_path: ~p"/css/dark-purple.css"]
], ],
[ [
key: "Cyan", key: "Cyan",
value: "dark-cyan", value: "dark-cyan",
data: [theme_path: Routes.static_path(conn, "/css/dark-cyan.css")] data: [theme_path: ~p"/css/dark-cyan.css"]
], ],
[ [
key: "Pink", key: "Pink",
value: "dark-pink", value: "dark-pink",
data: [theme_path: Routes.static_path(conn, "/css/dark-pink.css")] data: [theme_path: ~p"/css/dark-pink.css"]
], ],
[ [
key: "Grey", key: "Grey",
value: "dark-grey", value: "dark-grey",
data: [theme_path: Routes.static_path(conn, "/css/dark-grey.css")] data: [theme_path: ~p"/css/dark-grey.css"]
] ]
] ]
end end
@ -56,47 +56,47 @@ defmodule PhilomenaWeb.SettingView do
[ [
key: "Red", key: "Red",
value: "light-red", value: "light-red",
data: [theme_path: Routes.static_path(conn, "/css/light-red.css")] data: [theme_path: ~p"/css/light-red.css"]
], ],
[ [
key: "Orange", key: "Orange",
value: "light-orange", value: "light-orange",
data: [theme_path: Routes.static_path(conn, "/css/light-orange.css")] data: [theme_path: ~p"/css/light-orange.css"]
], ],
[ [
key: "Yellow", key: "Yellow",
value: "light-yellow", value: "light-yellow",
data: [theme_path: Routes.static_path(conn, "/css/light-yellow.css")] data: [theme_path: ~p"/css/light-yellow.css"]
], ],
[ [
key: "Green", key: "Green",
value: "light-green", value: "light-green",
data: [theme_path: Routes.static_path(conn, "/css/light-green.css")] data: [theme_path: ~p"/css/light-green.css"]
], ],
[ [
key: "Blue", key: "Blue",
value: "light-blue", value: "light-blue",
data: [theme_path: Routes.static_path(conn, "/css/light-blue.css")] data: [theme_path: ~p"/css/light-blue.css"]
], ],
[ [
key: "Purple", key: "Purple",
value: "light-purple", value: "light-purple",
data: [theme_path: Routes.static_path(conn, "/css/light-purple.css")] data: [theme_path: ~p"/css/light-purple.css"]
], ],
[ [
key: "Cyan", key: "Cyan",
value: "light-cyan", value: "light-cyan",
data: [theme_path: Routes.static_path(conn, "/css/light-cyan.css")] data: [theme_path: ~p"/css/light-cyan.css"]
], ],
[ [
key: "Pink", key: "Pink",
value: "light-pink", value: "light-pink",
data: [theme_path: Routes.static_path(conn, "/css/light-pink.css")] data: [theme_path: ~p"/css/light-pink.css"]
], ],
[ [
key: "Grey", key: "Grey",
value: "light-grey", value: "light-grey",
data: [theme_path: Routes.static_path(conn, "/css/light-grey.css")] data: [theme_path: ~p"/css/light-grey.css"]
] ]
] ]
end end

View file

@ -43,8 +43,8 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "comrak" name = "comrak"
version = "0.22.0" version = "0.24.1"
source = "git+https://github.com/philomena-dev/comrak?branch=main#a27d6b6487d94e040e5cdb70733d3a59cba5bab0" source = "git+https://github.com/philomena-dev/comrak?branch=main#6a03dabfc80033b24070dc5826c9225686e3a98a"
dependencies = [ dependencies = [
"derive_builder", "derive_builder",
"entities", "entities",
@ -59,9 +59,9 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.14.4" version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"darling_macro", "darling_macro",
@ -69,58 +69,58 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.14.4" version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 1.0.109", "syn",
] ]
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.14.4" version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 1.0.109", "syn",
] ]
[[package]] [[package]]
name = "derive_builder" name = "derive_builder"
version = "0.12.0" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7"
dependencies = [ dependencies = [
"derive_builder_macro", "derive_builder_macro",
] ]
[[package]] [[package]]
name = "derive_builder_core" name = "derive_builder_core"
version = "0.12.0" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn",
] ]
[[package]] [[package]]
name = "derive_builder_macro" name = "derive_builder_macro"
version = "0.12.0" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b"
dependencies = [ dependencies = [
"derive_builder_core", "derive_builder_core",
"syn 1.0.109", "syn",
] ]
[[package]] [[package]]
@ -348,7 +348,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.59", "syn",
] ]
[[package]] [[package]]
@ -379,20 +379,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
@ -506,7 +495,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.59", "syn",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -528,7 +517,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.59", "syn",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View file

@ -25,6 +25,8 @@ defmodule PhilomenaWeb.ConnCase do
# The default endpoint for testing # The default endpoint for testing
@endpoint PhilomenaWeb.Endpoint @endpoint PhilomenaWeb.Endpoint
use PhilomenaWeb, :verified_routes
end end
end end