Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@nixamp/desktop",
"productName": "nixamp",
"version": "0.17.0",
"version": "0.17.1",
"private": true,
"description": "nixamp as a desktop app: the PWA in a window, with the CLI bundled in.",
"author": "Profullstack, Inc. <anthony@profullstack.com>",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nixamp",
"version": "0.17.0",
"version": "0.17.1",
"description": "It really whips the terminal's ass. A Winamp-shaped audio player for your terminal.",
"license": "MIT",
"type": "module",
Expand Down
1 change: 1 addition & 0 deletions src/live-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ export async function handleLiveApi(
json(response, error.status, { error: error.message });
return true;
}
console.error(`live api ${request.method} ${path}:`, error instanceof Error ? error.message : error);
json(response, 500, { error: "the live event service failed" });
return true;
}
Expand Down
17 changes: 14 additions & 3 deletions src/live-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,22 @@ const EVENT_SCHEMA = `
ON live_events (visibility, status, starts_at, updated_at DESC);
CREATE INDEX IF NOT EXISTS live_events_owner
ON live_events (owner_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS live_events_kind
ON live_events (kind, status, starts_at);

-- The repo has no migration runner, so a table that already exists is
-- brought forward here. Every statement is idempotent, and the CHECKs are
-- replaced by name rather than added twice: the originals were unnamed, so
-- the ones carrying the old status list are found by what they say.
-- The columns come before any index on them: nixamp.com had live_events
-- from before 0.17.0, and an index on kind ahead of ADD COLUMN kind failed
-- there while every fresh database (tests, CI) sailed through.
ALTER TABLE live_events ADD COLUMN IF NOT EXISTS kind TEXT NOT NULL DEFAULT 'talk';
ALTER TABLE live_events ADD COLUMN IF NOT EXISTS doors_open_at TIMESTAMPTZ;
ALTER TABLE live_events ADD COLUMN IF NOT EXISTS ticket_price_cents INTEGER NOT NULL DEFAULT 0;
ALTER TABLE live_events ADD COLUMN IF NOT EXISTS ticket_currency TEXT NOT NULL DEFAULT 'USD';
ALTER TABLE live_events ADD COLUMN IF NOT EXISTS ticket_minutes INTEGER NOT NULL DEFAULT 1440;
ALTER TABLE live_events ADD COLUMN IF NOT EXISTS pay_to TEXT;
CREATE INDEX IF NOT EXISTS live_events_kind
ON live_events (kind, status, starts_at);

DO $$
DECLARE stale record;
Expand Down Expand Up @@ -511,7 +514,15 @@ export class LiveEvents {
constructor(private readonly db: Queryable) {}

private async ensure(): Promise<void> {
this.ready ??= this.db.query(EVENT_SCHEMA).then(() => undefined);
// A failed schema run is not remembered: the next request tries again
// rather than answering 500 until the process restarts.
this.ready ??= this.db.query(EVENT_SCHEMA).then(
() => undefined,
(error: unknown) => {
this.ready = null;
throw error;
},
);
await this.ready;
}

Expand Down
23 changes: 23 additions & 0 deletions test/live-events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,26 @@ test("an artist invitation is a role the store accepts", async () => {
});
assert.equal(invitation.role, "artist");
});

test("the schema adds every new column before it indexes one, and a failed run is retried", async () => {
const seen: string[] = [];
let failures = 1;
const database: Queryable = {
async query(text) {
seen.push(text);
if (text.includes("CREATE TABLE") && failures-- > 0) throw new Error("column \"kind\" does not exist");
return { rows: [] };
},
};
const events = new LiveEvents(database);
await assert.rejects(events.list(), /kind/);
await events.list();
assert.equal(seen.filter((text) => text.includes("CREATE TABLE")).length, 2, "the schema runs again after a failure");
const schema = seen[0]!;
for (const column of ["kind", "doors_open_at", "ticket_price_cents", "ticket_currency", "ticket_minutes", "pay_to"]) {
const added = schema.indexOf(`ADD COLUMN IF NOT EXISTS ${column}`);
assert.ok(added > 0, `${column} is brought forward`);
const indexed = schema.indexOf(`CREATE INDEX IF NOT EXISTS live_events_kind`);
assert.ok(indexed > added, `${column} exists before the kind index is built`);
}
});
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nixamp/web",
"version": "0.17.0",
"version": "0.17.1",
"private": true,
"description": "The nixamp PWA: a browser player, and a remote for nixamp serve.",
"license": "MIT",
Expand Down