storage: add behavior boundary and postgres adapter skeleton
This commit is contained in:
63
lib/parrhesia/storage.ex
Normal file
63
lib/parrhesia/storage.ex
Normal file
@@ -0,0 +1,63 @@
|
||||
defmodule Parrhesia.Storage do
|
||||
@moduledoc """
|
||||
Storage boundary entrypoint.
|
||||
|
||||
Domain/runtime code should resolve behavior modules through this module instead of
|
||||
depending on concrete adapter implementations directly.
|
||||
"""
|
||||
|
||||
@default_modules [
|
||||
events: Parrhesia.Storage.Adapters.Postgres.Events,
|
||||
moderation: Parrhesia.Storage.Adapters.Postgres.Moderation,
|
||||
groups: Parrhesia.Storage.Adapters.Postgres.Groups,
|
||||
admin: Parrhesia.Storage.Adapters.Postgres.Admin
|
||||
]
|
||||
|
||||
@spec events() :: module()
|
||||
def events, do: fetch_module!(:events, Parrhesia.Storage.Events)
|
||||
|
||||
@spec moderation() :: module()
|
||||
def moderation, do: fetch_module!(:moderation, Parrhesia.Storage.Moderation)
|
||||
|
||||
@spec groups() :: module()
|
||||
def groups, do: fetch_module!(:groups, Parrhesia.Storage.Groups)
|
||||
|
||||
@spec admin() :: module()
|
||||
def admin, do: fetch_module!(:admin, Parrhesia.Storage.Admin)
|
||||
|
||||
defp fetch_module!(key, behavior) do
|
||||
module =
|
||||
:parrhesia
|
||||
|> Application.get_env(:storage, [])
|
||||
|> Keyword.get(key, Keyword.fetch!(@default_modules, key))
|
||||
|
||||
ensure_behavior!(module, behavior, key)
|
||||
end
|
||||
|
||||
defp ensure_behavior!(module, behavior, key) do
|
||||
case Code.ensure_loaded(module) do
|
||||
{:module, _loaded_module} ->
|
||||
if implements_behavior?(module, behavior) do
|
||||
module
|
||||
else
|
||||
raise ArgumentError,
|
||||
"configured storage module #{inspect(module)} for #{inspect(key)} " <>
|
||||
"does not implement #{inspect(behavior)}"
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
raise ArgumentError,
|
||||
"configured storage module #{inspect(module)} for #{inspect(key)} could not be loaded: " <>
|
||||
"#{inspect(reason)}"
|
||||
end
|
||||
end
|
||||
|
||||
defp implements_behavior?(module, behavior) do
|
||||
module
|
||||
|> module_info_attributes()
|
||||
|> Keyword.get(:behaviour, [])
|
||||
|> Enum.member?(behavior)
|
||||
end
|
||||
|
||||
defp module_info_attributes(module), do: module.module_info(:attributes)
|
||||
end
|
||||
19
lib/parrhesia/storage/adapters/postgres/admin.ex
Normal file
19
lib/parrhesia/storage/adapters/postgres/admin.ex
Normal file
@@ -0,0 +1,19 @@
|
||||
defmodule Parrhesia.Storage.Adapters.Postgres.Admin do
|
||||
@moduledoc """
|
||||
PostgreSQL-backed implementation for `Parrhesia.Storage.Admin`.
|
||||
|
||||
Implementation is intentionally staged; callbacks currently return
|
||||
`{:error, :not_implemented}` until NIP-86 management storage lands.
|
||||
"""
|
||||
|
||||
@behaviour Parrhesia.Storage.Admin
|
||||
|
||||
@impl true
|
||||
def execute(_context, _method, _params), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def append_audit_log(_context, _entry), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def list_audit_logs(_context, _opts), do: {:error, :not_implemented}
|
||||
end
|
||||
31
lib/parrhesia/storage/adapters/postgres/events.ex
Normal file
31
lib/parrhesia/storage/adapters/postgres/events.ex
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
||||
@moduledoc """
|
||||
PostgreSQL-backed implementation for `Parrhesia.Storage.Events`.
|
||||
|
||||
Implementation is intentionally staged; callbacks currently return
|
||||
`{:error, :not_implemented}` until migrations and query paths land.
|
||||
"""
|
||||
|
||||
@behaviour Parrhesia.Storage.Events
|
||||
|
||||
@impl true
|
||||
def put_event(_context, _event), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def get_event(_context, _event_id), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def query(_context, _filters, _opts), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def count(_context, _filters, _opts), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def delete_by_request(_context, _event), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def vanish(_context, _event), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def purge_expired(_opts), do: {:error, :not_implemented}
|
||||
end
|
||||
31
lib/parrhesia/storage/adapters/postgres/groups.ex
Normal file
31
lib/parrhesia/storage/adapters/postgres/groups.ex
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule Parrhesia.Storage.Adapters.Postgres.Groups do
|
||||
@moduledoc """
|
||||
PostgreSQL-backed implementation for `Parrhesia.Storage.Groups`.
|
||||
|
||||
Implementation is intentionally staged; callbacks currently return
|
||||
`{:error, :not_implemented}` until group/membership schema lands.
|
||||
"""
|
||||
|
||||
@behaviour Parrhesia.Storage.Groups
|
||||
|
||||
@impl true
|
||||
def put_membership(_context, _membership), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def get_membership(_context, _group_id, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def delete_membership(_context, _group_id, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def list_memberships(_context, _group_id), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def put_role(_context, _role), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def delete_role(_context, _group_id, _pubkey, _role), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def list_roles(_context, _group_id, _pubkey), do: {:error, :not_implemented}
|
||||
end
|
||||
46
lib/parrhesia/storage/adapters/postgres/moderation.ex
Normal file
46
lib/parrhesia/storage/adapters/postgres/moderation.ex
Normal file
@@ -0,0 +1,46 @@
|
||||
defmodule Parrhesia.Storage.Adapters.Postgres.Moderation do
|
||||
@moduledoc """
|
||||
PostgreSQL-backed implementation for `Parrhesia.Storage.Moderation`.
|
||||
|
||||
Implementation is intentionally staged; callbacks currently return
|
||||
`{:error, :not_implemented}` until table design and policy paths land.
|
||||
"""
|
||||
|
||||
@behaviour Parrhesia.Storage.Moderation
|
||||
|
||||
@impl true
|
||||
def ban_pubkey(_context, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def unban_pubkey(_context, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def pubkey_banned?(_context, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def allow_pubkey(_context, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def disallow_pubkey(_context, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def pubkey_allowed?(_context, _pubkey), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def ban_event(_context, _event_id), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def unban_event(_context, _event_id), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def event_banned?(_context, _event_id), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def block_ip(_context, _ip_address), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def unblock_ip(_context, _ip_address), do: {:error, :not_implemented}
|
||||
|
||||
@impl true
|
||||
def ip_blocked?(_context, _ip_address), do: {:error, :not_implemented}
|
||||
end
|
||||
16
lib/parrhesia/storage/admin.ex
Normal file
16
lib/parrhesia/storage/admin.ex
Normal file
@@ -0,0 +1,16 @@
|
||||
defmodule Parrhesia.Storage.Admin do
|
||||
@moduledoc """
|
||||
Storage callbacks used by relay management endpoints (NIP-86 backing).
|
||||
"""
|
||||
|
||||
@type context :: map()
|
||||
@type method :: atom() | binary()
|
||||
@type params :: map()
|
||||
@type result :: map() | list() | term()
|
||||
@type audit_entry :: map()
|
||||
@type reason :: term()
|
||||
|
||||
@callback execute(context(), method(), params()) :: {:ok, result()} | {:error, reason()}
|
||||
@callback append_audit_log(context(), audit_entry()) :: :ok | {:error, reason()}
|
||||
@callback list_audit_logs(context(), keyword()) :: {:ok, [audit_entry()]} | {:error, reason()}
|
||||
end
|
||||
22
lib/parrhesia/storage/events.ex
Normal file
22
lib/parrhesia/storage/events.ex
Normal file
@@ -0,0 +1,22 @@
|
||||
defmodule Parrhesia.Storage.Events do
|
||||
@moduledoc """
|
||||
Storage callbacks for event persistence and query operations.
|
||||
"""
|
||||
|
||||
@type context :: map()
|
||||
@type event_id :: binary()
|
||||
@type event :: map()
|
||||
@type filter :: map()
|
||||
@type query_opts :: keyword()
|
||||
@type count_result :: non_neg_integer() | %{optional(atom()) => term()}
|
||||
@type reason :: term()
|
||||
|
||||
@callback put_event(context(), event()) :: {:ok, event()} | {:error, reason()}
|
||||
@callback get_event(context(), event_id()) :: {:ok, event() | nil} | {:error, reason()}
|
||||
@callback query(context(), [filter()], query_opts()) :: {:ok, [event()]} | {:error, reason()}
|
||||
@callback count(context(), [filter()], query_opts()) ::
|
||||
{:ok, count_result()} | {:error, reason()}
|
||||
@callback delete_by_request(context(), event()) :: {:ok, non_neg_integer()} | {:error, reason()}
|
||||
@callback vanish(context(), event()) :: {:ok, non_neg_integer()} | {:error, reason()}
|
||||
@callback purge_expired(query_opts()) :: {:ok, non_neg_integer()} | {:error, reason()}
|
||||
end
|
||||
22
lib/parrhesia/storage/groups.ex
Normal file
22
lib/parrhesia/storage/groups.ex
Normal file
@@ -0,0 +1,22 @@
|
||||
defmodule Parrhesia.Storage.Groups do
|
||||
@moduledoc """
|
||||
Storage callbacks for NIP-29/NIP-43 group membership and role state.
|
||||
"""
|
||||
|
||||
@type context :: map()
|
||||
@type group_id :: binary()
|
||||
@type pubkey :: binary()
|
||||
@type membership :: map()
|
||||
@type role :: map()
|
||||
@type reason :: term()
|
||||
|
||||
@callback put_membership(context(), membership()) :: {:ok, membership()} | {:error, reason()}
|
||||
@callback get_membership(context(), group_id(), pubkey()) ::
|
||||
{:ok, membership() | nil} | {:error, reason()}
|
||||
@callback delete_membership(context(), group_id(), pubkey()) :: :ok | {:error, reason()}
|
||||
@callback list_memberships(context(), group_id()) :: {:ok, [membership()]} | {:error, reason()}
|
||||
|
||||
@callback put_role(context(), role()) :: {:ok, role()} | {:error, reason()}
|
||||
@callback delete_role(context(), group_id(), pubkey(), binary()) :: :ok | {:error, reason()}
|
||||
@callback list_roles(context(), group_id(), pubkey()) :: {:ok, [role()]} | {:error, reason()}
|
||||
end
|
||||
27
lib/parrhesia/storage/moderation.ex
Normal file
27
lib/parrhesia/storage/moderation.ex
Normal file
@@ -0,0 +1,27 @@
|
||||
defmodule Parrhesia.Storage.Moderation do
|
||||
@moduledoc """
|
||||
Storage callbacks for moderation and access-control state.
|
||||
"""
|
||||
|
||||
@type context :: map()
|
||||
@type pubkey :: binary()
|
||||
@type event_id :: binary()
|
||||
@type ip_address :: binary()
|
||||
@type reason :: term()
|
||||
|
||||
@callback ban_pubkey(context(), pubkey()) :: :ok | {:error, reason()}
|
||||
@callback unban_pubkey(context(), pubkey()) :: :ok | {:error, reason()}
|
||||
@callback pubkey_banned?(context(), pubkey()) :: {:ok, boolean()} | {:error, reason()}
|
||||
|
||||
@callback allow_pubkey(context(), pubkey()) :: :ok | {:error, reason()}
|
||||
@callback disallow_pubkey(context(), pubkey()) :: :ok | {:error, reason()}
|
||||
@callback pubkey_allowed?(context(), pubkey()) :: {:ok, boolean()} | {:error, reason()}
|
||||
|
||||
@callback ban_event(context(), event_id()) :: :ok | {:error, reason()}
|
||||
@callback unban_event(context(), event_id()) :: :ok | {:error, reason()}
|
||||
@callback event_banned?(context(), event_id()) :: {:ok, boolean()} | {:error, reason()}
|
||||
|
||||
@callback block_ip(context(), ip_address()) :: :ok | {:error, reason()}
|
||||
@callback unblock_ip(context(), ip_address()) :: :ok | {:error, reason()}
|
||||
@callback ip_blocked?(context(), ip_address()) :: {:ok, boolean()} | {:error, reason()}
|
||||
end
|
||||
Reference in New Issue
Block a user