chore: rename project from podpuddle to podsteadr
Renames the repo directory and every podpuddle/PODPUDDLE reference across code, config, and docs to podsteadr/PODSTEADR (package names, Docker Compose project/service/volume names, env var names, UI/RSS strings). Existing Docker volume data (uploaded blobs, mediamtx recordings, the server's sqlite DB and its nostr identity key) was migrated to new podsteadr_-prefixed volumes with matching filenames so it isn't orphaned by the rename.
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# Hostname or IP your users will reach this box on (no scheme, no port)
|
# Hostname or IP your users will reach this box on (no scheme, no port)
|
||||||
PUBLIC_HOST=localhost
|
PUBLIC_HOST=localhost
|
||||||
|
|
||||||
# Where the podpuddle UI/API is reachable from browsers
|
# Where the podsteadr UI/API is reachable from browsers
|
||||||
PUBLIC_URL=http://${PUBLIC_HOST}:8095
|
PUBLIC_URL=http://${PUBLIC_HOST}:8095
|
||||||
|
|
||||||
# Public endpoints handed out for streaming / playback / uploads
|
# Public endpoints handed out for streaming / playback / uploads
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# podpuddle
|
# podsteadr
|
||||||
|
|
||||||
Self-hosted, nostr-native podcast publishing and livestreaming.
|
Self-hosted, nostr-native podcast publishing and livestreaming.
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ Then open http://localhost:8095, log in with a NIP-07 extension, and follow the
|
|||||||
|
|
||||||
| Service | Host port | Purpose |
|
| Service | Host port | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| podpuddle | 8095 | Web UI + API + RSS feeds |
|
| podsteadr | 8095 | Web UI + API + RSS feeds |
|
||||||
| MediaMTX | 1935 | RTMP ingest (OBS) |
|
| MediaMTX | 1935 | RTMP ingest (OBS) |
|
||||||
| MediaMTX | 8889 (+8189/udp) | WebRTC/WHIP ingest (browser) |
|
| MediaMTX | 8889 (+8189/udp) | WebRTC/WHIP ingest (browser) |
|
||||||
| MediaMTX | 8890 | HLS playback |
|
| MediaMTX | 8890 | HLS playback |
|
||||||
@@ -54,7 +54,7 @@ Tests: `cd server && npm test`.
|
|||||||
|
|
||||||
## Data
|
## Data
|
||||||
|
|
||||||
Everything lives in named docker volumes: `podpuddle-data` (SQLite, server nostr key,
|
Everything lives in named docker volumes: `podsteadr-data` (SQLite, server nostr key,
|
||||||
covers), `mediamtx-recordings` (stream recordings, 7-day retention), `blossom-data`
|
covers), `mediamtx-recordings` (stream recordings, 7-day retention), `blossom-data`
|
||||||
(media blobs). `docker compose down` keeps them; `down -v` wipes them.
|
(media blobs). `docker compose down` keeps them; `down -v` wipes them.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# blossom-server (v4.x) configuration for podpuddle.
|
# blossom-server (v4.x) configuration for podsteadr.
|
||||||
# Uploads require a signed nostr auth event (BUD-02, kind 24242);
|
# Uploads require a signed nostr auth event (BUD-02, kind 24242);
|
||||||
# reads are public so podcast apps can fetch enclosures.
|
# reads are public so podcast apps can fetch enclosures.
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -1,9 +1,9 @@
|
|||||||
name: podpuddle
|
name: podsteadr
|
||||||
|
|
||||||
services:
|
services:
|
||||||
podpuddle:
|
podsteadr:
|
||||||
build: .
|
build: .
|
||||||
container_name: podpuddle
|
container_name: podsteadr
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8095:8095"
|
- "8095:8095"
|
||||||
@@ -21,7 +21,7 @@ services:
|
|||||||
BLOSSOM_URL_INTERNAL: http://blossom:3000
|
BLOSSOM_URL_INTERNAL: http://blossom:3000
|
||||||
NOSTR_RELAYS: ${NOSTR_RELAYS:-wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band}
|
NOSTR_RELAYS: ${NOSTR_RELAYS:-wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band}
|
||||||
volumes:
|
volumes:
|
||||||
- podpuddle-data:/data
|
- podsteadr-data:/data
|
||||||
- mediamtx-recordings:/recordings:ro
|
- mediamtx-recordings:/recordings:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- mediamtx
|
- mediamtx
|
||||||
@@ -29,7 +29,7 @@ services:
|
|||||||
|
|
||||||
mediamtx:
|
mediamtx:
|
||||||
image: bluenviron/mediamtx:1.19.2
|
image: bluenviron/mediamtx:1.19.2
|
||||||
container_name: podpuddle-mediamtx
|
container_name: podsteadr-mediamtx
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "1935:1935" # RTMP ingest
|
- "1935:1935" # RTMP ingest
|
||||||
@@ -45,7 +45,7 @@ services:
|
|||||||
|
|
||||||
blossom:
|
blossom:
|
||||||
image: ghcr.io/hzrd149/blossom-server:4
|
image: ghcr.io/hzrd149/blossom-server:4
|
||||||
container_name: podpuddle-blossom
|
container_name: podsteadr-blossom
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8098:3000"
|
- "8098:3000"
|
||||||
@@ -54,6 +54,6 @@ services:
|
|||||||
- blossom-data:/app/data
|
- blossom-data:/app/data
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
podpuddle-data:
|
podsteadr-data:
|
||||||
mediamtx-recordings:
|
mediamtx-recordings:
|
||||||
blossom-data:
|
blossom-data:
|
||||||
|
|||||||
+13
-13
@@ -1,4 +1,4 @@
|
|||||||
# podpuddle — project status & handoff
|
# podsteadr — project status & handoff
|
||||||
|
|
||||||
_Last updated: 2026-07-10. State: **working, verified end-to-end, parked.**_
|
_Last updated: 2026-07-10. State: **working, verified end-to-end, parked.**_
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ fully standalone compose stack today.
|
|||||||
## How to resume
|
## How to resume
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd ~/podpuddle
|
cd ~/podsteadr
|
||||||
docker compose up -d # data volumes were kept; everything resumes as-is
|
docker compose up -d # data volumes were kept; everything resumes as-is
|
||||||
node scripts/e2e.mjs # 14 automated checks against the running stack
|
node scripts/e2e.mjs # 14 automated checks against the running stack
|
||||||
```
|
```
|
||||||
@@ -26,14 +26,14 @@ node scripts/e2e.mjs # 14 automated checks against the running stack
|
|||||||
Open http://localhost:8095 and log in with a NIP-07 extension (Alby, nos2x).
|
Open http://localhost:8095 and log in with a NIP-07 extension (Alby, nos2x).
|
||||||
For a non-localhost box, edit `.env` (`PUBLIC_HOST=…`) and `docker compose up -d --build`.
|
For a non-localhost box, edit `.env` (`PUBLIC_HOST=…`) and `docker compose up -d --build`.
|
||||||
|
|
||||||
State that persists in the named volumes: `podpuddle-data` (SQLite DB, the
|
State that persists in the named volumes: `podsteadr-data` (SQLite DB, the
|
||||||
server's nostr key in `config/server-nostr-key`, admin = first pubkey that ever
|
server's nostr key in `config/server-nostr-key`, admin = first pubkey that ever
|
||||||
logged in), `blossom-data` (media blobs), `mediamtx-recordings` (stream
|
logged in), `blossom-data` (media blobs), `mediamtx-recordings` (stream
|
||||||
recordings, 7-day retention). `docker compose down -v` wipes all of it.
|
recordings, 7-day retention). `docker compose down -v` wipes all of it.
|
||||||
|
|
||||||
Note: the automated e2e run left test data behind (an "E2E Test Show" podcast
|
Note: the automated e2e run left test data behind (an "E2E Test Show" podcast
|
||||||
with 2 episodes, test streams, and the e2e identity as **admin**). The e2e
|
with 2 episodes, test streams, and the e2e identity as **admin**). The e2e
|
||||||
identity's secret key is in `/tmp/podpuddle-e2e-key` (hex). For real use you
|
identity's secret key is in `/tmp/podsteadr-e2e-key` (hex). For real use you
|
||||||
probably want a clean slate: `docker compose down -v && docker compose up -d`,
|
probably want a clean slate: `docker compose down -v && docker compose up -d`,
|
||||||
then log in with your own extension first so *your* pubkey becomes admin.
|
then log in with your own extension first so *your* pubkey becomes admin.
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ Three containers on one compose network:
|
|||||||
|
|
||||||
| Container | Image | Host ports | Role |
|
| Container | Image | Host ports | Role |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `podpuddle` | built from `Dockerfile` (node:22 + ffmpeg) | 8095 | Fastify API + built Vue UI + RSS feeds |
|
| `podsteadr` | built from `Dockerfile` (node:22 + ffmpeg) | 8095 | Fastify API + built Vue UI + RSS feeds |
|
||||||
| `podpuddle-mediamtx` | `bluenviron/mediamtx:1.19.2` | 1935 (RTMP), 8889 (WHIP), 8189/udp (ICE), 8890→8888 (HLS) | ingest + HLS output + recording |
|
| `podsteadr-mediamtx` | `bluenviron/mediamtx:1.19.2` | 1935 (RTMP), 8889 (WHIP), 8189/udp (ICE), 8890→8888 (HLS) | ingest + HLS output + recording |
|
||||||
| `podpuddle-blossom` | `ghcr.io/hzrd149/blossom-server:4` (4.4.1) | 8098→3000 | sha256-addressed media blobs |
|
| `podsteadr-blossom` | `ghcr.io/hzrd149/blossom-server:4` (4.4.1) | 8098→3000 | sha256-addressed media blobs |
|
||||||
|
|
||||||
Key flows:
|
Key flows:
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ Key flows:
|
|||||||
directly on any API route (scripting). First pubkey to log in becomes admin.
|
directly on any API route (scripting). First pubkey to log in becomes admin.
|
||||||
- **Episode upload** — browser hashes the file (`crypto.subtle`, ≤2 GB), signs a
|
- **Episode upload** — browser hashes the file (`crypto.subtle`, ≤2 GB), signs a
|
||||||
kind-24242 BUD-02 auth via NIP-07, PUTs **directly to blossom** (file never
|
kind-24242 BUD-02 auth via NIP-07, PUTs **directly to blossom** (file never
|
||||||
transits podpuddle), then registers `{sha256, size, …}` with
|
transits podsteadr), then registers `{sha256, size, …}` with
|
||||||
`POST /api/podcasts/:id/episodes`; server HEAD-verifies the blob before
|
`POST /api/podcasts/:id/episodes`; server HEAD-verifies the blob before
|
||||||
accepting. Enclosure URL = `<blossom>/<sha256>.mp4`.
|
accepting. Enclosure URL = `<blossom>/<sha256>.mp4`.
|
||||||
- **RSS** — `GET /feeds/:podcastId/feed.xml`, RSS 2.0 + itunes + podcast
|
- **RSS** — `GET /feeds/:podcastId/feed.xml`, RSS 2.0 + itunes + podcast
|
||||||
@@ -68,7 +68,7 @@ Key flows:
|
|||||||
travels as `?key=` (OBS stream key) or WHIP `Bearer`, is stored only as a
|
travels as `?key=` (OBS stream key) or WHIP `Bearer`, is stored only as a
|
||||||
sha256 hash, and never appears in viewer URLs. MediaMTX delegates auth to
|
sha256 hash, and never appears in viewer URLs. MediaMTX delegates auth to
|
||||||
`POST /api/mediamtx/auth` (2xx = allow); reads are public.
|
`POST /api/mediamtx/auth` (2xx = allow); reads are public.
|
||||||
- **Status / NIP-53** — podpuddle polls the MediaMTX API (`:9997/v3/paths/list`,
|
- **Status / NIP-53** — podsteadr polls the MediaMTX API (`:9997/v3/paths/list`,
|
||||||
every 3 s) and flips `planned → live → ended` (the stock MediaMTX image has no
|
every 3 s) and flips `planned → live → ended` (the stock MediaMTX image has no
|
||||||
shell, so `runOn*` curl hooks are impossible — polling is deliberate). On each
|
shell, so `runOn*` curl hooks are impossible — polling is deliberate). On each
|
||||||
transition it (re)publishes the kind-30311 live event **signed by the server's
|
transition it (re)publishes the kind-30311 live event **signed by the server's
|
||||||
@@ -76,7 +76,7 @@ Key flows:
|
|||||||
with the user `p`-tagged as `host`, to the relay list in Settings. 30311 is
|
with the user `p`-tagged as `host`, to the relay list in Settings. 30311 is
|
||||||
replaceable, which is why the server key signs rather than the user's.
|
replaceable, which is why the server key signs rather than the user's.
|
||||||
- **Recordings** — MediaMTX records fmp4 segments to the shared volume;
|
- **Recordings** — MediaMTX records fmp4 segments to the shared volume;
|
||||||
podpuddle mounts it read-only, lists per-stream files, and
|
podsteadr mounts it read-only, lists per-stream files, and
|
||||||
`POST /api/streams/:id/recordings/publish` remuxes (`ffmpeg -c copy
|
`POST /api/streams/:id/recordings/publish` remuxes (`ffmpeg -c copy
|
||||||
+faststart`), uploads to blossom **with the server's key** (the only
|
+faststart`), uploads to blossom **with the server's key** (the only
|
||||||
server-signed upload path), and creates the episode.
|
server-signed upload path), and creates the episode.
|
||||||
@@ -112,7 +112,7 @@ frontend/ # Vue 3 + Vite + Tailwind + Pinia
|
|||||||
admin claim, episode blob verification, feed ETag/304, stream key
|
admin claim, episode blob verification, feed ETag/304, stream key
|
||||||
create/verify/rotate, webhook allow/deny paths, settings admin gating).
|
create/verify/rotate, webhook allow/deny paths, settings admin gating).
|
||||||
- `node scripts/e2e.mjs` against the compose stack — **14/14**.
|
- `node scripts/e2e.mjs` against the compose stack — **14/14**.
|
||||||
- Live leg (manual, via ffmpeg in the podpuddle container):
|
- Live leg (manual, via ffmpeg in the podsteadr container):
|
||||||
- RTMP publish with the right key → HLS `index.m3u8` 200 (after MediaMTX's
|
- RTMP publish with the right key → HLS `index.m3u8` 200 (after MediaMTX's
|
||||||
`?cookieCheck=1` redirect — use `curl -L`).
|
`?cookieCheck=1` redirect — use `curl -L`).
|
||||||
- Wrong key → connection reset by MediaMTX (webhook denial confirmed live).
|
- Wrong key → connection reset by MediaMTX (webhook denial confirmed live).
|
||||||
@@ -139,7 +139,7 @@ frontend/ # Vue 3 + Vite + Tailwind + Pinia
|
|||||||
streaming/seeking is degraded. Options later: upgrade to the Deno rewrite,
|
streaming/seeking is degraded. Options later: upgrade to the Deno rewrite,
|
||||||
or front blossom with nginx.
|
or front blossom with nginx.
|
||||||
3. **Split-horizon blossom URL**: browsers use `http://<host>:8098`, but inside
|
3. **Split-horizon blossom URL**: browsers use `http://<host>:8098`, but inside
|
||||||
the podpuddle container that address doesn't exist. `BLOSSOM_URL_INTERNAL`
|
the podsteadr container that address doesn't exist. `BLOSSOM_URL_INTERNAL`
|
||||||
(compose sets `http://blossom:3000`) is used server-side whenever the active
|
(compose sets `http://blossom:3000`) is used server-side whenever the active
|
||||||
blossom URL equals the bundled default; feeds always carry the public URL.
|
blossom URL equals the bundled default; feeds always carry the public URL.
|
||||||
4. **Stock MediaMTX image has no shell** — `runOnReady`-style hooks can't run
|
4. **Stock MediaMTX image has no shell** — `runOnReady`-style hooks can't run
|
||||||
@@ -154,7 +154,7 @@ frontend/ # Vue 3 + Vite + Tailwind + Pinia
|
|||||||
## Remaining work
|
## Remaining work
|
||||||
|
|
||||||
- **Archipelago packaging** (the original deployment target, archy repo):
|
- **Archipelago packaging** (the original deployment target, archy repo):
|
||||||
`apps/podpuddle/manifest.yml` + `apps/podpuddle-mediamtx` + `apps/podpuddle-blossom`
|
`apps/podsteadr/manifest.yml` + `apps/podsteadr-mediamtx` + `apps/podsteadr-blossom`
|
||||||
following the `apps/btcpay-server` (dependencies) + `apps/monero-ui`
|
following the `apps/btcpay-server` (dependencies) + `apps/monero-ui`
|
||||||
(`container.build` on `/opt/archipelago/docker/...`) patterns; bind volumes
|
(`container.build` on `/opt/archipelago/docker/...`) patterns; bind volumes
|
||||||
under `/var/lib/archipelago/<app>`; add ports **8095, 1935, 8889, 8189/udp,
|
under `/var/lib/archipelago/<app>`; add ports **8095, 1935, 8889, 8189/udp,
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>podpuddle</title>
|
<title>podsteadr</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "podpuddle-frontend",
|
"name": "podsteadr-frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "podpuddle-frontend",
|
"name": "podsteadr-frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hls.js": "^1.6.0",
|
"hls.js": "^1.6.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "podpuddle-frontend",
|
"name": "podsteadr-frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ async function logout() {
|
|||||||
<header class="border-b border-slate-200 bg-white">
|
<header class="border-b border-slate-200 bg-white">
|
||||||
<div class="mx-auto flex max-w-4xl items-center justify-between px-4 py-3">
|
<div class="mx-auto flex max-w-4xl items-center justify-between px-4 py-3">
|
||||||
<RouterLink to="/" class="flex items-center gap-2 text-lg font-bold text-puddle-700">
|
<RouterLink to="/" class="flex items-center gap-2 text-lg font-bold text-puddle-700">
|
||||||
<span aria-hidden="true">💧</span> podpuddle
|
<span aria-hidden="true">💧</span> podsteadr
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<nav v-if="auth.pubkey" class="flex items-center gap-4 text-sm">
|
<nav v-if="auth.pubkey" class="flex items-center gap-4 text-sm">
|
||||||
<RouterLink to="/" class="hover:text-puddle-600">Home</RouterLink>
|
<RouterLink to="/" class="hover:text-puddle-600">Home</RouterLink>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Thin fetch wrapper for the podpuddle API (session cookie auth).
|
// Thin fetch wrapper for the podsteadr API (session cookie auth).
|
||||||
|
|
||||||
export class ApiError extends Error {
|
export class ApiError extends Error {
|
||||||
constructor(public status: number, message: string) {
|
constructor(public status: number, message: string) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Browser-direct Blossom (BUD-02) upload: hash locally, sign the kind-24242
|
// Browser-direct Blossom (BUD-02) upload: hash locally, sign the kind-24242
|
||||||
// authorization with the user's NIP-07 extension, PUT straight to the blossom
|
// authorization with the user's NIP-07 extension, PUT straight to the blossom
|
||||||
// server. The file never transits podpuddle.
|
// server. The file never transits podsteadr.
|
||||||
|
|
||||||
import { nip07 } from './nip07';
|
import { nip07 } from './nip07';
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ async function login() {
|
|||||||
<div class="mx-auto max-w-md">
|
<div class="mx-auto max-w-md">
|
||||||
<div class="card text-center">
|
<div class="card text-center">
|
||||||
<div class="mb-2 text-4xl" aria-hidden="true">💧</div>
|
<div class="mb-2 text-4xl" aria-hidden="true">💧</div>
|
||||||
<h1 class="mb-1 text-2xl font-bold">podpuddle</h1>
|
<h1 class="mb-1 text-2xl font-bold">podsteadr</h1>
|
||||||
<p class="mb-6 text-slate-500">Publish podcasts and go live — on nostr, from your own server.</p>
|
<p class="mb-6 text-slate-500">Publish podcasts and go live — on nostr, from your own server.</p>
|
||||||
|
|
||||||
<template v-if="hasNip07()">
|
<template v-if="hasNip07()">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# MediaMTX configuration for podpuddle.
|
# MediaMTX configuration for podsteadr.
|
||||||
# Ingest: RTMP (OBS) + WebRTC/WHIP (browser). Output: HLS. Publish auth is
|
# Ingest: RTMP (OBS) + WebRTC/WHIP (browser). Output: HLS. Publish auth is
|
||||||
# delegated to podpuddle via HTTP; stream status is polled from the API.
|
# delegated to podsteadr via HTTP; stream status is polled from the API.
|
||||||
|
|
||||||
logLevel: info
|
logLevel: info
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ apiAddress: :9997
|
|||||||
|
|
||||||
# ---- authentication ------------------------------------------------------
|
# ---- authentication ------------------------------------------------------
|
||||||
authMethod: http
|
authMethod: http
|
||||||
authHTTPAddress: http://podpuddle:8095/api/mediamtx/auth
|
authHTTPAddress: http://podsteadr:8095/api/mediamtx/auth
|
||||||
authHTTPExclude:
|
authHTTPExclude:
|
||||||
- action: api
|
- action: api
|
||||||
- action: metrics
|
- action: metrics
|
||||||
@@ -45,5 +45,5 @@ pathDefaults:
|
|||||||
|
|
||||||
paths:
|
paths:
|
||||||
# Streams live at live/<streamId>; publish requires the stream secret,
|
# Streams live at live/<streamId>; publish requires the stream secret,
|
||||||
# which podpuddle checks in the auth webhook.
|
# which podsteadr checks in the auth webhook.
|
||||||
"~^live/[A-Za-z0-9]+$": {}
|
"~^live/[A-Za-z0-9]+$": {}
|
||||||
|
|||||||
+3
-3
@@ -10,12 +10,12 @@ import { createHash } from 'node:crypto';
|
|||||||
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
||||||
import { finalizeEvent, generateSecretKey } from '../server/node_modules/nostr-tools/lib/esm/pure.js';
|
import { finalizeEvent, generateSecretKey } from '../server/node_modules/nostr-tools/lib/esm/pure.js';
|
||||||
|
|
||||||
const BASE = process.env.PODPUDDLE_URL ?? 'http://localhost:8095';
|
const BASE = process.env.PODSTEADR_URL ?? 'http://localhost:8095';
|
||||||
const BLOSSOM = process.env.BLOSSOM_URL ?? 'http://localhost:8098';
|
const BLOSSOM = process.env.BLOSSOM_URL ?? 'http://localhost:8098';
|
||||||
|
|
||||||
// Persist the test identity across runs — the first pubkey to log in becomes
|
// Persist the test identity across runs — the first pubkey to log in becomes
|
||||||
// admin, so reruns must reuse it for the settings test to pass.
|
// admin, so reruns must reuse it for the settings test to pass.
|
||||||
const KEY_FILE = '/tmp/podpuddle-e2e-key';
|
const KEY_FILE = '/tmp/podsteadr-e2e-key';
|
||||||
let sk;
|
let sk;
|
||||||
if (existsSync(KEY_FILE)) {
|
if (existsSync(KEY_FILE)) {
|
||||||
sk = Uint8Array.from(Buffer.from(readFileSync(KEY_FILE, 'utf8').trim(), 'hex'));
|
sk = Uint8Array.from(Buffer.from(readFileSync(KEY_FILE, 'utf8').trim(), 'hex'));
|
||||||
@@ -89,7 +89,7 @@ ok('podcast created', podcast.status === 201 && podcast.json.id);
|
|||||||
const podcastId = podcast.json.id;
|
const podcastId = podcast.json.id;
|
||||||
|
|
||||||
// ---- blossom upload (BUD-02, signed like the browser would) ----
|
// ---- blossom upload (BUD-02, signed like the browser would) ----
|
||||||
const media = readFileSync(process.env.TEST_MP4 ?? '/tmp/podpuddle-test.mp4');
|
const media = readFileSync(process.env.TEST_MP4 ?? '/tmp/podsteadr-test.mp4');
|
||||||
const sha256 = createHash('sha256').update(media).digest('hex');
|
const sha256 = createHash('sha256').update(media).digest('hex');
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
const auth24242 = finalizeEvent(
|
const auth24242 = finalizeEvent(
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "podpuddle-server",
|
"name": "podsteadr-server",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "podpuddle-server",
|
"name": "podsteadr-server",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/cookie": "^11.0.2",
|
"@fastify/cookie": "^11.0.2",
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "podpuddle-server",
|
"name": "podsteadr-server",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ function nip98Header(url: string, method: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
dataDir = mkdtempSync(join(tmpdir(), 'podpuddle-test-'));
|
dataDir = mkdtempSync(join(tmpdir(), 'podsteadr-test-'));
|
||||||
const config = loadConfig({
|
const config = loadConfig({
|
||||||
DATA_DIR: dataDir,
|
DATA_DIR: dataDir,
|
||||||
PUBLIC_URL: 'http://localhost:8095',
|
PUBLIC_URL: 'http://localhost:8095',
|
||||||
@@ -59,7 +59,7 @@ describe('auth', () => {
|
|||||||
expect(res.json().pubkey).toBe(pk);
|
expect(res.json().pubkey).toBe(pk);
|
||||||
expect(res.json().isAdmin).toBe(true); // first login claims admin
|
expect(res.json().isAdmin).toBe(true); // first login claims admin
|
||||||
const setCookie = res.headers['set-cookie'] as string;
|
const setCookie = res.headers['set-cookie'] as string;
|
||||||
expect(setCookie).toContain('podpuddle_session=');
|
expect(setCookie).toContain('podsteadr_session=');
|
||||||
cookie = setCookie.split(';')[0];
|
cookie = setCookie.split(';')[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ describe('streams + mediamtx auth webhook', () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/api/streams',
|
url: '/api/streams',
|
||||||
headers: { cookie },
|
headers: { cookie },
|
||||||
payload: { title: 'Live Test', summary: 'hi', hashtags: ['podpuddle'] },
|
payload: { title: 'Live Test', summary: 'hi', hashtags: ['podsteadr'] },
|
||||||
});
|
});
|
||||||
expect(res.statusCode).toBe(201);
|
expect(res.statusCode).toBe(201);
|
||||||
const body = res.json();
|
const body = res.json();
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ export interface BuildAppOptions {
|
|||||||
|
|
||||||
export async function buildApp(opts: BuildAppOptions): Promise<FastifyInstance> {
|
export async function buildApp(opts: BuildAppOptions): Promise<FastifyInstance> {
|
||||||
const { config } = opts;
|
const { config } = opts;
|
||||||
const db = openDatabase(opts.dbPath ?? join(config.DATA_DIR, 'podpuddle.sqlite3'));
|
const db = openDatabase(opts.dbPath ?? join(config.DATA_DIR, 'podsteadr.sqlite3'));
|
||||||
const settings = new SettingsService(db, config);
|
const settings = new SettingsService(db, config);
|
||||||
const serverKey = loadServerKey(config.DATA_DIR);
|
const serverKey = loadServerKey(config.DATA_DIR);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { DB } from '../db/database.js';
|
|||||||
import type { Config } from '../config.js';
|
import type { Config } from '../config.js';
|
||||||
import { Nip98Error, verifyNip98 } from '../services/nip98.js';
|
import { Nip98Error, verifyNip98 } from '../services/nip98.js';
|
||||||
|
|
||||||
export const SESSION_COOKIE = 'podpuddle_session';
|
export const SESSION_COOKIE = 'podsteadr_session';
|
||||||
|
|
||||||
declare module 'fastify' {
|
declare module 'fastify' {
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export function buildFeedXml(podcast: Podcast, episodes: Episode[], ctx: FeedCon
|
|||||||
` <link>${xmlEscape(link)}</link>`,
|
` <link>${xmlEscape(link)}</link>`,
|
||||||
` <description>${xmlEscape(podcast.description)}</description>`,
|
` <description>${xmlEscape(podcast.description)}</description>`,
|
||||||
` <language>${xmlEscape(podcast.language)}</language>`,
|
` <language>${xmlEscape(podcast.language)}</language>`,
|
||||||
` <generator>podpuddle</generator>`,
|
` <generator>podsteadr</generator>`,
|
||||||
` <lastBuildDate>${rfc2822(lastPub)}</lastBuildDate>`,
|
` <lastBuildDate>${rfc2822(lastPub)}</lastBuildDate>`,
|
||||||
` <atom:link href="${xmlEscape(feedUrl)}" rel="self" type="application/rss+xml"/>`,
|
` <atom:link href="${xmlEscape(feedUrl)}" rel="self" type="application/rss+xml"/>`,
|
||||||
` <podcast:guid>${podcast.podcast_guid}</podcast:guid>`,
|
` <podcast:guid>${podcast.podcast_guid}</podcast:guid>`,
|
||||||
|
|||||||
Reference in New Issue
Block a user