Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Phoenix 1.8 server-rendered MVC — **no LiveView**. Views/templates use `phoeni

Four Elixir app namespaces under `lib/`:

- **`Philomena`** — domain contexts (images, tags, forums, comments, users, filters, galleries, notifications, ...). Each context is a `<name>.ex` module plus a `<name>/` directory of Ecto schemas and helpers. Background jobs live in `lib/philomena/workers/` and run via Exq (Redis/Valkey-backed); contexts enqueue e.g. `IndexWorker`, `ThumbnailWorker`.
- **`Philomena`** — domain contexts (images, tags, forums, comments, users, filters, galleries, notifications, ...). Each context is a `<name>.ex` module plus a `<name>/` directory of Ecto schemas and helpers. Background jobs live in `lib/philomena/workers/` and run via Oban (Postgres-backed); contexts enqueue e.g. `IndexWorker`, `ThumbnailWorker`.
- **`PhilomenaWeb`** — controllers, plugs, views, templates. Routing is aggressively RESTful: instead of custom actions there are many small nested singleton controllers (e.g. `Image.VoteController`, `Topic.SubscriptionController`) with only `create`/`delete`. The public JSON API is `lib/philomena_web/controllers/api/json/` and is documented by `openapi.yaml` at the repo root — keep the two in sync. Authorization uses Canada/Canary (`can?` protocols + plugs).
- **`PhilomenaQuery`** — the search layer. `parse/` is a nimble_parsec-based parser for the user-facing search query language; `search.ex` + `search/` is the OpenSearch client. Each searchable domain implements the `PhilomenaQuery.Search.Index` behaviour (e.g. `Philomena.Images.SearchIndex`) defining the index mapping and document serialization. Data flow: writes go to Postgres, then documents are (re)indexed into OpenSearch via `PhilomenaQuery.Search.reindex`/`IndexWorker`.
- **`PhilomenaMedia`** — media intake pipeline: `analyzers/` (mime/dimension/duration detection), `processors/` (per-format thumbnailing/optimization), intensities for duplicate detection, and `objects.ex` for S3 storage (ex_aws; s3proxy in dev).
Expand Down
8 changes: 4 additions & 4 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ config :philomena,
search_target_poll_interval_ms: 5_000,
search_migration_settle_ms: 15_000

config :exq,
max_retries: 5,
scheduler_enable: true,
start_on_application: false
config :philomena, Oban,
repo: Philomena.Repo,
plugins: [Oban.Pruner],
queues: [videos: 2, images: 4, indexing: 12, notifications: 2]

# Configures the endpoint
config :philomena, PhilomenaWeb.Endpoint,
Expand Down
11 changes: 1 addition & 10 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,9 @@ json_config =
config :philomena,
config: json_config

config :exq,
host: System.get_env("REDIS_HOST", "localhost"),
queues: [
{"videos", 2},
{"images", 4},
{"indexing", 12},
{"notifications", 2}
]

if is_nil(System.get_env("START_WORKER")) do
# Make queueing available but don't process any jobs
config :exq, queues: []
config :philomena, Oban, queues: false
end

# S3/Object store config
Expand Down
10 changes: 5 additions & 5 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ config :philomena,
pwned_passwords: false,
captcha: false

# Keep test enqueues in memory. The application still exercises the same
# enqueue calls, but the test suite cannot fill the shared development Valkey
# instance with jobs that reference the test database.
config :exq,
queue_adapter: Exq.Adapters.Queue.Mock
# Keep test enqueues in the sandbox database without starting queue consumers.
config :philomena, Oban,
testing: :manual,
queues: false,
plugins: false

# Namespace OpenSearch indexes so test runs cannot touch dev data on the
# shared cluster. Search-backed tests recreate their index in setup; see
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ services:
# event's module and function. The last entry in the list of filters should
# be a bare `level` which will be used as a catch-all for all other log
# events that do not match any of the previous filters.
- PHILOMENA_LOG=${PHILOMENA_LOG-Ecto=debug,Exq=none,PhilomenaMedia.Objects=info,debug}
- PHILOMENA_LOG=${PHILOMENA_LOG-Ecto=debug,Oban=none,PhilomenaMedia.Objects=info,debug}
- MIX_ENV=dev
- PGPASSWORD=postgres
- ANONYMOUS_NAME_SALT=2fmJRo0OgMFe65kyAJBxPT0QtkVes/jnKDdtP21fexsRqiw8TlSY7yO+uFyMZycp
Expand Down
4 changes: 2 additions & 2 deletions lib/philomena/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ defmodule Philomena.Application do

# Search write-target tracking, so document writes fan out to both
# indices during a search index migration. Must start before anything
# which writes documents (the endpoint and the Exq workers).
# which writes documents (the endpoint and the Oban workers).
{PhilomenaQuery.Search.WriteTargets, []},

# Background queueing system
Philomena.ExqSupervisor,
{Oban, Application.fetch_env!(:philomena, Oban)},

# Mailer
{Task.Supervisor, name: Philomena.AsyncEmailSupervisor},
Expand Down
5 changes: 2 additions & 3 deletions lib/philomena/bans.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ defmodule Philomena.Bans do
alias Philomena.Bans.UserQueryForm
alias Philomena.ModerationLogs
alias Philomena.Multi
alias Philomena.Workers.IndexJob
alias Philomena.UserIps
alias Philomena.Users

Expand Down Expand Up @@ -553,9 +554,7 @@ defmodule Philomena.Bans do
|> repo.insert()
end
end)
|> Multi.on_commit(fn _changes ->
Users.reindex_user(%Users.User{id: target.id})
end)
|> IndexJob.put_enqueue("Users", :id, [target.id])
end

@doc """
Expand Down
57 changes: 7 additions & 50 deletions lib/philomena/comments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule Philomena.Comments do
alias Philomena.Filters.Filter
alias Philomena.Images
alias Philomena.Images.Image
alias Philomena.IndexWorker
alias Philomena.Workers.IndexJob
alias Philomena.IntegerId
alias Philomena.Loader
alias Philomena.ModerationLogs
Expand Down Expand Up @@ -83,7 +83,7 @@ defmodule Philomena.Comments do
end

defp put_reindex_comment(%Multi{} = multi, step \\ :comment) do
Multi.on_commit(multi, fn %{^step => comment} -> reindex_comment(comment) end)
IndexJob.put_enqueue(multi, "Comments", :id, fn %{^step => comment} -> [comment.id] end)
end

defp put_approval_report(%Multi{} = multi) do
Expand Down Expand Up @@ -354,8 +354,9 @@ defmodule Philomena.Comments do
Write access, image commenting permission, the Images-owned forced-filter
prerequisite, and the 15-second creation limit are checked before insertion.
The transaction updates the image count, notification, and subscription state.
Indexing, statistics/reporting, rate tracking, and the firehose broadcast run
after commit. The image is returned for the caller to reuse.
Indexing, statistics/reporting, and rate tracking are committed with the
write; the firehose broadcast runs after commit. The image is returned for
the caller to reuse.

## Examples

Expand Down Expand Up @@ -477,7 +478,8 @@ defmodule Philomena.Comments do

Write access is checked before image authorization, forced-filter enforcement,
and comment authorization. A successful transaction records the prior version
Reporting, indexing, and the firehose broadcast run after commit. Validation
Reporting and indexing are committed with the write; the firehose broadcast
runs after commit. Validation
returns the changeset preserving the loaded comment and image. On success,
the image is returned for the caller to reuse.

Expand Down Expand Up @@ -828,51 +830,6 @@ defmodule Philomena.Comments do
Search.update_by_query(Comment, data.query, data.set_replacements, data.replacements)
end

@doc """
Queues one comment for search indexing and returns it unchanged.

## Examples

iex> reindex_comment(comment)
%Comment{}

"""
@spec reindex_comment(Comment.t()) :: Comment.t()
def reindex_comment(%Comment{} = comment) do
Exq.enqueue(Exq, "indexing", IndexWorker, ["Comments", "id", [comment.id]])
comment
end

@doc """
Queues every comment on `image` for indexing and returns the image unchanged.

## Examples

iex> reindex_comments_on_image(image)
%Image{}

"""
@spec reindex_comments_on_image(Image.t()) :: Image.t()
def reindex_comments_on_image(%Image{} = image) do
Exq.enqueue(Exq, "indexing", IndexWorker, ["Comments", "image_id", [image.id]])
image
end

@doc """
Queues comments on the given image IDs for reindexing and returns the list unchanged.

## Examples

iex> reindex_comments_on_images([1, 2, 3])
[1, 2, 3]

"""
@spec reindex_comments_on_images([integer()]) :: [integer()]
def reindex_comments_on_images(image_ids) when is_list(image_ids) do
Exq.enqueue(Exq, "indexing", IndexWorker, ["Comments", "image_id", image_ids])
image_ids
end

@doc """
Returns the association queries required to serialize comment search records.

Expand Down
17 changes: 0 additions & 17 deletions lib/philomena/exq_supervisor.ex

This file was deleted.

33 changes: 5 additions & 28 deletions lib/philomena/filters.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Philomena.Filters do
alias Philomena.Users
alias Philomena.Users.User
alias PhilomenaQuery.Search
alias Philomena.IndexWorker
alias Philomena.Workers.IndexJob

defp ensure_current_filter(%User{current_filter: current_filter} = user) do
if current_filter do
Expand Down Expand Up @@ -59,14 +59,7 @@ defmodule Philomena.Filters do
end

defp put_reindex_filter(multi, step) do
Multi.on_commit(multi, fn %{^step => filter} -> reindex_filter(filter) end)
end

defp reindex_filter_ids([]), do: []

defp reindex_filter_ids(filter_ids) do
Exq.enqueue(Exq, "indexing", IndexWorker, ["Filters", "id", filter_ids])
filter_ids
IndexJob.put_enqueue(multi, "Filters", :id, fn %{^step => filter} -> [filter.id] end)
end

@doc """
Expand Down Expand Up @@ -921,8 +914,9 @@ defmodule Philomena.Filters do
|> Multi.all(spoilered_ids_step, select(exclude(spoilered_filters, :update), [f], f.id))
|> Multi.update_all(hidden_step, hidden_filters, [])
|> Multi.update_all(spoilered_step, spoilered_filters, [])
|> Multi.on_commit(fn %{^hidden_ids_step => hidden_ids, ^spoilered_ids_step => spoilered_ids} ->
reindex_filter_ids(Enum.uniq(hidden_ids ++ spoilered_ids))
|> IndexJob.put_enqueue("Filters", :id, fn
%{^hidden_ids_step => hidden_ids, ^spoilered_ids_step => spoilered_ids} ->
Enum.uniq(hidden_ids ++ spoilered_ids)
end)
end

Expand All @@ -942,23 +936,6 @@ defmodule Philomena.Filters do
Search.update_by_query(Filter, data.query, data.set_replacements, data.replacements)
end

@doc """
Queues a single filter for search index updates.
Returns the filter struct unchanged, for use in a pipeline.

## Examples

iex> reindex_filter(filter)
%Filter{}

"""
@spec reindex_filter(Filter.t()) :: Filter.t()
def reindex_filter(%Filter{} = filter) do
Exq.enqueue(Exq, "indexing", IndexWorker, ["Filters", "id", [filter.id]])

filter
end

@doc """
Returns a list of associations to preload when indexing filters.

Expand Down
Loading
Loading