# `pub struct Api` Api manages the HTTP interface for the application Members: - `host: String` - the hostname of the service for checking NIP 98 auth, from `HOST` - `port: u16` - a port to run the server on from `PORT` - `admins: Vec` - a list of admin pubkeys from `ADMINS` - `origins: Vec` - to be used in CORS headers, from `ALLOW_ORIGINS` - `repo: Repo` Notes: - Authentication is done using NIP 98 - Each route is responsible for authorization using `self.is_admin(pubkey)` or `self.is_tenant(authorized_pubkey, tenant_pubkey)` - Successful API responses should be of the form `{data, code: "ok"}` with an appropriate http status code. - Unsuccessful API responses should be of the form `{error, code}` with an appropriate http status code. `code` is a short error code (e.g. `duplicate-subdomain`) and `error` is a human-readable error message. ## `pub fn new() -> Self` - Reads environment and populates members ## `pub fn serve(&self) -> Result<()>` - Initializes an `axum::Router` - Adds CORS middleware based on `origins` - Calls `axum::serve` with a listener --- Tenant routes ## `async fn list_tenants(...) -> Response` - Serves `GET /tenants` - Authorizes admin only - Return `data` is a list of tenant structs from `repo.list_tenants` ## `async fn get_tenant(...) -> Response` - Serves `GET /tenants/:pubkey` - Authorizes admin or matching tenant - Return `data` is a single tenant struct from `repo.get_tenant` ## `async fn create_tenant(...) -> Response` - Serves `POST /tenants` - Authorizes anyone, but must be authorized - Creates a new tenant using `repo.create_tenant` based on the authorized pubkey - If tenant is a duplicate, return a `422` with `code=pubkey-exists` - Return `data` is a single tenant struct. Use HTTP `201`. --- Relay routes ## `async fn list_relays(...) -> Response` - Serves `GET /relays?tenant=` - Authorizes admin or existing tenants - If user is admin, `tenant` query parameter is optional - If user is a tenant, `tenant` query parameter is not ok; authenticated `pubkey` is used - Return `data` is a list of relay structs from `repo.list_relays` ## `async fn get_relay(...) -> Response` - Serves `GET /relays/:id` - Authorizes admin or relay owner - Return `data` is a single relay struct from `repo.get_relay` ## `async fn create_relay(...) -> Response` - Serves `POST /relays` - Authorizes admin or matching tenant pubkey in request body - Validates/prepares the relay data to be saved using `prepare_relay` - Creates a new relay using `repo.create_relay` - If relay is a duplicate by subdomain, return a `422` with `code=subdomain-exists` - Return `data` is a single relay struct. Use HTTP `201`. ## `async fn update_relay(...) -> Response` - Serves `PUT /relays/:id` - Authorizes admin or relay owner - Validates/prepares the relay data to be saved using `prepare_relay` - Updates the given relay using `repo.update_relay` - If relay is a duplicate by subdomain, return a `422` with `code=subdomain-exists` - Return `data` is a single relay struct. ## `async fn deactivate_relay(...) -> Response` - Serves `POST /relays/:id/deactivate` - Authorizes admin or relay owner - Deactivates relay using `repo.deactivate_relay` - Return `data` is empty --- Billing routes ## `async fn list_invoices(...) -> Response` - Serves `GET /invoices?tenant=` - Authorizes admin or existing tenants - If user is admin, `tenant` query parameter is optional - If user is a tenant, `tenant` query parameter is not ok; authenticated `pubkey` is used - Return `data` is a list of invoice structs from `repo.list_invoices` # Utility functions ## `extract_auth_pubkey(headers: &HeaderMap, method: &Method, uri: &Uri) -> Result` - Parses `Authorization` header - Validates event kind and signature using `nostr_sdk` - Validates event `u` and `method` tags against parameters - Returns pubkey if header is valid Refer to https://github.com/nostr-protocol/nips/blob/master/98.md for details. Use `nostr_sdk` functionality where possible. ## `prepare_relay(relay: Relay) -> anyhow::Result` - Validate `subdomain` - If `plan` is free and `blossom` is enabled, return `premium-feature` - If `plan` is free and `livekit` is enabled, return `premium-feature` - Populate `schema` if not already set - Populate missing fields using reasonable defaults