feat: initial Signage 0.1.0
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import type { ModuleDatabase } from "@ekklesia/module-sdk";
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
import { viewers } from "../../schema/tables.js";
|
||||
import type { NewStoredViewer, SignageViewerStore, StoredViewer, ViewerChanges } from "./types.js";
|
||||
|
||||
/** The viewer half of Signage's production store. Deliberately dull: heartbeat coalescing, health
|
||||
* derivation, and every other decision live above it. */
|
||||
|
||||
function toViewer(row: typeof viewers.$inferSelect): StoredViewer {
|
||||
return {
|
||||
cachedAssetCount: row.cachedAssetCount,
|
||||
channelId: row.channelId,
|
||||
createdAt: row.createdAt,
|
||||
deviceId: row.deviceId,
|
||||
id: row.id,
|
||||
lastError:
|
||||
row.lastErrorCode === null || row.lastErrorAt === null
|
||||
? null
|
||||
: { at: row.lastErrorAt, code: row.lastErrorCode, detail: row.lastErrorDetail },
|
||||
lastSeenAt: row.lastSeenAt,
|
||||
name: row.name,
|
||||
offlineReady: row.offlineReady,
|
||||
reportedContributionId: row.reportedContributionId,
|
||||
reportedPlaybackRevision: row.reportedPlaybackRevision,
|
||||
reportedProviderId: row.reportedProviderId,
|
||||
reportedSelectionId: row.reportedSelectionId,
|
||||
reportedTimelineId: row.reportedTimelineId,
|
||||
viewerKey: row.viewerKey,
|
||||
};
|
||||
}
|
||||
|
||||
export function createViewerStore(database: ModuleDatabase): SignageViewerStore {
|
||||
return {
|
||||
async deleteViewer(viewerId: string): Promise<void> {
|
||||
await database.delete(viewers).where(eq(viewers.id, viewerId));
|
||||
},
|
||||
|
||||
async findViewer(viewerId: string): Promise<StoredViewer | undefined> {
|
||||
const rows = await database.select().from(viewers).where(eq(viewers.id, viewerId));
|
||||
const row = rows[0];
|
||||
return row ? toViewer(row) : undefined;
|
||||
},
|
||||
|
||||
async findViewerByKey(viewerKey: string): Promise<StoredViewer | undefined> {
|
||||
const rows = await database.select().from(viewers).where(eq(viewers.viewerKey, viewerKey));
|
||||
const row = rows[0];
|
||||
return row ? toViewer(row) : undefined;
|
||||
},
|
||||
|
||||
async insertViewer(viewer: NewStoredViewer): Promise<void> {
|
||||
await database.insert(viewers).values({
|
||||
cachedAssetCount: viewer.cachedAssetCount,
|
||||
channelId: viewer.channelId,
|
||||
deviceId: viewer.deviceId,
|
||||
id: viewer.id,
|
||||
name: viewer.name,
|
||||
offlineReady: viewer.offlineReady,
|
||||
reportedContributionId: viewer.reportedContributionId,
|
||||
reportedPlaybackRevision: viewer.reportedPlaybackRevision,
|
||||
reportedProviderId: viewer.reportedProviderId,
|
||||
reportedSelectionId: viewer.reportedSelectionId,
|
||||
reportedTimelineId: viewer.reportedTimelineId,
|
||||
viewerKey: viewer.viewerKey,
|
||||
});
|
||||
},
|
||||
|
||||
async listViewers(): Promise<readonly StoredViewer[]> {
|
||||
const rows = await database.select().from(viewers).orderBy(asc(viewers.name));
|
||||
return rows.map(toViewer);
|
||||
},
|
||||
|
||||
async updateViewer(viewerId: string, changes: ViewerChanges): Promise<void> {
|
||||
const values: Record<string, unknown> = { updatedAt: new Date() };
|
||||
for (const key of [
|
||||
"cachedAssetCount",
|
||||
"channelId",
|
||||
"deviceId",
|
||||
"lastSeenAt",
|
||||
"name",
|
||||
"offlineReady",
|
||||
"reportedContributionId",
|
||||
"reportedPlaybackRevision",
|
||||
"reportedProviderId",
|
||||
"reportedSelectionId",
|
||||
"reportedTimelineId",
|
||||
] as const) {
|
||||
if (changes[key] !== undefined) {
|
||||
values[key] = changes[key];
|
||||
}
|
||||
}
|
||||
if (changes.lastError !== undefined) {
|
||||
values.lastErrorAt = changes.lastError?.at ?? null;
|
||||
values.lastErrorCode = changes.lastError?.code ?? null;
|
||||
values.lastErrorDetail = changes.lastError?.detail ?? null;
|
||||
}
|
||||
await database.update(viewers).set(values).where(eq(viewers.id, viewerId));
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user