learning_ai_clock/backend/src/modules/shared-timers/repository.ts
saravanakumardb1 f10b83c122 feat(backend): scaffold product-specific Fastify backend (port 4011)
Add backend/ directory with Fastify 5 + TypeScript ESM service:
- Modules: timers, routines, households, shared-timers, webhooks (migrated from platform-service)
- Cosmos containers: timers, routines, households, shared_timers, webhook_subscriptions, webhook_events
- JWT verification via jose (matches platform-service issuer)
- Shared @bytelyst/* packages via file: refs
- 171 Vitest tests passing

Update AGENTS.md: update backend integration section with product backend details
2026-03-01 20:39:08 -08:00

92 lines
2.7 KiB
TypeScript

/**
* Shared timers repository — Cosmos DB CRUD for household shared timers.
*
* Container: shared_timers (partition key: /householdId)
*/
import { getContainer } from '../../lib/cosmos.js';
import type { SharedTimerDoc, SharedTimerQuery } from './types.js';
function container() {
return getContainer('shared_timers');
}
export async function listSharedTimers(
householdId: string,
productId: string,
query: SharedTimerQuery
): Promise<{ items: SharedTimerDoc[]; total: number }> {
const conditions: string[] = ['c.householdId = @householdId', 'c.productId = @productId'];
const params: { name: string; value: string | number }[] = [
{ name: '@householdId', value: householdId },
{ name: '@productId', value: productId },
];
if (query.state) {
conditions.push('c.state = @state');
params.push({ name: '@state', value: query.state });
}
if (query.type) {
conditions.push('c.type = @type');
params.push({ name: '@type', value: query.type });
}
const where = `WHERE ${conditions.join(' AND ')}`;
const sortField = `c.${query.sortBy}`;
const orderDir = query.sortOrder.toUpperCase();
const countResult = await container()
.items.query<number>({
query: `SELECT VALUE COUNT(1) FROM c ${where}`,
parameters: params,
})
.fetchAll();
const total = countResult.resources[0] ?? 0;
const { resources } = await container()
.items.query<SharedTimerDoc>({
query: `SELECT * FROM c ${where} ORDER BY ${sortField} ${orderDir} OFFSET @offset LIMIT @limit`,
parameters: [
...params,
{ name: '@offset', value: query.offset },
{ name: '@limit', value: query.limit },
],
})
.fetchAll();
return { items: resources, total };
}
export async function getSharedTimer(
id: string,
householdId: string
): Promise<SharedTimerDoc | null> {
try {
const { resource } = await container().item(id, householdId).read<SharedTimerDoc>();
return resource ?? null;
} catch {
return null;
}
}
export async function createSharedTimer(doc: SharedTimerDoc): Promise<SharedTimerDoc> {
const { resource } = await container().items.create(doc);
return resource as SharedTimerDoc;
}
export async function replaceSharedTimer(doc: SharedTimerDoc): Promise<SharedTimerDoc> {
const { resource } = await container().item(doc.id, doc.householdId).replace(doc);
return resource as SharedTimerDoc;
}
export async function deleteSharedTimer(id: string, householdId: string): Promise<boolean> {
try {
const existing = await getSharedTimer(id, householdId);
if (!existing) return false;
await container().item(id, householdId).delete();
return true;
} catch {
return false;
}
}