From 109768f094ee9d26e1fa18ccaf4036bf87055c01 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Tue, 7 Apr 2026 09:50:47 +0200 Subject: [PATCH] implement issue #6 Basic relay metadata, include name and icons Add script to reload on config changes Updated ReadMe to reflect our changes --- README.md | 141 ++++++++++++++--- config/config.exs | 21 +++ dev.sh | 50 ++++++ .../components/layouts/root.html.heex | 3 + lib/gc_index_relay_web/endpoint.ex | 1 + lib/gc_index_relay_web/plugs/relay_info.ex | 33 ++++ priv/static/favicon.ico | Bin 152 -> 5230 bytes priv/static/images/favicon-32x32.png | Bin 0 -> 2074 bytes priv/static/images/mercury_icon.png | Bin 0 -> 223177 bytes priv/static/images/mercury_icon_small.png | Bin 0 -> 26736 bytes setup.sh | 3 +- test/features/relay_api.feature | 49 ++++++ .../relay_integration_test.exs | 145 ++++++++++++++++++ 13 files changed, 427 insertions(+), 19 deletions(-) create mode 100755 dev.sh create mode 100644 lib/gc_index_relay_web/plugs/relay_info.ex create mode 100644 priv/static/images/favicon-32x32.png create mode 100644 priv/static/images/mercury_icon.png create mode 100644 priv/static/images/mercury_icon_small.png diff --git a/README.md b/README.md index e2f7a6a..35d48a6 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,145 @@ -# GcIndexRelay +
+ Mercury Index-Relay logo +
+ +# GitCitadel Mercury Index-Relay + +A Nostr index relay built with Phoenix 1.8 / Elixir. Stores and serves Nostr events via a REST API, with filter-based querying per NIP-01. + +**Supported NIPs:** [NIP-11](https://github.com/nostr-protocol/nips/blob/master/11.md) (relay info), [NIP-70](https://github.com/nostr-protocol/nips/blob/master/70.md) (protected events) ## Getting Started +### Prerequisites + +- Docker (for the database) +- Elixir ~> 1.15 + +### Automated setup + +Run the setup script — it installs Erlang/Elixir via asdf, starts the Apache AGE database container, and runs `mix setup`: + +```bash +chmod +x setup.sh +./setup.sh +``` + +After setup, database credentials are written to `.env`. Source it before running any `mix` commands: + +```bash +source .env +``` + +### Manual setup + Start the Apache AGE Docker container: ```bash -docker build \ - -t age \ - -f ./db/Dockerfile \ - ./db docker run \ -d \ + --name gc_age_db \ -p 5455:5432 \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=gc_index_relay_dev \ - age + apache/age:release_PG17_1.6.0 ``` -To start your Phoenix server: +Set database environment variables, then install dependencies and run migrations: -- Run `mix setup` to install and setup dependencies -- Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server` +```bash +export POSTGRES_HOST=localhost +export POSTGRES_PORT=5455 +export POSTGRES_USER=postgres +export POSTGRES_PASSWORD=postgres +export POSTGRES_DB=gc_index_relay_dev -Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. +mix setup +``` + +### Starting the server + +```bash +source .env +mix phx.server +``` + +The relay is available at [http://localhost:4000](http://localhost:4000). + +During development, use `dev.sh` instead to get automatic server restarts when config files change: + +```bash +source .env && ./dev.sh +``` -Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html). +## API + +| Method | Path | Description | +|--------|------|-------------| +| `GET` | `/api` | List available endpoints | +| `GET` | `/api/events` | Query events via URL params (`since`, `until`, `limit` required) | +| `POST` | `/api/events/filter` | Query events with a NIP-01 filter body (`limit` required) | +| `GET` | `/api/events/:id` | Fetch a single event by ID | +| `POST` | `/api/events` | Publish a new event | +| `DELETE` | `/api/events/:id` | Delete an event by ID | +| `GET` | `/api/swagger` | Swagger UI | +| `GET` | `/health` | Health check | + +### NIP-11 relay info + +```bash +curl -s -H "Accept: application/nostr+json" http://localhost:4000 | jq +``` + +Edit the relay metadata in `config/config.exs` under the `:relay_info` key. + +## Testing + +Unit tests (no database required): + +```bash +mix test.unit +``` + +Integration tests (requires the database to be running): + +```bash +source .env +mix test.integration +``` + +Run the full integration probe against the relay API (covers all endpoints, CORS, NIP-11, NIP-70): + +```bash +source .env +mix test.integration test/gc_index_relay_web/relay_integration_test.exs +``` + +The test scenarios are documented in [test/features/relay_api.feature](test/features/relay_api.feature) and cover: + +- Discovery (`GET /api`) +- Publishing and rejecting events (`POST /api/events`) +- Deleting events (`DELETE /api/events/:id`) +- Cacheable queries (`GET /api/events?since=&until=&limit=`) +- Filter-based queries (`POST /api/events/filter`) +- Single-event lookup (`GET /api/events/:id`) +- CORS preflight and headers +- NIP-11 relay info document including the `icon` field + +Pre-commit check (compile with warnings-as-errors, format, credo, integration tests): + +```bash +source .env +mix precommit +``` ## Project Overview ### Database - The database stores Nostr events. -- Nostr events, once signed, are considered to be immutable. +- Nostr events, once signed, are considered immutable. +- Uses Apache AGE (PostgreSQL with graph extensions). #### Migrations @@ -42,7 +149,7 @@ After modifying an Ecto schema, generate a migration with: mix ecto.gen.migration ``` -Edit the generated migration file as needed, then perform the migration with: +Edit the generated migration file as needed, then apply it: ```bash mix ecto.migrate @@ -52,8 +159,6 @@ Refer to the Fly.io guide [Safe Ecto Migrations](https://github.com/fly-apps/saf ## Learn more -- Official website: https://www.phoenixframework.org/ -- Guides: https://hexdocs.pm/phoenix/overview.html -- Docs: https://hexdocs.pm/phoenix -- Forum: https://elixirforum.com/c/phoenix-forum -- Source: https://github.com/phoenixframework/phoenix +- Phoenix: https://www.phoenixframework.org/ +- Nostr protocol: https://github.com/nostr-protocol/nostr +- NIP-01 (basic protocol): https://github.com/nostr-protocol/nips/blob/master/01.md diff --git a/config/config.exs b/config/config.exs index fe07fa4..c1a703c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -49,6 +49,27 @@ config :logger, :default_formatter, # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason +# NIP-11 relay information document +# Served at GET / with Accept: application/nostr+json +# Edit these values to describe your relay instance. +config :gc_index_relay, :relay_info, + name: "Mercury Index-Relay", + icon: + "https://git.imwald.eu/silberengel/gc_index_relay/src/branch/test/local-setup/priv/static/favicon.ico", + banner: + "https://git.imwald.eu/silberengel/gc_index_relay/src/branch/test/local-setup/priv/static/mercury_icon.png", + description: + "A Nostr index relay for the http protocol, from GitCitadel. Featuring a RESTful API and Swagger, it specializes in swift retrieval or publications, repos, and similar graphs of related events", + software: "https://git.imwald.eu/silberengel/gc_index_relay.git", + version: "0.2", + supported_nips: [11, 70], + limitation: %{ + max_limit: 100, + auth_required: false, + payment_required: false, + restricted_writes: false + } + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..1b2ae40 --- /dev/null +++ b/dev.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Development server with auto-restart on config file changes. +# +# Normal code changes (controllers, templates, etc.) are still hot-reloaded +# by Phoenix automatically. This script only handles the cases Phoenix can't: +# config/config.exs, config/dev.exs, and config/runtime.exs. +# +# Usage: +# chmod +x dev.sh +# source .env && ./dev.sh + +set -euo pipefail + +CONFIG_FILES=( + config/config.exs + config/dev.exs + config/runtime.exs +) + +cleanup() { + if [ -n "${SERVER_PID:-}" ] && kill -0 "$SERVER_PID" 2>/dev/null; then + echo "" + echo "[dev] Stopping server (pid $SERVER_PID)..." + kill "$SERVER_PID" + wait "$SERVER_PID" 2>/dev/null || true + fi + exit 0 +} + +trap cleanup INT TERM + +echo "[dev] Watching config files for changes: ${CONFIG_FILES[*]}" +echo "[dev] Normal code changes are still hot-reloaded automatically." +echo "[dev] Press Ctrl+C to stop." +echo "" + +while true; do + echo "[dev] Starting server..." + mix phx.server & + SERVER_PID=$! + + # Block until any config file is modified + inotifywait -q -e modify "${CONFIG_FILES[@]}" 2>/dev/null + + echo "" + echo "[dev] Config changed — restarting server..." + kill "$SERVER_PID" 2>/dev/null + wait "$SERVER_PID" 2>/dev/null || true + echo "" +done diff --git a/lib/gc_index_relay_web/components/layouts/root.html.heex b/lib/gc_index_relay_web/components/layouts/root.html.heex index ff2d7f8..012d6cf 100644 --- a/lib/gc_index_relay_web/components/layouts/root.html.heex +++ b/lib/gc_index_relay_web/components/layouts/root.html.heex @@ -7,6 +7,9 @@ <.live_title default="GcIndexRelay" suffix=" · Phoenix Framework"> {assigns[:page_title]} + + +