Docs: a page each, and API references for ipx and the directory laid out like docs.rs (#223)
site/gen.mjs writes them from site/api.mjs, which holds each endpoint and type as the handlers have them, and site/docs.mjs, the guides. A misspelt type in api.mjs fails the build rather than leaving a name that links nowhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
22
site/guides/cli.html
Normal file
22
site/guides/cli.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<h1>The command line</h1>
|
||||
<p>The same <code>ipx</code> program that runs the server is its command line. Run it in the container with <code>docker exec ipx ipx …</code>; when the server is running, it does the work and the command shows what happened.</p>
|
||||
<div class="scroll"><table>
|
||||
<thead><tr><th>Command</th><th>What it does</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>ipx list</code></td><td>Every podcast and its state</td></tr>
|
||||
<tr><td><code>ipx status</code></td><td>How many podcasts, how many files waiting and downloaded</td></tr>
|
||||
<tr><td><code>ipx fetch [id] [--force]</code></td><td>Check everything that is due, or one podcast; <code>--force</code> checks it even if it is not due</td></tr>
|
||||
<tr><td><code>ipx add <url></code>, <code>ipx rm <id></code></td><td>Add a podcast, or take it out</td></tr>
|
||||
<tr><td><code>ipx import <file></code>, <code>ipx export <file></code></td><td>OPML in or out</td></tr>
|
||||
<tr><td><code>ipx reap [--dry-run]</code></td><td>Clear old files now, to stay within the space you set</td></tr>
|
||||
<tr><td><code>ipx user add|list|passwd|rm</code></td><td>Accounts</td></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
<h2>The control socket</h2>
|
||||
<p>A program on the same machine can also talk to the running server over its control socket, one JSON command a line, and read back one event a line:</p>
|
||||
<pre><code>printf '{"cmd":"fetch","force":true}\n' | socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/ipx.sock
|
||||
{"ev":"feed_start","feed":"atp"}
|
||||
{"ev":"download_done","feed":"atp","enclosure":42,"path":"…","bytes":3000000}
|
||||
{"ev":"feed_done","feed":"atp","new":1,"downloaded":1,"failed":0,"torrents":0}
|
||||
{"ev":"scan_done","feeds":1}</code></pre>
|
||||
<p>The commands are <code>fetch</code> (optional <code>feed</code>, <code>force</code>), <code>download</code> (<code>enclosure</code>), <code>reap</code> (optional <code>dry_run</code>) and <code>status</code>.</p>
|
||||
32
site/guides/directory.html
Normal file
32
site/guides/directory.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<h1>The directory</h1>
|
||||
<p>The directory gathers the public podcasts that iPX servers' listeners follow, and how many follow each. It never holds a file and never learns who listens.</p>
|
||||
|
||||
<h2>Browsing it</h2>
|
||||
<p>Anyone can browse <a href="https://directory.ipxreader.app">directory.ipxreader.app</a> without signing in: the most followed, the categories, everything A to Z, and a page for each podcast with its latest episodes and its address, to subscribe to in any podcast app.</p>
|
||||
|
||||
<h2>Connecting your iPX</h2>
|
||||
<p>In iPX, an admin opens Admin, Directory and enters the directory's address, <code>https://directory.ipxreader.app</code>, and the token its operator gave your server. Everyone on your server then has the Directory at the top of their list, and subscribes from it in one click.</p>
|
||||
<p>Turn on sharing to add your server's podcasts to it. iPX sends only the podcasts' addresses and how many people on your server follow each, never who. It leaves out anything that looks private: a podcast someone marked Private, one with a login or a key in its address, and paid feeds such as Patreon's.</p>
|
||||
|
||||
<h2>Its API</h2>
|
||||
<p>Anything the pages show, a program can read through the directory's API with a token: see the <a href="/docs/api/directory/">ipx-directory API reference</a>.</p>
|
||||
|
||||
<h2>Running your own</h2>
|
||||
<p>The directory is in the same repository. Build it, give it a database (Postgres or SQLite) and a config file, and make a token for each server that will use it:</p>
|
||||
<pre><code>docker build -f Dockerfile.directory -t ipx-directory .</code></pre>
|
||||
<pre><code>services:
|
||||
ipx-directory:
|
||||
image: ipx-directory
|
||||
container_name: ipx-directory
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DIRECTORY_DATABASE_URL: "postgres://ipx_directory:password@db-host:5432/ipx_directory"
|
||||
ports:
|
||||
- "8100:8100"
|
||||
volumes:
|
||||
- ./directory:/config:ro # directory.toml</code></pre>
|
||||
<pre><code># directory/directory.toml
|
||||
bind = "0.0.0.0:8100"
|
||||
database_url_env = "DIRECTORY_DATABASE_URL"</code></pre>
|
||||
<pre><code>docker exec ipx-directory ipx-directory token new my-server --access rw</code></pre>
|
||||
<p>That prints the token, to give to the server, and the lines to add to <code>directory.toml</code>, which keeps only its SHA-256. The directory reads the file again within a minute, so adding or revoking a token needs no restart. It checks each podcast every six hours.</p>
|
||||
67
site/guides/install.html
Normal file
67
site/guides/install.html
Normal file
@@ -0,0 +1,67 @@
|
||||
<h1>Install ipx and ipx-web</h1>
|
||||
<p class="lede">iPX is two containers: <strong>ipx</strong>, which checks your podcasts, downloads them and answers the API, and <strong>ipx-web</strong>, which serves the page and passes the API on to ipx.</p>
|
||||
<p>You need Docker with Compose. There are no published images yet, so you build both from the source:</p>
|
||||
<pre><code>git clone https://git.sdf1.net/rays/ipx.git
|
||||
cd ipx
|
||||
docker build -t ipx .
|
||||
docker build -f Dockerfile.web -t ipx-web .</code></pre>
|
||||
<p>The repository has a sample <code>docker-compose.yml</code> that runs both, with the database in SQLite and everything ipx keeps in three folders beside the file:</p>
|
||||
<pre><code>services:
|
||||
ipx:
|
||||
image: ipx
|
||||
container_name: ipx
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PUID: "99" # who owns the files it writes
|
||||
PGID: "100"
|
||||
TZ: "UTC"
|
||||
ports:
|
||||
- "6881:6881/tcp" # BitTorrent
|
||||
- "6881:6881/udp"
|
||||
volumes:
|
||||
- ./config:/config # config.toml, written on first start
|
||||
- ./data:/data # the database
|
||||
- ./downloads:/downloads
|
||||
|
||||
ipx-web:
|
||||
image: ipx-web
|
||||
container_name: ipx-web
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
IPX_UPSTREAM: "ipx:8099"
|
||||
ports:
|
||||
- "8099:8080" # the page
|
||||
depends_on:
|
||||
- ipx</code></pre>
|
||||
<pre><code>docker compose up -d</code></pre>
|
||||
<p>ipx has no port of its own: everything reaches it through ipx-web, on port 8099. Open <code>http://<your server>:8099</code> and sign in as <strong>admin</strong> with the password <strong>ipodderx</strong>, then change it:</p>
|
||||
<pre><code>echo -n 'a good password' | docker exec -i ipx ipx user passwd admin</code></pre>
|
||||
<p>The password is read from what you pipe in, so it stays out of your shell's history.</p>
|
||||
|
||||
<h2>Adding people</h2>
|
||||
<p>An admin adds people in Admin, Accounts, or from the command line:</p>
|
||||
<pre><code>echo -n 'their password' | docker exec -i ipx ipx user add sam</code></pre>
|
||||
<p>Each person starts with no podcasts and subscribes to their own. Two people subscribing to the same show cost nothing extra: it is fetched once and each file is kept once.</p>
|
||||
|
||||
<h2>Settings</h2>
|
||||
<p>How often podcasts are checked, how much disk to use, how many new episodes to download and the rest are in Admin on the page, and take effect straight away. <code>/config/config.toml</code> holds only what ipx needs before it can read its database: where downloads go, BitTorrent, and who gets in.</p>
|
||||
<div class="scroll"><table>
|
||||
<thead><tr><th>Environment variable</th><th>What it does</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>IPX_DATABASE_URL</code></td><td>A <code>postgres://user:password@host:5432/database</code> address, to keep everything in Postgres instead of SQLite</td></tr>
|
||||
<tr><td><code>PUID</code>, <code>PGID</code></td><td>The user and group that own the files ipx writes</td></tr>
|
||||
<tr><td><code>IPX_LOG_FORMAT</code></td><td><code>json</code> logs one JSON object a line, for Loki and the like</td></tr>
|
||||
<tr><td><code>OTEL_EXPORTER_OTLP_ENDPOINT</code></td><td>Sends traces of requests, checks and downloads to an OpenTelemetry collector; set <code>WEB_TRACING=on</code> on ipx-web too</td></tr>
|
||||
<tr><td><code>IPX_UPSTREAM</code></td><td>On ipx-web: where ipx is, <code>ipx:8099</code> by default</td></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
|
||||
<h2>Reaching it from outside</h2>
|
||||
<p>The page is plain HTTP. If anyone will reach it from outside your network, put HTTPS in front of it: a reverse proxy you already run, or a Cloudflare tunnel. People can then sign in through Cloudflare Access or Authentik instead of a password; <a href="https://git.sdf1.net/rays/ipx/src/branch/main/docs/sso.md">single sign-on</a> says how.</p>
|
||||
|
||||
<h2>Updating</h2>
|
||||
<pre><code>git pull
|
||||
docker build -t ipx .
|
||||
docker build -f Dockerfile.web -t ipx-web .
|
||||
docker compose up -d</code></pre>
|
||||
<p>Your podcasts, accounts and downloads are in the three folders and are kept.</p>
|
||||
Reference in New Issue
Block a user