openvibers/openvibe.media Commits

The SQLite era is gone (on PostgreSQL since 2026-09-28; no compatibility kept): the one-time migrate-to-postgres script, the *_DB_PATH settings (config, .env.example, systemd unit, test helpers), the unused better-sqlite3 dependency and its lockfile entries, and docs, docstrings and STATUS lines about SQLite or its rollback window. observability.test.js waits up to 30 s for the real server to boot (a cold PGlite boot plus migrations missed the 10 s window on a loaded machine). Reviewed independently (VERDICT: SHIP). npm test 64/65 test files passed, 1 skipped (n-1.test.js: N-1 SQL: skipped (the recorded N-1 releas.

OP OpenVibers committed 29 Sept 2026 · dbdcbeeac5 · 82 files, +122 −180


Changes

ddiff --git a/.env.example b/.env.example
iindex 91d94ce..f636483 100644
---- a/.env.example
++++ b/.env.example
@@@ -9,14 +9,15 @@ NODE_ENV=development
MEDIA_PUBLIC_URL=https://openvibe.media
# PostgreSQL (ADR-035), written by OpenVibe.Host roles/data/add-service.sh media: the runtime role through PgBouncer,
--# the owner on a direct connection (migrations and the one-time import), and Valkey (shared limit counters).
--# Without DATABASE_URL, development uses an embedded PGlite database in data/pglite.
++# the owner on a direct connection (migrations), and Valkey (shared limit counters).
++# Without DATABASE_URL, development uses an embedded PGlite database in data/pglite (MEDIA_PGLITE_DIR overrides it).
DATABASE_URL=
DATABASE_DIRECT_URL=
VALKEY_URL=
VALKEY_PREFIX=
--# The SQLite file of releases before PostgreSQL: read once by scripts/migrate-to-postgres.js.
--DB_PATH=./data/media.db
++# The service's data directory: the reports (duration reconcile, storage orphans, H15 repair) and the drill
++# artifacts are written under it.
++MEDIA_DATA_DIR=./data
# Restore-drill mode, set only by `ovhost drill media` (OpenVibe.Host docs/restore-drills.md): reads
# only from a restored copy, no stored bytes, no jobs, webhooks, events or ffmpeg; refuses to start
ddiff --git a/README.md b/README.md
iindex e4fb36a..bcdf970 100644
---- a/README.md
++++ b/README.md
@@@ -71,13 +71,15 @@ reads events.
```
server/
index.js boot, route mounts, background jobs, graceful shutdown
-- config.js env config (PORT, DB_PATH, *_PATH, MEDIA_B2_*/R2_*, RTP pool, OV_NETWORK_URL)
++ config.js env config (PORT, MEDIA_DATA_DIR, *_PATH, DATABASE_URL/DATABASE_DIRECT_URL, MEDIA_B2_*/R2_*,
++ RTP pool, OV_NETWORK_URL)
auth.js tenancy + auth middleware, JWKS fetch, app seeding
webhooks.js HMAC-signed outbound webhooks
drill.js MEDIA_DRILL: restore-drill mode (reads only, no bytes, no jobs, nothing leaves)
-- db/schema.sql apps, vods, clips, pastes(+likes/comments), files, content_views, media_settings,
-- media_objects/locations/relationships/variants/jobs/holds/invariant_violations
db/database.js PostgreSQL helpers (openvibe-sdk/db, async), all app_id-scoped
++ db/init.js applies migrations/ (DATABASE_URL/DATABASE_DIRECT_URL, or embedded PGlite)
++ migrations/NNNN_*.sql apps, vods, clips, pastes(+likes/comments), files, content_views, media_settings,
++ media_objects/locations/relationships/variants/jobs/holds/invariant_violations
vod/recorder.js ffmpeg recording: RTMP pull + RTP (SDP) ingest, codec passthrough
vod/media-tools.js probes, seekable remux, DVR sidecar, chunk-segment concat
vod/finalize.js finalize pipeline (remux → probe → master recovery → thumbnail → webhook)
@@@ -381,9 +383,8 @@ that is deleted; none for sandbox tenants. `test/object-events.test.js`.
## Restore drills (`MEDIA_DRILL=1`)
A restore drill starts a second Media from this checkout on 127.0.0.1:14100 with `MEDIA_DRILL=1` and
--`DATABASE_URL` naming a restored copy of the database (pgBackRest restores the cluster on a spare port; the Host's
--`ovhost drill media` still restores the SQLite backups of the releases before PostgreSQL and needs that step before
--it drills this one). In that mode (`server/drill.js`) Media:
++`DATABASE_URL` naming a restored copy of the database (pgBackRest restores the cluster on a spare port). In that
++mode (`server/drill.js`) Media:
- refuses to start unless `DATABASE_URL` is set and does not name production's database (`ov_media` on 5432, or
PgBouncer's 6432), `DATABASE_DIRECT_URL` is unset (a drill never migrates), `HOST` is loopback and `PORT` is not
@@@ -573,16 +574,6 @@ guardian (refuses new recordings when free space is critical), stale live-thumb
cleanup, and an on-boot sweep that finalizes recordings orphaned by an unclean
shutdown.
--## Moving to PostgreSQL
--
--`scripts/migrate-to-postgres.js` is the one-time import of the SQLite `media.db` (`DB_PATH`) into `ov_media`
--(openvibe-sdk `runSqliteMigration`: migrations as the owner, every table copied into emptied tables, counts and
--checksums verified; `--pglite` rehearses it in memory). It runs while the service is stopped (OpenVibe.Host
--`roles/data/switch-service.sh media /opt/openvibe.media/data/media.db`); the SQLite file stays read-only for 7 days as
--the rollback. Timestamps stay SQLite's text (`ov_now()`, `datetime()`, `julianday()` in the migration); the retention
--hold guards and the object-change triggers are PL/pgSQL. (The predecessor-DB importer and its bulk-copy recipe were
--retired with the move.)
--
## Not ported
- **Song-request media queue** (`media/` yt-dlp downloader/queue): deeply
@@@ -610,8 +601,9 @@ Run `npm test` for the policy regression tests.
## Acceptance
--`npm test` runs every `test/*.test.js` in turn (temp databases, local stubs, no network). What they
--prove includes: tenancy and developer-project tenants (`app-tenants`, `app-tenant-abuse`); the object
++`npm test` runs every `test/*.test.js` in turn (each process gets a migrated PGlite database, or the
++PostgreSQL containers under `npm run test:pg`; local stubs, no network). What they prove includes: tenancy
++and developer-project tenants (`app-tenants`, `app-tenant-abuse`); the object
model, multipart, reconcile, verification and tiering (`objects-*`, `object-tiering`, `tier-*`); jobs
with leases and fencing (`jobs*`); VOD finalize, durations and storage policy (`vod-*`); private items
stay private and nobody acts on another's ids (`security-private`, `security-idor`,
@@@ -656,5 +648,4 @@ writes its settings); the release migrates it at boot; nginx serves `openvibe.me
fixtures (`npm run n-1:record`).
Rollback: ovhost puts the previous sha back by itself when `/api/ready` does not answer 2xx after the
--restart; afterwards `sudo ovhost rollback media --to <sha>`. A release from before PostgreSQL reads the SQLite
--`media.db` it left (read-only for 7 days after the switch); anything written since would need moving back first.
++restart; afterwards `sudo ovhost rollback media --to <sha>`.
ddiff --git a/docs/object-model.md b/docs/object-model.md
iindex 447392b..a766848 100644
---- a/docs/object-model.md
++++ b/docs/object-model.md
@@@ -76,7 +76,7 @@ the same functions.
## Backfill
```
--node scripts/backfill-objects.js [--dry-run] [--only-missing] [--json] [--out report.json] [--db ./data/media.db]
++node scripts/backfill-objects.js [--dry-run] [--only-missing] [--json] [--out report.json]
```
The backfill creates one object per vod, clip, file, screenshot/avatar paste and thumbnail, with
@@@ -90,12 +90,12 @@ relationships and thumbnail variants.
Skip reasons today: `clips-only recording (ephemeral, never published)`, `screenshot paste without a file path`, `external thumbnail url`.
--The run holds SQLite's write lock for its duration, typically seconds. Prefer the boot backfill, or
++The backfill's own transaction holds row and table locks for its duration, typically seconds. Prefer the boot backfill, or
run the script while traffic is quiet.
### Keeping the model current: object-first writes
--Every write to a projected row writes its object in the same SQLite transaction
++Every write to a projected row writes its object in the same PostgreSQL transaction
(`model.withObject(kind, ids, write)`; roadmap WS-G task 1, which retires compatibility shim C-75,
"write the row, then sync the object"). The row change and the re-projection commit together or
not at all, so a crash or an error between them can no longer leave the object behind its row. A
@@@ -119,7 +119,7 @@ after the outcome event.
| avatar ingest | `avatars/ingest` |
| tier moves | `moveToCold`, `moveToHot`, `promoteToR2`, `demoteFromR2`, sweep, `migrateLegacy`: the copy the move verified is marked `present` in the same transaction |
--**Deletes need nothing more.** A SQLite trigger on `vods`, `clips`, `files` and `pastes` marks the
++**Deletes need nothing more.** A PostgreSQL trigger (defined in `migrations/`, PL/pgSQL) on `vods`, `clips`, `files` and `pastes` marks the
object `deleted` in the same statement whenever its row is deleted, which covers all of the
inherited delete paths.
@@@ -137,7 +137,7 @@ projects what it inserts.
### Drift report
```
--node scripts/object-drift-report.js [--app live] [--limit 20] [--json] [--out report.json] [--db ./data/media.db]
++node scripts/object-drift-report.js [--app live] [--limit 20] [--json] [--out report.json]
```
For every projected row, and each vod/clip thumbnail, the report derives what the object must say
@@@ -183,17 +183,18 @@ Rules for both job and backfill:
request uses `INTERNAL_API_KEY`.
```
--node scripts/backfill-owner-subject.js [--db media.db] [--batch 500] [--json] # dry run: counts per tenant
++node scripts/backfill-owner-subject.js [--batch 500] [--json] # dry run: counts per tenant
node scripts/backfill-owner-subject.js --apply --backup <file.json> [--batch 500] # fill
node scripts/backfill-owner-subject.js --rollback <file.json> [--apply] # undo (dry without --apply)
```
- **The dry run** (the default) opens the database read-only and prints per tenant: objects, already
set, to fill, unresolvable, unsupported tenant and no owner.
--- **`--apply --backup <file.json>`** first takes an online backup to `<file>.media.db` and requires
-- `PRAGMA integrity_check` = `ok` on it. It then writes `<file.json>` (0600), the rows it is about to
-- change, and fills them in `--batch` transactions. Finally it rewrites the file to list exactly the
-- rows it changed. It refuses an existing backup name, and a re-run fills only what is still missing.
++- **`--apply --backup <file.json>`** writes the rollback file `<file.json>` (0600) with the rows it is
++ about to change, and fills them in `--batch` transactions. The database's own safety net is
++ PostgreSQL's point-in-time recovery (pgBackRest), not an online copy. Finally it rewrites the file to
++ list exactly the rows it changed. It refuses an existing backup name, and a re-run fills only what is
++ still missing.
- **`--rollback <file.json> --apply`** sets `owner_subject` back to `NULL` on each listed row that
still carries the subject the backfill wrote for the same owner. Rows changed since are counted and
left alone. Without `--apply` it only reports, which also verifies an applied backfill. Stop the job
@@@ -539,7 +540,7 @@ admin routes' log), with who, the object, the kind and the reason.
## Reconciliation
```
--node scripts/reconcile-objects.js [--verify] [--hash] [--app live] [--json] [--out report.json] [--db ./data/media.db]
++node scripts/reconcile-objects.js [--verify] [--hash] [--app live] [--json] [--out report.json]
```
**Modes.**
@@@ -599,7 +600,7 @@ and nothing is concluded about its keys.
restore drill. On demand:
```
--node scripts/vods-orphans-report.js --storage [--no-remote] [--limit 2000] [--out report.json] [--json] [--db ./data/media.db]
++node scripts/vods-orphans-report.js --storage [--no-remote] [--limit 2000] [--out report.json] [--json]
```
`--no-remote` lists no bucket. The detailed per-object recommendations for the `vods-orphans/` prefix
@@@ -639,7 +640,7 @@ that only moved to B2/R2 keeps it. Test: `test/content-hash.test.js`.
- The report, which lists each one for an operator to decide on:
```
-- node scripts/no-good-copy-report.js [--app live] [--json] [--out report.json] [--db ./data/media.db]
++ node scripts/no-good-copy-report.js [--app live] [--json] [--out report.json]
```
For each object it prints the owner (app, `owner_user_id`, `owner_subject`), `legacy_ref`, the `vods`/`clips`/`files`/`pastes` rows that point at it, its relationships, variants and holds, every location with its state and `verified_at`, and the last verification. The report opens the database read-only. It exits 1 when there are any.
@@@ -662,7 +663,7 @@ A *public playback object* is a `ready` vod or clip whose visibility is `public`
- **Report.**
```
-- node scripts/object-invariant.js [--dry-run] [--app live] [--json] [--db ./data/media.db]
++ node scripts/object-invariant.js [--dry-run] [--app live] [--json]
```
Lists every public playback object above target, largest first, records the rows, and resolves rows that no longer apply. `--dry-run` writes nothing.
@@@ -690,7 +691,7 @@ Code: `server/jobs/`. Tests: `test/jobs.test.js`, `test/jobs-invariant.test.js`,
30 s, 2 min, 8 min, …, at most 1 h) until `max_attempts`; a handler can mark a failure permanent.
**Events.** Every state change and its `media.job.<transition>` event (`proposed`, `queued`,
--`started`, `retrying`, `succeeded`, `failed`, `cancelled`; subject `job <id>`) commit in one SQLite
++`started`, `retrying`, `succeeded`, `failed`, `cancelled`; subject `job <id>`) commit in one PostgreSQL
transaction through Media's outbox, the same rule as the outcome events (`webhooks.announce()`): no
change without its event, no event for a change that rolled back. Progress events are `low`
priority; outcomes and proposals are `important`. Job events go to OpenVibe.Events only, not to app
@@@ -809,7 +810,7 @@ still holds the value that was read, and re-projects the object. Every run write
```
node scripts/vod-duration-reconcile.js [--app live] [--batch 50] [--after <id>] [--all] [--ids 1,2] [--confirm-remote] # dry run
--node scripts/vod-duration-reconcile.js --apply --backup <file.json> [same selection] # checked DB backup first; file = rollback
++node scripts/vod-duration-reconcile.js --apply --backup <file.json> [same selection] # file = the rollback file (every repair's old values)
node scripts/vod-duration-reconcile.js --rollback <file.json> [--apply]
```
ddiff --git a/scripts/h15-repair.js b/scripts/h15-repair.js
iindex a39fe83..01834d9 100644
---- a/scripts/h15-repair.js
++++ b/scripts/h15-repair.js
@@@ -134,7 +134,7 @@ const { execFile } = require('child_process');
}
const record = { generated_at: new Date().toISOString(), applied: false, objects: report.count, plan: steps.map(({ file, ...s }) => s) };
if (!APPLY) {
-- const out = arg('out') ? path.resolve(arg('out')) : path.join(path.dirname(path.resolve(config.db.path)), 'reports', `h15-repair-${record.generated_at.replace(/[:.]/g, '-')}.json`);
++ const out = arg('out') ? path.resolve(arg('out')) : path.join(config.dataDir, 'reports', `h15-repair-${record.generated_at.replace(/[:.]/g, '-')}.json`);
fs.mkdirSync(path.dirname(out), { recursive: true });
fs.writeFileSync(out, JSON.stringify(record, null, 2));
console.log(`\nDry run: nothing changed. Plan written to ${out}`);
ddiff --git a/scripts/migrate-to-postgres.js b/scripts/migrate-to-postgres.js
ddeleted file mode 100755
iindex 8d0aff6..0000000
---- a/scripts/migrate-to-postgres.js
++++ /dev/null
@@@ -1,25 +0,0 @@
--#!/usr/bin/env node
--'use strict';
--/**
-- * The one-time move of Media's SQLite database (DB_PATH) into its PostgreSQL schema (ADR-035; the procedure is
-- * openvibe-sdk docs/migrating-to-postgresql.md, section 6, carried out by openvibe-sdk/db runSqliteMigration).
-- *
-- * node scripts/migrate-to-postgres.js [--sqlite <file>] [--pglite] [--json]
-- *
-- * Applies migrations/ as the owner (DATABASE_DIRECT_URL), copies every table into emptied tables, verifies row counts
-- * and checksums, and exits 1 unless everything verified. The SQLite file is opened read-only. Every table keeps its
-- * name and columns.
-- */
--require('dotenv').config();
--const { runSqliteMigration } = require('openvibe-sdk/db');
--const config = require('../server/config');
--const { MIGRATIONS } = require('../server/db/database');
--
--const TABLES = {};
--
--if (require.main === module) {
-- runSqliteMigration({ service: 'media', sqlite: config.db.path, directUrl: config.db.directUrl, migrations: MIGRATIONS, tables: TABLES })
-- .then((code) => process.exit(code), (err) => { console.error(`migrate-to-postgres failed: ${err.message}`); process.exit(1); });
--}
--
--module.exports = { TABLES };
ddiff --git a/scripts/r2-eviction-drill.js b/scripts/r2-eviction-drill.js
iindex 429a5f9..90a7211 100644
---- a/scripts/r2-eviction-drill.js
++++ b/scripts/r2-eviction-drill.js
@@@ -221,7 +221,7 @@ if (require.main === module) {
await db.initDb(); // PostgreSQL (DATABASE_URL), as the service
await storage.tierConfig.init(storage.DEFAULTS, { log: { info() {}, warn: (m) => console.error(`[Tiers] ${m}`), error: (m) => console.error(`[Tiers] ${m}`) } });
const art = await runDrill(opts, { storage, db, model });
-- const out = opts.out || path.join(path.dirname(config.db.path), 'drills', `r2-eviction-${art.vod_id ?? 'none'}-${art.started_at.replace(/[:.]/g, '-')}.json`);
++ const out = opts.out || path.join(config.dataDir, 'drills', `r2-eviction-${art.vod_id ?? 'none'}-${art.started_at.replace(/[:.]/g, '-')}.json`);
fs.mkdirSync(path.dirname(out), { recursive: true });
fs.writeFileSync(out, JSON.stringify(art, null, 2));
console.log(opts.json ? JSON.stringify(art, null, 2) : `${summarize(art)}\nartifact: ${out}`);
ddiff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh
iindex 78d3b8c..47fef50 100755
---- a/scripts/smoke-test.sh
++++ b/scripts/smoke-test.sh
@@@ -37,7 +37,7 @@ ffmpeg -y -f lavfi -i "testsrc=duration=5:size=640x360:rate=30" \
echo "-- booting server on :$PORT (temp data dir)"
SEED="[{\"app_id\":\"live\",\"name\":\"Live\",\"api_key\":\"$LIVE_KEY\",\"allowed_origins\":[\"http://localhost:3000\"]},{\"app_id\":\"games\",\"name\":\"Games\",\"api_key\":\"$GAMES_KEY\",\"quota_bytes\":10485760}]"
env PORT="$PORT" HOST=127.0.0.1 \
-- DB_PATH="$WORK/data/media.db" \
++ MEDIA_DATA_DIR="$WORK/data" \
VOD_PATH="$WORK/data/vods" CLIPS_PATH="$WORK/data/clips" \
PASTES_PATH="$WORK/data/pastes" THUMBNAILS_PATH="$WORK/data/thumbnails" \
FILES_PATH="$WORK/data/files" \
ddiff --git a/scripts/vods-orphans-report.js b/scripts/vods-orphans-report.js
iindex 77a35e6..7a3504b 100755
---- a/scripts/vods-orphans-report.js
++++ b/scripts/vods-orphans-report.js
@@@ -70,7 +70,7 @@ const path = require('path');
if (has('storage')) { await storageReport(); await db.close(); process.exit(0); }
if (!vodStorage.providerConfigured(provider)) usage(`${provider} is not configured (MEDIA_${provider.toUpperCase()}_* not in the environment)`);
const report = await orphans.buildReport({ provider, prefix: arg('prefix') || orphans.PREFIX });
-- const file = arg('out') ? path.resolve(arg('out')) : path.join(path.dirname(config.db.path), 'reports', `vods-orphans-${report.generated_at.replace(/[:.]/g, '-')}.json`);
++ const file = arg('out') ? path.resolve(arg('out')) : path.join(config.dataDir, 'reports', `vods-orphans-${report.generated_at.replace(/[:.]/g, '-')}.json`);
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, JSON.stringify(report, null, 1));
log(`${report.provider}://${report.bucket}/${report.prefix}: ${report.totals.objects} object(s), ${report.totals.gb} GB (read-only report)`);
ddiff --git a/server/admin/routes.js b/server/admin/routes.js
iindex 77ecebc..ddd66b3 100644
---- a/server/admin/routes.js
++++ b/server/admin/routes.js
@@@ -113,8 +113,9 @@ router.get('/', async (req, res) => {
];
const breakdown = directories.map(d => ({ name: d.name, ...dirStatsRecursive(d.path) }));
++ // The store's size (PostgreSQL; the server answers 0 when it does not report one).
let dbBytes = 0;
-- try { dbBytes = fs.statSync(config.db.path).size; } catch { /* */ }
++ try { dbBytes = Number((await db.get('SELECT pg_database_size(current_database()) AS bytes')).bytes) || 0; } catch { /* */ }
// App-scoped DB stats
const vodStats = await db.get(`
ddiff --git a/server/config.js b/server/config.js
iindex 066d2d4..31ce4b2 100644
---- a/server/config.js
++++ b/server/config.js
@@@ -21,13 +21,13 @@ const config = {
publicUrl: (process.env.MEDIA_PUBLIC_URL || 'https://openvibe.media').replace(/\/$/, ''),
// PostgreSQL (ADR-035): DATABASE_URL serves (PgBouncer), DATABASE_DIRECT_URL migrates (owner). Without them,
-- // development uses an embedded PGlite database in data/pglite. path: the SQLite file of releases before
-- // PostgreSQL, read once by scripts/migrate-to-postgres.js.
++ // development uses an embedded PGlite database in data/pglite (MEDIA_PGLITE_DIR overrides it).
db: {
url: process.env.DATABASE_URL || '',
directUrl: process.env.DATABASE_DIRECT_URL || '',
-- path: process.env.DB_PATH || './data/media.db',
},
++ // The service's data directory (MEDIA_DATA_DIR): reports and drills are written under it.
++ dataDir: process.env.MEDIA_DATA_DIR || './data',
// Valkey (ADR-035): per-actor limit counters shared across processes; without it they count in this process.
valkey: { url: process.env.VALKEY_URL || '', prefix: process.env.VALKEY_PREFIX || 'ov:media:' },
@@@ -155,6 +155,6 @@ config.assets.path = path.resolve(config.assets.path);
config.thumbnails.path = path.resolve(config.thumbnails.path);
config.files.path = path.resolve(config.files.path);
config.objects.path = path.resolve(config.objects.path);
--config.db.path = path.resolve(config.db.path);
++config.dataDir = path.resolve(config.dataDir);
module.exports = config;
ddiff --git a/server/db/database.js b/server/db/database.js
iindex 8c56bab..f09ca36 100644
---- a/server/db/database.js
++++ b/server/db/database.js
@@@ -22,7 +22,8 @@ let database = null;
* The serving handle (ADR-035): DATABASE_URL through PgBouncer; in development without it, an embedded PGlite database
* in data/pglite (MEDIA_PGLITE_DIR overrides it). Migrations run first, as the owner (DATABASE_DIRECT_URL), or on the
* embedded handle. The schema, its triggers (retention holds, object deletes, object change events) and the default
-- * settings are migrations/NNNN_*.sql; timestamps stay SQLite's text (ov_now(), datetime() in the migration).
++ * settings are migrations/NNNN_*.sql; timestamps stay text in the `YYYY-MM-DD HH:MM:SS` UTC shape (ov_now(), datetime()
++ * in the migration).
*/
async function openDb(cfg = config, { log = console, registry } = {}) {
if (!cfg.db.url) {
@@@ -265,7 +266,7 @@ const LIST_ORDERS = {
function _listOrder(order) { return LIST_ORDERS[order] || LIST_ORDERS.newest; }
/**
-- * A list's `since` filter as an SQLite datetime ('YYYY-MM-DD HH:MM:SS', UTC), or null. Accepts that
++ * A list's `since` filter as a text datetime ('YYYY-MM-DD HH:MM:SS', UTC), or null. Accepts that
* form or anything Date.parse reads (ISO 8601); anything else is ignored rather than refused.
*/
function sinceParam(value) {
ddiff --git a/server/drill.js b/server/drill.js
iindex 5e6c327..eb2e13d 100644
---- a/server/drill.js
++++ b/server/drill.js
@@@ -40,7 +40,7 @@ function parse(value) {
const enabled = parse(process.env.MEDIA_DRILL);
const REPO_ROOT = path.resolve(__dirname, '..');
--/** Where production runs (OpenVibe.Host inventory: repo /opt/openvibe.media, database data/media.db). */
++/** Where production runs (OpenVibe.Host inventory: repo /opt/openvibe.media, database ov_media). */
const PRODUCTION_ROOT = '/opt/openvibe.media';
const PRODUCTION_PORT = 4100;
// Production's database: ov_media on the host's PostgreSQL (5432) or through PgBouncer (6432).
ddiff --git a/server/events.js b/server/events.js
iindex eeb9830..c45c9c9 100644
---- a/server/events.js
++++ b/server/events.js
@@@ -16,7 +16,7 @@
* (subject object <id>; Events only; payload object id, tenant,
* kind, legacy_ref and the times/visibilities, nothing else)
*
-- * The outbox row is written INSIDE the SQLite transaction that makes the state change it describes
++ * The outbox row is written INSIDE the PostgreSQL transaction that makes the state change it describes
* (record(), called from webhooks.announce()): the event exists if and only if the change committed,
* so a crash can neither lose an outcome nor announce one that rolled back. The webhook for the same
* outcome is sent after the commit and carries the envelope's event_id, so a consumer receiving it by
ddiff --git a/server/index.js b/server/index.js
iindex fd3dc49..a02ff81 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -10,7 +10,7 @@
// Restore-drill mode (MEDIA_DRILL=1, `ovhost drill media`): refuse an unsafe environment before the
// database is opened, then cut every way out of the process but its own HTTP port. The instance
--// serves reads from a restored copy of media.db, never opens a stored file and starts nothing else
++// serves reads from a restored copy of the database, never opens a stored file and starts nothing else
// (server/drill.js).
require('dotenv').config();
const drill = require('./drill');
@@@ -309,7 +309,7 @@ const ready = (async () => {
}
if (drill.enabled) { console.log(`[Drill] Ready: http://${config.host}:${config.port} (reads only)`); return; }
console.log(`[Media] OpenVibe.Media listening on ${config.host}:${config.port} (${config.nodeEnv})`);
-- console.log(`[Media] Data: db=${config.db.path} vods=${config.vod.path} clips=${config.vod.clipsPath}`);
++ console.log(`[Media] Data: db=${config.db.url ? 'postgresql' : 'pglite'} vods=${config.vod.path} clips=${config.vod.clipsPath}`);
console.log(`[Media] RTP ingest pool: udp ${config.rtp.portMin}-${config.rtp.portMax} (127.0.0.1)`);
// Durable events (roadmap Wave 3): webhook outcomes also go to OpenVibe.Events. Off without EVENTS_URL.
try { require('./events').init(); } catch (err) { console.warn('[Events] not started:', err.message); }
ddiff --git a/server/jobs/queue.js b/server/jobs/queue.js
iindex a42b01b..da35be7 100644
---- a/server/jobs/queue.js
++++ b/server/jobs/queue.js
@@@ -8,7 +8,7 @@
* running held by the worker (lease_until renewed while it runs; lease_token names the claim)
* succeeded | failed | cancelled finished
*
-- * Every state change and its media.job.<transition> event commit in ONE SQLite transaction (the same
++ * Every state change and its media.job.<transition> event commit in ONE PostgreSQL transaction (the same
* rule as webhooks.announce(), see events.js): an event exists if and only if its change committed.
* The relay is woken after the commit. Job events go to OpenVibe.Events only (no app webhook).
*
@@@ -133,7 +133,7 @@ function jobPublic(row) {
};
}
--/** A SQLite UTC time ('YYYY-MM-DD HH:MM:SS') as ISO 8601 UTC ('…T…Z'), or null. */
++/** An ISO 8601 UTC ('…T…Z') time from a `YYYY-MM-DD HH:MM:SS` (UTC) text timestamp, as the migrations store them, or null. */
function isoTime(v) {
if (v == null || v === '') return null;
const s = String(v);
@@@ -247,7 +247,7 @@ async function enqueue({ appId, type, objectId = null, params = {}, status = 'qu
let out = null;
const id = `mjob_${ids.ulid()}`;
await db.getDb().tx(async () => {
-- // One enqueue per tenant at a time (SQLite's one writer did this): the key, the identical active job and the
++ // One enqueue per tenant at a time (an advisory lock per tenant serialises them): the key, the identical active job and the
// active count below are read with every earlier enqueue of the tenant committed.
await db.get('SELECT pg_advisory_xact_lock(hashtext(?)) AS locked', [`media.jobs:${appId}`]);
if (key) {
ddiff --git a/server/me/explorer.js b/server/me/explorer.js
iindex ac3b0ad..838543e 100644
---- a/server/me/explorer.js
++++ b/server/me/explorer.js
@@@ -31,7 +31,7 @@ const STATUS_FILTERS = [...model.LIFECYCLES, 'all'];
/** Relations whose `from` object was made out of the `to` object (a clip is its own work, not a derivative). */
const DERIVED_RELATIONS = ['derived_from', 'thumbnail_of'];
--/** SQLite's "YYYY-MM-DD HH:MM:SS" (UTC) or ISO → ISO 8601, or null. */
++/** ISO 8601 from a "YYYY-MM-DD HH:MM:SS" (UTC) text timestamp, as the migrations store them, or from ISO, or null. */
function iso(v) {
if (!v) return null;
const s = String(v);
ddiff --git a/server/objects/drift.js b/server/objects/drift.js
iindex 64a7d72..e9c4c1e 100644
---- a/server/objects/drift.js
++++ b/server/objects/drift.js
@@@ -37,7 +37,7 @@ function expectedFields(p) {
};
}
--// SQLite hands integers back as numbers and TEXT as strings; an app-local user id may be either.
++// The store hands bigint back as a number or a string and TEXT as a string; an app-local user id may be either.
const same = (a, b) => (a == null ? '' : String(a)) === (b == null ? '' : String(b));
/** Every row of `table` (optionally one tenant's) in key order, a page at a time. */
ddiff --git a/server/objects/h15-repair.js b/server/objects/h15-repair.js
iindex 2b43a6c..8e463cc 100644
---- a/server/objects/h15-repair.js
++++ b/server/objects/h15-repair.js
@@@ -77,7 +77,7 @@ function plan(item, probes = {}, facts = {}) {
}
/**
-- * Apply one planned change. h = better-sqlite3 handle. facts: { size, sha256 } of the good bytes (rebaseline,
++ * Apply one planned change. h = database handle. facts: { size, sha256 } of the good bytes (rebaseline,
* regenerate). → the change with old values, or { skipped } when the row moved on.
*/
async function apply(h, step, facts = {}) {
ddiff --git a/server/objects/model.js b/server/objects/model.js
iindex 37675f9..620ec72 100644
---- a/server/objects/model.js
++++ b/server/objects/model.js
@@@ -49,7 +49,7 @@ function parseJson(s, fallback) {
try { return JSON.parse(s); } catch { return fallback; }
}
--/** SQLite 'YYYY-MM-DD HH:MM:SS' (UTC) → epoch ms, so backfilled ids sort by original creation time. */
++/** Epoch ms from a `YYYY-MM-DD HH:MM:SS` (UTC) text timestamp, as the migrations store them, so backfilled ids sort by original creation time. */
function toMs(createdAt) {
const ms = Date.parse(String(createdAt || '').replace(' ', 'T') + (/[zZ]|[+-]\d\d:?\d\d$/.test(String(createdAt || '')) ? '' : 'Z'));
return Number.isFinite(ms) ? ms : Date.now();
ddiff --git a/server/objects/namespaces.js b/server/objects/namespaces.js
iindex 4ef6870..c5c1ecf 100644
---- a/server/objects/namespaces.js
++++ b/server/objects/namespaces.js
@@@ -177,7 +177,7 @@ function limitsOf(app, row) {
*/
/**
* Inside a transaction, the tenant's quota lock (held to commit): a check and the row it admits commit before the next
-- * check of the same tenant reads usage, as SQLite's one writer made them (ADR-035).
++ * check of the same tenant reads usage, as the tenant's quota lock makes them (ADR-035).
*/
async function lockQuota(appId) {
if (db.getDb().inTransaction()) await db.get('SELECT pg_advisory_xact_lock(hashtext(?)) AS locked', [`media.quota:${appId}`]);
ddiff --git a/server/objects/owner-subject.js b/server/objects/owner-subject.js
iindex 09014d9..157d2e6 100644
---- a/server/objects/owner-subject.js
++++ b/server/objects/owner-subject.js
@@@ -128,8 +128,8 @@ function createResolver({
// ── Reads ────────────────────────────────────────────────────
/** Per-tenant ownership counts (read-only). */
--async function summary(sqlite) {
-- const rows = await sqlite.prepare(`
++async function summary(db) {
++ const rows = await db.prepare(`
SELECT app_id, count(*) AS objects,
sum(owner_subject IS NOT NULL) AS already_set,
sum(owner_subject IS NULL AND owner_user_id IS NOT NULL) AS missing,
@@@ -141,8 +141,8 @@ async function summary(sqlite) {
}
/** Distinct (owner_app, owner_user_id) pairs that still lack a subject, with their object counts. */
--async function pendingOwners(sqlite) {
-- return await sqlite.prepare(`
++async function pendingOwners(db) {
++ return await db.prepare(`
SELECT COALESCE(owner_app, app_id) AS owner_app, owner_user_id, count(*) AS objects
FROM media_objects WHERE owner_subject IS NULL AND owner_user_id IS NOT NULL
GROUP BY COALESCE(owner_app, app_id), owner_user_id ORDER BY 1, 2`).all();
@@@ -177,8 +177,8 @@ async function resolveOwners(owners, { resolver }) {
* The rows the resolved subjects fill, read in id order `batch` at a time.
* -> [{ id, app_id, owner_app, owner_user_id, owner_subject }]
*/
--async function planChanges(sqlite, subjects, { batch = DEFAULT_BATCH } = {}) {
-- const page = sqlite.prepare(`
++async function planChanges(db, subjects, { batch = DEFAULT_BATCH } = {}) {
++ const page = db.prepare(`
SELECT id, app_id, COALESCE(owner_app, app_id) AS owner_app, owner_user_id FROM media_objects
WHERE owner_subject IS NULL AND owner_user_id IS NOT NULL AND id > ? ORDER BY id LIMIT ?`);
const changes = [];
@@@ -201,15 +201,15 @@ async function planChanges(sqlite, subjects, { batch = DEFAULT_BATCH } = {}) {
* Fill owner_subject on the planned rows, one transaction per `batch` rows. A row is written only if
* it still has no subject and the same owner. -> { applied: [change], skipped: [change], batches }
*/
--async function applyChanges(sqlite, changes, { batch = DEFAULT_BATCH, onBatch = null } = {}) {
-- const set = sqlite.prepare(`
++async function applyChanges(db, changes, { batch = DEFAULT_BATCH, onBatch = null } = {}) {
++ const set = db.prepare(`
UPDATE media_objects SET owner_subject = ?, updated_at = ov_now()
WHERE id = ? AND owner_subject IS NULL AND COALESCE(owner_app, app_id) = ? AND owner_user_id = ?`);
const applied = [], skipped = [];
let batches = 0;
for (let i = 0; i < changes.length; i += batch) {
const slice = changes.slice(i, i + batch);
-- await sqlite.tx(async () => {
++ await db.tx(async () => {
for (const c of slice) ((await set.run(c.owner_subject, c.id, c.owner_app, c.owner_user_id)).changes ? applied : skipped).push(c);
});
batches++;
@@@ -224,16 +224,16 @@ async function applyChanges(sqlite, changes, { batch = DEFAULT_BATCH, onBatch =
* alone and counted. With apply=false nothing is written.
* -> { restored, changed_since, missing, rows: { restored: [id], changed_since: [id], missing: [id] } }
*/
--async function rollbackChanges(sqlite, entries, { apply = false, batch = DEFAULT_BATCH } = {}) {
-- const get = sqlite.prepare('SELECT id, owner_subject, COALESCE(owner_app, app_id) AS owner_app, owner_user_id FROM media_objects WHERE id = ?');
-- const clear = sqlite.prepare(`
++async function rollbackChanges(db, entries, { apply = false, batch = DEFAULT_BATCH } = {}) {
++ const get = db.prepare('SELECT id, owner_subject, COALESCE(owner_app, app_id) AS owner_app, owner_user_id FROM media_objects WHERE id = ?');
++ const clear = db.prepare(`
UPDATE media_objects SET owner_subject = NULL, updated_at = ov_now()
WHERE id = ? AND owner_subject = ? AND COALESCE(owner_app, app_id) = ? AND owner_user_id = ?`);
const rows = { restored: [], changed_since: [], missing: [] };
const unchanged = (row, e) => row.owner_subject === e.owner_subject && row.owner_app === e.owner_app && Number(row.owner_user_id) === Number(e.owner_user_id);
for (let i = 0; i < entries.length; i += batch) {
const slice = entries.slice(i, i + batch);
-- await sqlite.tx(async () => {
++ await db.tx(async () => {
for (const e of slice) {
const row = await get.get(e.id);
if (!row) { rows.missing.push(e.id); continue; }
@@@ -250,8 +250,8 @@ async function rollbackChanges(sqlite, entries, { apply = false, batch = DEFAULT
* One reconcile pass (the service's job): resolve every owner still lacking a subject and fill their
* objects. -> { owners, filled, skipped, unresolvable, unsupported, via }
*/
--async function reconcileOnce(sqlite, { resolver, batch = DEFAULT_BATCH } = {}) {
-- const owners = await pendingOwners(sqlite);
++async function reconcileOnce(db, { resolver, batch = DEFAULT_BATCH } = {}) {
++ const owners = await pendingOwners(db);
const out = { owners: owners.length, filled: 0, skipped: 0, unresolvable: 0, unsupported: 0, via: null };
if (!owners.length) return out;
const callsBefore = resolver.state ? resolver.state.calls : 0;
@@@ -260,7 +260,7 @@ async function reconcileOnce(sqlite, { resolver, batch = DEFAULT_BATCH } = {}) {
out.unsupported = r.unsupported.reduce((n, o) => n + o.objects, 0);
out.via = resolver.state && resolver.state.calls > callsBefore ? resolver.state.via : null;
if (!r.subjects.size) return out;
-- const applied = await applyChanges(sqlite, await planChanges(sqlite, r.subjects, { batch }), { batch });
++ const applied = await applyChanges(db, await planChanges(db, r.subjects, { batch }), { batch });
out.filled = applied.applied.length;
out.skipped = applied.skipped.length;
return out;
ddiff --git a/server/objects/tiering.js b/server/objects/tiering.js
iindex 18cd64f..9f2e046 100644
---- a/server/objects/tiering.js
++++ b/server/objects/tiering.js
@@@ -56,7 +56,7 @@ function sha256File(p) {
});
}
--/** SQLite 'YYYY-MM-DD HH:MM:SS' (UTC) or ISO → epoch ms (NaN when unreadable). */
++/** Epoch ms from a `YYYY-MM-DD HH:MM:SS` (UTC) text timestamp, as the migrations store them, or ISO (NaN when unreadable). */
function msOf(t) {
const s = String(t || '');
return Date.parse(/[zZ]|[+-]\d\d:?\d\d$/.test(s) ? s : `${s.replace(' ', 'T')}Z`);
ddiff --git a/server/observability.js b/server/observability.js
iindex c246662..1fe0317 100644
---- a/server/observability.js
++++ b/server/observability.js
@@@ -5,7 +5,7 @@
* GET /metrics Prometheus text; direct loopback callers only (404 through nginx)
* GET /api/ready named checks; 503 only when a required one fails, optional failures → degraded
*
-- * Required (Media cannot serve without them): the SQLite database answers a real query, and every
++ * Required (Media cannot serve without them): the PostgreSQL database answers a real query, and every
* local storage directory accepts a write. Optional (one capability degrades, the rest keeps
* working): the Network public key (user-JWT and service-token routes; app-key tenants are
* unaffected), each CONFIGURED remote tier (B2 cold storage, R2 cache; a HeadBucket at most once a
ddiff --git a/server/public/page-frame.js b/server/public/page-frame.js
iindex 61510af..483f33d 100644
---- a/server/public/page-frame.js
++++ b/server/public/page-frame.js
@@@ -39,7 +39,7 @@ const snip = (s, n = 200) => { const t = String(s || '').replace(/\s+/g, ' ').tr
/** JSON that is safe inside a <script>: no `</` can end the element early. */
const jsonForScript = (v) => JSON.stringify(v).replace(/</g, '\\u003c');
--/** SQLite's "YYYY-MM-DD HH:MM:SS" (UTC) → ISO 8601, or null. */
++/** ISO 8601 from a "YYYY-MM-DD HH:MM:SS" (UTC) text timestamp, as the migrations store them, or null. */
function isoDate(dt) {
if (!dt) return null;
const d = new Date(String(dt).includes('T') ? dt : `${dt}Z`.replace(' ', 'T'));
ddiff --git a/server/vod/duration-reconcile.js b/server/vod/duration-reconcile.js
iindex 83589e0..b1c1f10 100644
---- a/server/vod/duration-reconcile.js
++++ b/server/vod/duration-reconcile.js
@@@ -108,7 +108,7 @@ async function measure(vod, { confirm = false, confirmRemote = false, probeTimeo
}
function reportsDir() {
-- return path.join(path.dirname(config.db.path), 'reports');
++ return path.join(config.dataDir, 'reports');
}
function writeReport(report, file = null) {
ddiff --git a/server/vod/orphans-report.js b/server/vod/orphans-report.js
iindex 76f891a..2f3be8c 100644
---- a/server/vod/orphans-report.js
++++ b/server/vod/orphans-report.js
@@@ -438,7 +438,7 @@ async function buildStorageReport({ providers = null, list = null, listUploads =
}
function reportsDir() {
-- return path.join(path.dirname(config.db.path), 'reports');
++ return path.join(config.dataDir, 'reports');
}
/** Write a storage report as JSON (default <data>/reports/storage-orphans-<time>.json). Returns the path. */
ddiff --git a/server/webhooks.js b/server/webhooks.js
iindex 5a4e9fa..5be7d8f 100644
---- a/server/webhooks.js
++++ b/server/webhooks.js
@@@ -9,7 +9,7 @@
* storage.alert | storage.recovered
*
* Outcomes go through announce(): the state change and its durable OpenVibe.Events outbox row
-- * commit in one SQLite transaction, then the webhook is sent with `event_id` = that event's id
++ * commit in one PostgreSQL transaction, then the webhook is sent with `event_id` = that event's id
* (absent when the outbox is off), so a consumer that also reads Events dedupes the pair.
*/
'use strict';
@@@ -79,7 +79,7 @@ async function sendWebhook(appOrId, event, data, { eventId = null } = {}) {
/**
* Commit an outcome and announce it. `change()` (synchronous DB writes, optional) and the outbox
-- * row for `event` run in ONE SQLite transaction, so the durable event exists if and only if the
++ * row for `event` run in ONE PostgreSQL transaction, so the durable event exists if and only if the
* change committed: never lost after a commit, never announced for a rollback. `payload()` is read
* inside that transaction (the row as committed). After the commit the relay is woken and the
* app's webhook is sent with the same event_id. Returns { data, eventId }; throws (nothing changed,
ddiff --git a/test/account-data.test.js b/test/account-data.test.js
iindex ef4c43e..777fee1 100644
---- a/test/account-data.test.js
++++ b/test/account-data.test.js
@@@ -13,7 +13,6 @@ const path = require('path');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-account-data-'));
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.MEDIA_INBOUND_EVENTS_SECRET = 'm'.repeat(40);
const log = console.log;
console.log = (...a) => { if (!/^\[/.test(String(a[0]))) log(...a); };
ddiff --git a/test/actor-limits.test.js b/test/actor-limits.test.js
iindex 3c329f3..bf7602e 100644
---- a/test/actor-limits.test.js
++++ b/test/actor-limits.test.js
@@@ -13,7 +13,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-limits-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
ddiff --git a/test/app-tenant-abuse.test.js b/test/app-tenant-abuse.test.js
iindex 6989e63..5986b1c 100644
---- a/test/app-tenant-abuse.test.js
++++ b/test/app-tenant-abuse.test.js
@@@ -16,7 +16,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-tenantabuse-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
ddiff --git a/test/app-tenants.test.js b/test/app-tenants.test.js
iindex d3c0100..fae032b 100644
---- a/test/app-tenants.test.js
++++ b/test/app-tenants.test.js
@@@ -14,7 +14,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-apptenants-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
ddiff --git a/test/clip-cut-job.test.js b/test/clip-cut-job.test.js
iindex e86111d..9316f7a 100644
---- a/test/clip-cut-job.test.js
++++ b/test/clip-cut-job.test.js
@@@ -12,7 +12,6 @@ const { spawnSync } = require('child_process');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-clipjob-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/clip-origin-filters.test.js b/test/clip-origin-filters.test.js
iindex 8bdcf51..5a8d26c 100644
---- a/test/clip-origin-filters.test.js
++++ b/test/clip-origin-filters.test.js
@@@ -16,7 +16,6 @@ const http = require('http');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-clip-origin-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/content-hash.test.js b/test/content-hash.test.js
iindex 40c1ee7..7aa9639 100644
---- a/test/content-hash.test.js
++++ b/test/content-hash.test.js
@@@ -14,7 +14,7 @@ const crypto = require('crypto');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-hash-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
MEDIA_INVARIANT_SCAN_HOURS: '0', MEDIA_HASH_SETTLE_S: '60', MEDIA_HASH_INTERVAL_MIN: '15',
});
ddiff --git a/test/crawl.test.js b/test/crawl.test.js
iindex 37648d8..fb4c036 100644
---- a/test/crawl.test.js
++++ b/test/crawl.test.js
@@@ -13,7 +13,7 @@ const express = require('express');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-crawl-'));
-- Object.assign(process.env, { DB_PATH: path.join(tmp, 'media.db'), MEDIA_PUBLIC_URL: 'https://media.test', VOD_PATH: path.join(tmp, 'vods'), CLIPS_PATH: path.join(tmp, 'clips') });
++ Object.assign(process.env, { MEDIA_PUBLIC_URL: 'https://media.test', VOD_PATH: path.join(tmp, 'vods'), CLIPS_PATH: path.join(tmp, 'clips') });
const db = require('../server/db/database');
const pages = require('../server/public/pages');
ddiff --git a/test/db-open-keeps-clips.test.js b/test/db-open-keeps-clips.test.js
iindex 71780f9..c99e7a4 100644
---- a/test/db-open-keeps-clips.test.js
++++ b/test/db-open-keeps-clips.test.js
@@@ -12,7 +12,6 @@ const path = require('path');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-dbopen-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
for (const [k, n] of [['VOD_PATH', 'vods'], ['CLIPS_PATH', 'clips'], ['FILES_PATH', 'files'], ['THUMBNAILS_PATH', 'thumbnails'], ['PASTES_PATH', 'pastes'], ['OBJECTS_PATH', 'objects']]) process.env[k] = dir(n);
process.env.MEDIA_PUBLIC_URL = 'https://media.test';
console.log = () => {};
ddiff --git a/test/duration-reconcile.test.js b/test/duration-reconcile.test.js
iindex 66acb19..36be894 100644
---- a/test/duration-reconcile.test.js
++++ b/test/duration-reconcile.test.js
@@@ -61,7 +61,7 @@ const { spawnSync, execFile } = require('child_process');
await new Promise((r) => s3.listen(0, '127.0.0.1', r));
const endpoint = `http://127.0.0.1:${s3.address().port}`;
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'data', 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ MEDIA_DATA_DIR: path.join(tmp, 'data'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
MEDIA_B2_ENDPOINT: endpoint, MEDIA_B2_BUCKET: 'b2-bucket', MEDIA_B2_KEY_ID: 'k', MEDIA_B2_APP_KEY: 's', MEDIA_B2_REGION: 'us-west-004',
MEDIA_INVARIANT_SCAN_HOURS: '0', MEDIA_JOBS_ENABLED: '0',
@@@ -146,7 +146,7 @@ const { spawnSync, execFile } = require('child_process');
assert.strictEqual(j.status, 'succeeded', j.error);
let res = JSON.parse(j.result);
assert.deepStrictEqual([res.mode, res.counts.checked, res.counts.repaired, res.next_after_id], ['apply', 4, 3, ids.missing]);
-- assert.ok(fs.existsSync(path.join(path.dirname(process.env.DB_PATH), 'reports', res.report)), 'the report is written');
++ assert.ok(fs.existsSync(path.join(process.env.MEDIA_DATA_DIR, 'reports', res.report)), 'the report is written');
assert.deepStrictEqual([(await vod(ids.wallClock)).duration_seconds, (await vod(ids.wallClock)).duration_source], [6, 'probe']);
assert.deepStrictEqual([(await vod(ids.remote)).duration_seconds, (await vod(ids.remote)).duration_source], [8, 'probe']);
assert.deepStrictEqual([(await vod(ids.missing)).duration_seconds, (await vod(ids.missing)).duration_source], [6, 'probe']);
@@@ -156,7 +156,7 @@ const { spawnSync, execFile } = require('child_process');
j = (await queue.enqueue({ appId: 'live', type: 'vod.duration.reconcile', params: spec.validate({ obj: null, params: { limit: 4, apply: true } }), createdBy: 'test2' })).job;
res = JSON.parse((await worker.runNow(j.id)).result);
assert.deepStrictEqual([res.range.after_id, res.counts.checked, res.range.done, res.next_after_id], [ids.missing, 3, true, 0], 'continues, reaches the end, wraps');
-- const report = JSON.parse(fs.readFileSync(path.join(path.dirname(process.env.DB_PATH), 'reports', JSON.parse((await queue.list('live', { type: 'vod.duration.reconcile' })).jobs.slice(-1)[0].result).report), 'utf8'));
++ const report = JSON.parse(fs.readFileSync(path.join(process.env.MEDIA_DATA_DIR, 'reports', JSON.parse((await queue.list('live', { type: 'vod.duration.reconcile' })).jobs.slice(-1)[0].result).report), 'utf8'));
const rb0 = await reconcile.rollback(report.rows);
assert.deepStrictEqual(rb0, { restored: 0, would_restore: 3, changed_since: 0 });
const rb = await reconcile.rollback(report.rows, { apply: true });
ddiff --git a/test/events.test.js b/test/events.test.js
iindex 902a930..0c08012 100644
---- a/test/events.test.js
++++ b/test/events.test.js
@@@ -11,7 +11,6 @@ const http = require('http');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-events-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/export-tokens.test.js b/test/export-tokens.test.js
iindex fd65bd5..5c41159 100644
---- a/test/export-tokens.test.js
++++ b/test/export-tokens.test.js
@@@ -15,7 +15,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-export-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
ddiff --git a/test/helpers/pg-preload.mjs b/test/helpers/pg-preload.mjs
iindex 7cabdbf..0198551 100644
---- a/test/helpers/pg-preload.mjs
++++ b/test/helpers/pg-preload.mjs
@@@ -10,8 +10,8 @@ const require = createRequire(import.meta.url);
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
const { createTestDb } = require('openvibe-sdk/testing');
const t = await createTestDb({ migrations: path.join(root, 'migrations'), store: process.env.MEDIA_TEST_STORE || 'pglite', service: 'media', max: 4 });
--// Tests insert rows with small explicit ids (SQLite's AUTOINCREMENT then continued after them; a PostgreSQL identity
--// does not): generated ids start at 100000 here, clear of every id a test names.
++// Tests insert rows with small explicit ids. A PostgreSQL identity would continue after them, so generated ids
++// are set to start at 100000 here, clear of every id a test names.
for (const r of await t.db.prepare(`SELECT pg_get_serial_sequence(quote_ident(table_name), column_name) AS seq FROM information_schema.columns
WHERE table_schema = current_schema() AND is_identity = 'YES'`).all()) {
if (r.seq) await t.db.prepare('SELECT setval(?::regclass, 100000)').get(r.seq);
ddiff --git a/test/jobs-fencing.test.js b/test/jobs-fencing.test.js
iindex 687a2c9..a245eb4 100644
---- a/test/jobs-fencing.test.js
++++ b/test/jobs-fencing.test.js
@@@ -13,7 +13,6 @@ const path = require('path');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-fencing-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/jobs-invariant.test.js b/test/jobs-invariant.test.js
iindex d772bc5..9e025c2 100644
---- a/test/jobs-invariant.test.js
++++ b/test/jobs-invariant.test.js
@@@ -15,7 +15,6 @@ const { spawnSync } = require('child_process');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-jobinv-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/jobs-previews.test.js b/test/jobs-previews.test.js
iindex ca6dd35..d82ccc7 100644
---- a/test/jobs-previews.test.js
++++ b/test/jobs-previews.test.js
@@@ -13,7 +13,6 @@ const { spawnSync } = require('child_process');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-previews-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/jobs.test.js b/test/jobs.test.js
iindex 6261078..fbc4a84 100644
---- a/test/jobs.test.js
++++ b/test/jobs.test.js
@@@ -15,7 +15,6 @@ const http = require('http');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-jobs-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
@@@ -238,7 +237,7 @@ const http = require('http');
assert.deepStrictEqual(r.body.jobs.map((j) => j.id), [scanId]);
r = await call('GET', `/api/v2/live/jobs/${scanId}`);
assert.deepStrictEqual([r.status, r.body.job.idempotency_key, r.body.job.params, r.body.job.created_by, r.body.job.created_at.includes('T')], [200, 'scan-1', {}, 'app:live', false],
-- 'GET the job answers as before (queue.jobPublic: params, idempotency key, creator, SQLite times)');
++ "GET the job answers as before (queue.jobPublic: params, idempotency key, creator, the store's `YYYY-MM-DD HH:MM:SS` UTC text timestamps)");
r = await call('GET', `/api/v2/games/jobs/${scanId}`, { key: 'games-key' });
assert.strictEqual(r.status, 404, 'tenants are isolated');
r = await call('GET', `/api/v2/live/jobs?status=bogus`);
ddiff --git a/test/me-explorer.test.js b/test/me-explorer.test.js
iindex 9858cc4..68f532f 100644
---- a/test/me-explorer.test.js
++++ b/test/me-explorer.test.js
@@@ -21,7 +21,7 @@ const http = require('http');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-me-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
OBJECTS_PATH: dir('objects'), THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: 'https://openvibe.network',
});
ddiff --git a/test/moderation-events.test.js b/test/moderation-events.test.js
iindex 1932851..877df53 100644
---- a/test/moderation-events.test.js
++++ b/test/moderation-events.test.js
@@@ -12,7 +12,7 @@ const http = require('http');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-modevents-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
});
const stub = http.createServer((req, res) => { req.resume(); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.statusCode = 404; res.end('{}'); }); });
ddiff --git a/test/n-1.test.js b/test/n-1.test.js
iindex b7220b5..db09232 100644
---- a/test/n-1.test.js
++++ b/test/n-1.test.js
@@@ -77,11 +77,11 @@ async function check(name, fn) {
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
fs.mkdirSync(dataDir, { recursive: true });
let server = null;
-- // This release on PostgreSQL (ADR-035) and N-1 on SQLite: they never share a database (the switch stops the
-- // service, imports, then starts this release), so N-1's SQL has no schema of this release to run on. The client
-- // half still runs, on a freshly migrated and seeded database. Both on PostgreSQL: N keeps every migration N-1 ran.
++ // This release on PostgreSQL (ADR-035) and a recorded N-1 that still ran on SQLite: they never share a database,
++ // so N-1's SQL has no schema of this release to run on. The client half still runs, on a freshly migrated and
++ // seeded database. Once the N-1 fixture is recorded from a PostgreSQL release: N keeps every migration N-1 ran.
const pg = fs.existsSync(path.join(ROOT, 'migrations'));
-- const sqlSkip = pg && worker.engine !== 'postgresql' ? 'N-1 SQL: skipped (N-1 runs on SQLite and this release on PostgreSQL: no shared database, so no N-1 statement runs on this schema; scripts/migrate-to-postgres.js moves the data and was rehearsed)' : null;
++ const sqlSkip = pg && worker.engine !== 'postgresql' ? 'N-1 SQL: skipped (the recorded N-1 release runs on SQLite and this release on PostgreSQL: no shared database, so no N-1 statement runs on this schema)' : null;
if (pg && worker.engine === 'postgresql') {
await check(`N-1's ${worker.migrations.length} migration file(s) are all here, unchanged`, () => {
const dir = path.join(ROOT, 'migrations');
ddiff --git a/test/namespaces.test.js b/test/namespaces.test.js
iindex 23dbd18..369af20 100644
---- a/test/namespaces.test.js
++++ b/test/namespaces.test.js
@@@ -18,7 +18,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-namespaces-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
@@@ -51,8 +50,8 @@ const crypto = require('crypto');
iss: 'https://openvibe.network', sub: `app:${APP_A}`, actor_type: 'app', aud: ['openvibe.media'], cap, ns, project_id: project, env, iat: now, exp: now + 300, jti: jti(),
}, keys.privateKey);
-- // Tenants: Live, and developer project PA in production and in its sandbox. (The SQLite release moved older rows
-- // into namespaces at boot; on PostgreSQL the schema starts with them, and writes keep them.)
++ // Tenants: Live, and developer project PA in production and in its sandbox. (On PostgreSQL the schema starts
++ // with the namespaces in place, and writes keep them.)
await db.upsertApp({ app_id: 'live', api_key: 'live-key' });
await db.ensureProjectTenant(PA, 'production', 1000000);
await db.ensureProjectTenant(PA, 'sandbox', 1000000);
ddiff --git a/test/object-events.test.js b/test/object-events.test.js
iindex 1d5cd0e..b34ea9f 100644
---- a/test/object-events.test.js
++++ b/test/object-events.test.js
@@@ -14,7 +14,7 @@ const http = require('http');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-objevents-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
});
ddiff --git a/test/object-first-writes.test.js b/test/object-first-writes.test.js
iindex 7ca381a..e356812 100644
---- a/test/object-first-writes.test.js
++++ b/test/object-first-writes.test.js
@@@ -1,6 +1,6 @@
'use strict';
// Object-first writes (WS-G task 1, retiring compatibility shim C-75): every write to an inherited
--// vods/clips/files/pastes row makes or updates its media_object in the SAME SQLite transaction
++// vods/clips/files/pastes row makes or updates its media_object in the SAME PostgreSQL transaction
// (objects/model.js withObject). For each main write path the object is there, linked and agreeing
// with the row, as soon as the write returns; when the object cannot be written, the row write
// rolls back with it (neither row); and the drift report (server/objects/drift.js,
@@@ -18,7 +18,7 @@ const sharp = require('sharp');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-object-first-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'), MEDIA_PUBLIC_URL: 'https://media.test',
});
ddiff --git a/test/object-tiering.test.js b/test/object-tiering.test.js
iindex 6340020..034c8c6 100644
---- a/test/object-tiering.test.js
++++ b/test/object-tiering.test.js
@@@ -69,7 +69,7 @@ const crypto = require('crypto');
await new Promise((r) => s3.listen(0, '127.0.0.1', r));
const endpoint = `http://127.0.0.1:${s3.address().port}`;
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
MEDIA_B2_ENDPOINT: endpoint, MEDIA_B2_BUCKET: 'b2-bucket', MEDIA_B2_KEY_ID: 'k', MEDIA_B2_APP_KEY: 's', MEDIA_B2_REGION: 'us-west-004',
MEDIA_R2_ENDPOINT: endpoint, MEDIA_R2_BUCKET: 'r2-bucket', MEDIA_R2_ACCESS_KEY_ID: 'k', MEDIA_R2_SECRET_ACCESS_KEY: 's',
ddiff --git a/test/objects-api.test.js b/test/objects-api.test.js
iindex 5cb1c75..c0213be 100644
---- a/test/objects-api.test.js
++++ b/test/objects-api.test.js
@@@ -13,7 +13,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-objapi-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
ddiff --git a/test/objects-model.test.js b/test/objects-model.test.js
iindex 434125e..1e970a7 100644
---- a/test/objects-model.test.js
++++ b/test/objects-model.test.js
@@@ -11,7 +11,6 @@ const path = require('path');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-objects-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/objects-multipart.test.js b/test/objects-multipart.test.js
iindex 23bf305..efdedf0 100644
---- a/test/objects-multipart.test.js
++++ b/test/objects-multipart.test.js
@@@ -13,7 +13,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-multipart-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.FILES_PATH = dir('files');
process.env.OBJECTS_PATH = dir('objects');
ddiff --git a/test/objects-reconcile.test.js b/test/objects-reconcile.test.js
iindex 8080600..6833ae8 100644
---- a/test/objects-reconcile.test.js
++++ b/test/objects-reconcile.test.js
@@@ -13,7 +13,7 @@ const crypto = require('crypto');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-reconcile-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
const env = {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'), MEDIA_PUBLIC_URL: 'https://media.test',
MEDIA_PUBLIC_OBJECT_TARGET_MB: '1', MEDIA_PUBLIC_OBJECT_WARN_MB: '2', MEDIA_PUBLIC_OBJECT_MAX_MB: '3',
};
ddiff --git a/test/objects-verify.test.js b/test/objects-verify.test.js
iindex 23fa515..c08c850 100644
---- a/test/objects-verify.test.js
++++ b/test/objects-verify.test.js
@@@ -15,7 +15,7 @@ const crypto = require('crypto');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-verify-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
const env = {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'), ASSETS_PATH: dir('assets'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: 'http://127.0.0.1:9',
};
ddiff --git a/test/observability.test.js b/test/observability.test.js
iindex c730912..983796c 100644
---- a/test/observability.test.js
++++ b/test/observability.test.js
@@@ -14,7 +14,7 @@ const { spawn } = require('child_process');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-obs-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
const ENV = {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), PASTES_PATH: dir('pastes'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), PASTES_PATH: dir('pastes'),
THUMBNAILS_PATH: dir('thumbnails'), FILES_PATH: dir('files'), OBJECTS_PATH: dir('objects'), ASSETS_PATH: dir('assets'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: 'http://127.0.0.1:9', RELEASE_COMMIT: 'abcdef1234567',
};
@@@ -132,13 +132,13 @@ const { spawn } = require('child_process');
// ── The real index.js boots with both routes ──
const port = await new Promise((resolve) => { const s = net.createServer().listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => resolve(p)); }); });
const bootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-obs-boot-'));
-- const env = { ...process.env, ...ENV, DB_PATH: path.join(bootDir, 'media.db'), MEDIA_PGLITE_DIR: path.join(bootDir, 'pglite'), DATABASE_URL: '', DATABASE_DIRECT_URL: '', VALKEY_URL: '', PORT: String(port), HOST: '127.0.0.1', NODE_ENV: 'test' };
++ const env = { ...process.env, ...ENV, MEDIA_PGLITE_DIR: path.join(bootDir, 'pglite'), DATABASE_URL: '', DATABASE_DIRECT_URL: '', VALKEY_URL: '', PORT: String(port), HOST: '127.0.0.1', NODE_ENV: 'test' };
for (const k of ['VOD_PATH', 'CLIPS_PATH', 'PASTES_PATH', 'THUMBNAILS_PATH', 'FILES_PATH', 'OBJECTS_PATH', 'ASSETS_PATH']) env[k] = path.join(bootDir, k.toLowerCase());
const child = spawn(process.execPath, [path.join(__dirname, '..', 'server', 'index.js')], { env, stdio: ['ignore', 'pipe', 'pipe'] });
let log = ''; child.stdout.on('data', (d) => { log += d; }); child.stderr.on('data', (d) => { log += d; });
const b2 = `http://127.0.0.1:${port}`;
let up = null;
-- for (let i = 0; i < 100 && !up; i++) {
++ for (let i = 0; i < 300 && !up; i++) { // 30 s: a cold PGlite boot plus migrations is slow on a loaded machine
await new Promise((res) => setTimeout(res, 100));
try { up = await request(b2, '/api/ready'); } catch { /* not listening yet */ }
}
ddiff --git a/test/outbox-transaction.test.js b/test/outbox-transaction.test.js
iindex 7dc52c0..1168c1b 100644
---- a/test/outbox-transaction.test.js
++++ b/test/outbox-transaction.test.js
@@@ -1,5 +1,5 @@
'use strict';
--// Roadmap Wave 3 (audit item 7): Media's outbox row is written in the SAME SQLite transaction as
++// Roadmap Wave 3 (audit item 7): Media's outbox row is written in the SAME PostgreSQL transaction as
// the state change it describes. An outcome is never lost (the change committed, the event did
// not) and never a phantom (the event exists, the change rolled back). The webhook for the same
// outcome follows the commit and carries the event's id, so a consumer reading both paths
@@@ -15,7 +15,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-outbox-tx-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/owner-subject.test.js b/test/owner-subject.test.js
iindex 2889a6b..0e0c5b9 100644
---- a/test/owner-subject.test.js
++++ b/test/owner-subject.test.js
@@@ -15,7 +15,7 @@ const { ids } = require('openvibe-contracts');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-owner-subject-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
const env = {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'), ASSETS_PATH: dir('assets'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: 'http://127.0.0.1:9', MEDIA_SIGNING_SECRET: 'test-signing-secret',
INTERNAL_API_KEY: 'internal-key-0123456789abcdef', OV_OAUTH_CLIENT_ID: 'media', OV_OAUTH_CLIENT_SECRET: 'media-secret',
@@@ -97,7 +97,7 @@ const { ids } = require('openvibe-contracts');
for (let uid = 1000; uid < 1520; uid++) { subjectOf(uid); many.push(await obj({ owner_user_id: uid, kind: 'clip' })); }
const TO_FILL = 3 + many.length;
-- // (The one-off backfill script ran in production on SQLite and is retired: the reconcile job below fills what is left.)
++ // (The one-off backfill script is retired: the reconcile job below fills what is left.)
// A token Network refuses at resolve-batch: the key answers instead.
const resolver = ownerSubject.createResolver({ networkUrl: NET });
net.grant = true;
ddiff --git a/test/pastes-moved.test.js b/test/pastes-moved.test.js
iindex 93bffdf..7329ff1 100644
---- a/test/pastes-moved.test.js
++++ b/test/pastes-moved.test.js
@@@ -10,7 +10,6 @@ const express = require('express');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-moved-'));
-- process.env.DB_PATH = path.join(tmp, 'media.db');
const db = require('../server/db/database');
await db.upsertApp({ app_id: 'live', api_key: 'live-key' });
const shot = path.join(tmp, 'shot.png');
ddiff --git a/test/perf-budget.test.js b/test/perf-budget.test.js
iindex 76aaea7..4456166 100644
---- a/test/perf-budget.test.js
++++ b/test/perf-budget.test.js
@@@ -32,7 +32,7 @@ const { measure, check, format } = require('openvibe-shared/perf-budget');
const port = await freePort();
const child = spawn(process.execPath, [path.join(__dirname, '..', 'server', 'index.js')], {
cwd: path.join(__dirname, '..'),
-- env: { ...process.env, PORT: String(port), HOST: '127.0.0.1', NODE_ENV: 'test', DB_PATH: path.join(dir, 'media.db'), MEDIA_PGLITE_DIR: path.join(dir, 'pglite'), DATABASE_URL: '', DATABASE_DIRECT_URL: '', VALKEY_URL: '', VOD_PATH: path.join(dir, 'vods'), CLIPS_PATH: path.join(dir, 'clips'), FILES_PATH: path.join(dir, 'files'), THUMBNAILS_PATH: path.join(dir, 'thumbnails'), PASTES_PATH: path.join(dir, 'pastes'), OBJECTS_PATH: path.join(dir, 'objects'), MEDIA_JOBS_ENABLED: 'off' },
++ env: { ...process.env, PORT: String(port), HOST: '127.0.0.1', NODE_ENV: 'test', MEDIA_PGLITE_DIR: path.join(dir, 'pglite'), DATABASE_URL: '', DATABASE_DIRECT_URL: '', VALKEY_URL: '', VOD_PATH: path.join(dir, 'vods'), CLIPS_PATH: path.join(dir, 'clips'), FILES_PATH: path.join(dir, 'files'), THUMBNAILS_PATH: path.join(dir, 'thumbnails'), PASTES_PATH: path.join(dir, 'pastes'), OBJECTS_PATH: path.join(dir, 'objects'), MEDIA_JOBS_ENABLED: 'off' },
stdio: ['ignore', 'ignore', 'pipe'],
});
let stderr = '';
ddiff --git a/test/private-not-found.test.js b/test/private-not-found.test.js
iindex 1a7b8c5..dd69c76 100644
---- a/test/private-not-found.test.js
++++ b/test/private-not-found.test.js
@@@ -13,7 +13,6 @@ const http = require('http');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-private404-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/public-pages.test.js b/test/public-pages.test.js
iindex 81adf29..13b1d8b 100644
---- a/test/public-pages.test.js
++++ b/test/public-pages.test.js
@@@ -9,7 +9,6 @@ const http = require('http');
(async () => {
const tmp = path.join(os.tmpdir(), `ov-media-pages-${process.pid}.db`);
-- process.env.DB_PATH = tmp;
process.env.MEDIA_PUBLIC_URL = 'https://media.test';
const db = require('../server/db/database');
ddiff --git a/test/r2-eviction-drill.test.js b/test/r2-eviction-drill.test.js
iindex a684709..6fc2a4c 100644
---- a/test/r2-eviction-drill.test.js
++++ b/test/r2-eviction-drill.test.js
@@@ -73,7 +73,7 @@ const crypto = require('crypto');
await new Promise((r) => s3.listen(0, '127.0.0.1', r));
const endpoint = `http://127.0.0.1:${s3.address().port}`;
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
MEDIA_B2_ENDPOINT: endpoint, MEDIA_B2_BUCKET: 'b2-bucket', MEDIA_B2_KEY_ID: 'k', MEDIA_B2_APP_KEY: 's', MEDIA_B2_REGION: 'us-west-004',
MEDIA_R2_ENDPOINT: endpoint, MEDIA_R2_BUCKET: 'r2-bucket', MEDIA_R2_ACCESS_KEY_ID: 'k', MEDIA_R2_SECRET_ACCESS_KEY: 's',
ddiff --git a/test/readiness.test.js b/test/readiness.test.js
iindex 56b2395..8687a7e 100644
---- a/test/readiness.test.js
++++ b/test/readiness.test.js
@@@ -16,7 +16,7 @@ const http = require('http');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-readiness-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
OBJECTS_PATH: dir('objects'), THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: 'https://openvibe.network',
});
ddiff --git a/test/recorder-lifecycle.test.js b/test/recorder-lifecycle.test.js
iindex b47b04a..d5488c2 100644
---- a/test/recorder-lifecycle.test.js
++++ b/test/recorder-lifecycle.test.js
@@@ -13,7 +13,7 @@ const { EventEmitter } = require('events');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-rec-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
});
delete process.env.MEDIA_RTMP_PULL_ALLOW;
ddiff --git a/test/retention-holds.test.js b/test/retention-holds.test.js
iindex 65a27f9..b7dedae 100644
---- a/test/retention-holds.test.js
++++ b/test/retention-holds.test.js
@@@ -16,7 +16,7 @@ const http = require('http');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-holds-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
OBJECTS_PATH: dir('objects'), THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: 'https://openvibe.network',
});
ddiff --git a/test/revocations.test.js b/test/revocations.test.js
iindex e257bde..687ed90 100644
---- a/test/revocations.test.js
++++ b/test/revocations.test.js
@@@ -10,7 +10,6 @@ const http = require('http');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-revoke-'));
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.MEDIA_INBOUND_EVENTS_SECRET = 'm'.repeat(40);
const express = require('express');
const { signDeliveryHeaders } = require('openvibe-sdk/events');
ddiff --git a/test/rtmp-allowlist.test.js b/test/rtmp-allowlist.test.js
iindex b1648c9..8f59302 100644
---- a/test/rtmp-allowlist.test.js
++++ b/test/rtmp-allowlist.test.js
@@@ -14,7 +14,7 @@ const express = require('express');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-rtmp-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
});
delete process.env.MEDIA_RTMP_PULL_ALLOW;
ddiff --git a/test/security-idor.test.js b/test/security-idor.test.js
iindex 81da7f4..838c5be 100644
---- a/test/security-idor.test.js
++++ b/test/security-idor.test.js
@@@ -23,7 +23,7 @@ const crypto = require('crypto');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-idor-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'), OBJECTS_PATH: dir('objects'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'), OBJECTS_PATH: dir('objects'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), ASSETS_PATH: dir('assets'),
OV_NETWORK_URL: 'https://openvibe.network', MEDIA_PUBLIC_URL: 'https://media.test', MEDIA_SIGNING_SECRET: 'test-signing-secret',
MEDIA_UPLOAD_MIN_FREE_MB: '0', MEDIA_MULTIPART_MIN_PART_MB: '1', MEDIA_JOBS_ENABLED: '0',
ddiff --git a/test/security-private.test.js b/test/security-private.test.js
iindex eb0f975..bce068d 100644
---- a/test/security-private.test.js
++++ b/test/security-private.test.js
@@@ -21,7 +21,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-secprivate-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.FILES_PATH = dir('files');
ddiff --git a/test/security-secrets.test.js b/test/security-secrets.test.js
iindex 81e2036..170f96e 100644
---- a/test/security-secrets.test.js
++++ b/test/security-secrets.test.js
@@@ -86,7 +86,7 @@ const crypto = require('crypto');
const NET = `http://127.0.0.1:${network.address().port}`;
Object.assign(process.env, SECRET_ENV, {
PORT: '0', HOST: '127.0.0.1', NODE_ENV: 'test',
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), PASTES_PATH: dir('pastes'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), PASTES_PATH: dir('pastes'),
THUMBNAILS_PATH: dir('thumbnails'), FILES_PATH: dir('files'), OBJECTS_PATH: dir('objects'), ASSETS_PATH: dir('assets'),
MEDIA_PUBLIC_URL: 'https://media.test', OV_NETWORK_URL: NET, OV_NETWORK_INTERNAL_URL: NET, EVENTS_URL: NET,
OV_LIVE_INTERNAL_URL: CLOSED, LIVE_APP_INTERNAL_URL: CLOSED, APP_INTERNAL_URLS: JSON.stringify({ live: CLOSED }),
ddiff --git a/test/security-ssrf.test.js b/test/security-ssrf.test.js
iindex 2b25ca7..bd4b15f 100644
---- a/test/security-ssrf.test.js
++++ b/test/security-ssrf.test.js
@@@ -21,7 +21,6 @@ const https = require('https');
(async () => {
const ROOT = path.join(__dirname, '..');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-ssrf-'));
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.PASTES_PATH = path.join(tmp, 'pastes');
process.env.MEDIA_PUBLIC_URL = 'https://media.test';
delete process.env.MEDIA_RTMP_PULL_ALLOW;
ddiff --git a/test/service-token.test.js b/test/service-token.test.js
iindex 27a1b11..7a9aefa 100644
---- a/test/service-token.test.js
++++ b/test/service-token.test.js
@@@ -10,7 +10,6 @@ const crypto = require('crypto');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-svc-'));
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.FILES_PATH = path.join(tmp, 'files');
process.env.OV_NETWORK_URL = 'https://openvibe.network';
process.env.MEDIA_PUBLIC_URL = 'https://media.test';
ddiff --git a/test/stat-series.test.js b/test/stat-series.test.js
iindex 436c8e3..e44c7a2 100644
---- a/test/stat-series.test.js
++++ b/test/stat-series.test.js
@@@ -7,7 +7,6 @@ const path = require('path');
(async () => {
const tmp = path.join(os.tmpdir(), `ov-media-series-${process.pid}.db`);
-- process.env.DB_PATH = tmp;
const db = require('../server/db/database');
const raw = db.getDb();
ddiff --git a/test/storage-orphans.test.js b/test/storage-orphans.test.js
iindex 8d6bb57..a5de097 100644
---- a/test/storage-orphans.test.js
++++ b/test/storage-orphans.test.js
@@@ -53,7 +53,7 @@ const crypto = require('crypto');
await new Promise((r) => s3.listen(0, '127.0.0.1', r));
const endpoint = `http://127.0.0.1:${s3.address().port}`;
const env = {
-- DB_PATH: path.join(tmp, 'data', 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ MEDIA_DATA_DIR: path.join(tmp, 'data'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'), ASSETS_PATH: dir('assets'),
MEDIA_B2_ENDPOINT: endpoint, MEDIA_B2_BUCKET: 'b2-bucket', MEDIA_B2_KEY_ID: 'k', MEDIA_B2_APP_KEY: 's', MEDIA_B2_REGION: 'us-west-004',
MEDIA_R2_ENDPOINT: endpoint, MEDIA_R2_BUCKET: 'r2-bucket', MEDIA_R2_ACCESS_KEY_ID: 'k', MEDIA_R2_SECRET_ACCESS_KEY: 's',
ddiff --git a/test/thumbnail-unavailable.test.js b/test/thumbnail-unavailable.test.js
iindex de0654a..769efc6 100644
---- a/test/thumbnail-unavailable.test.js
++++ b/test/thumbnail-unavailable.test.js
@@@ -15,7 +15,7 @@ const express = require('express');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-thumbfail-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'), MEDIA_INVARIANT_SCAN_HOURS: '0',
});
ddiff --git a/test/tier-config.test.js b/test/tier-config.test.js
iindex 7fe44e5..eff0752 100644
---- a/test/tier-config.test.js
++++ b/test/tier-config.test.js
@@@ -12,7 +12,7 @@ const path = require('path');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-tiercfg-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
});
ddiff --git a/test/tier-decisions.test.js b/test/tier-decisions.test.js
iindex 97eb76e..1ce6012 100644
---- a/test/tier-decisions.test.js
++++ b/test/tier-decisions.test.js
@@@ -63,7 +63,7 @@ const crypto = require('crypto');
await new Promise((r) => s3.listen(0, '127.0.0.1', r));
const endpoint = `http://127.0.0.1:${s3.address().port}`;
Object.assign(process.env, {
-- DB_PATH: path.join(tmp, 'media.db'), VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
++ VOD_PATH: dir('vods'), CLIPS_PATH: dir('clips'), FILES_PATH: dir('files'),
THUMBNAILS_PATH: dir('thumbnails'), PASTES_PATH: dir('pastes'), OBJECTS_PATH: dir('objects'),
MEDIA_B2_ENDPOINT: endpoint, MEDIA_B2_BUCKET: 'b2-bucket', MEDIA_B2_KEY_ID: 'k', MEDIA_B2_APP_KEY: 's', MEDIA_B2_REGION: 'us-west-004',
MEDIA_R2_ENDPOINT: endpoint, MEDIA_R2_BUCKET: 'r2-bucket', MEDIA_R2_ACCESS_KEY_ID: 'k', MEDIA_R2_SECRET_ACCESS_KEY: 's',
ddiff --git a/test/trust-proxy.test.js b/test/trust-proxy.test.js
iindex b63f649..e6460b4 100644
---- a/test/trust-proxy.test.js
++++ b/test/trust-proxy.test.js
@@@ -57,7 +57,7 @@ const express = require('express');
assert.ok(!/set\('trust proxy',\s*true\)/.test(src), 'no trust-everyone left');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-trust-'));
const freePort = await new Promise((resolve) => { const s = net.createServer().listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => resolve(p)); }); });
-- const env = { ...process.env, DB_PATH: path.join(tmp, 'media.db'), MEDIA_PGLITE_DIR: path.join(tmp, 'pglite'), DATABASE_URL: '', DATABASE_DIRECT_URL: '', VALKEY_URL: '', VOD_PATH: path.join(tmp, 'vods'), CLIPS_PATH: path.join(tmp, 'clips'), PASTES_PATH: path.join(tmp, 'pastes'),
++ const env = { ...process.env, MEDIA_PGLITE_DIR: path.join(tmp, 'pglite'), DATABASE_URL: '', DATABASE_DIRECT_URL: '', VALKEY_URL: '', VOD_PATH: path.join(tmp, 'vods'), CLIPS_PATH: path.join(tmp, 'clips'), PASTES_PATH: path.join(tmp, 'pastes'),
THUMBNAILS_PATH: path.join(tmp, 'thumbs'), FILES_PATH: path.join(tmp, 'files'), OBJECTS_PATH: path.join(tmp, 'objects'), PORT: String(freePort), HOST: ext ? '0.0.0.0' : '127.0.0.1',
NODE_ENV: 'test', MEDIA_JOBS_ENABLED: '0', MEDIA_VERIFY_ENABLED: '0', MEDIA_OWNER_SUBJECT_SYNC: '0', EVENTS_URL: '', MEDIA_APP_KEYS: 'live:live-key-for-trust-test' };
const child = spawn(process.execPath, [path.join(__dirname, '../server/index.js')], { env, stdio: ['ignore', 'pipe', 'pipe'] });
ddiff --git a/test/vod-duration.test.js b/test/vod-duration.test.js
iindex f434976..b2c823c 100644
---- a/test/vod-duration.test.js
++++ b/test/vod-duration.test.js
@@@ -13,7 +13,6 @@ const { spawnSync } = require('child_process');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-duration-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.THUMBNAILS_PATH = dir('thumbnails');
ddiff --git a/test/vod-finalize-job.test.js b/test/vod-finalize-job.test.js
iindex 9cc54ec..148aa8c 100644
---- a/test/vod-finalize-job.test.js
++++ b/test/vod-finalize-job.test.js
@@@ -12,7 +12,6 @@ const path = require('path');
(async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ov-media-finalize-job-'));
const dir = (n) => { const d = path.join(tmp, n); fs.mkdirSync(d, { recursive: true }); return d; };
-- process.env.DB_PATH = path.join(tmp, 'media.db');
process.env.VOD_PATH = dir('vods');
process.env.CLIPS_PATH = dir('clips');
process.env.THUMBNAILS_PATH = dir('thumbnails');