feat: initialize ingest-api with Koa and telemetry handling

- Create ingest-api with Koa framework for handling device telemetry.
- Implement telemetry ingestion with MongoDB, Redis, and PostgreSQL.
- Add API endpoints for telemetry and health checks.
- Set up testing with Vitest for unit and end-to-end tests.
- Configure Prettier and ESLint for code formatting and linting.
- Create Docker Compose setup for PostgreSQL, MongoDB, and Redis services.
- Establish contracts package for shared Zod schemas and TypeScript types.
- Implement React frontend with device status and greeting functionalities.
- Add VS Code setup documentation for onboarding developers.
This commit is contained in:
marco.chan committed 2026-09-03 10:13:02 +08:00
1 parent 534fa6dc51
commit 7923ba1b8d
70 files changed
+3085 -3434

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
# contracts
Zod schemas and TypeScript types shared by ingest-api, crud-api, and web.
This package depends on **zod only**. Do not add Koa, Express, Mongo, or trest here — the frontend workspace should stay installable with `yarn workspaces focus web`.
+12
View File
@@ -0,0 +1,12 @@
{
"name": "contracts",
"private": true,
"version": "0.0.1",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"zod": "^3"
}
}
+20
View File
@@ -0,0 +1,20 @@
import { z } from 'zod';
export const deviceStatusSourceSchema = z.enum(['postgres', 'redis', 'missing']);
export const deviceStatusSchema = z.object({
deviceId: z.string().min(1),
lastSeen: z.string().datetime().nullable(),
payload: z.record(z.unknown()),
online: z.boolean(),
source: deviceStatusSourceSchema,
ageMs: z.number().int().nonnegative().nullable(),
});
export const deviceStatusPairSchema = z.object({
postgres: deviceStatusSchema,
redis: deviceStatusSchema,
});
export type DeviceStatus = z.infer<typeof deviceStatusSchema>;
export type DeviceStatusPair = z.infer<typeof deviceStatusPairSchema>;
+12
View File
@@ -0,0 +1,12 @@
import { z } from 'zod';
export const greetingQuerySchema = z.object({
name: z.string().min(1),
});
export const greetingOutputSchema = z.object({
message: z.string(),
});
export type GreetingQuery = z.infer<typeof greetingQuerySchema>;
export type GreetingOutput = z.infer<typeof greetingOutputSchema>;
+4
View File
@@ -0,0 +1,4 @@
export * from './device-status.js';
export * from './greeting.js';
export * from './presence.js';
export * from './telemetry.js';
+4
View File
@@ -0,0 +1,4 @@
/** Redis TTL and Postgres "online" window for the demo. */
export const PRESENCE_TTL_SECONDS = 15;
export const DEMO_DEVICE_ID = 'dev-1';
+11
View File
@@ -0,0 +1,11 @@
import { z } from 'zod';
export const telemetryPayloadSchema = z.record(z.unknown());
export const telemetryIngestSchema = z.object({
deviceId: z.string().min(1),
sentAt: z.string().datetime(),
payload: telemetryPayloadSchema,
});
export type TelemetryIngest = z.infer<typeof telemetryIngestSchema>;