diff --git a/migrations/0001_camera.sql b/migrations/0001_camera.sql new file mode 100644 index 0000000..c261551 --- /dev/null +++ b/migrations/0001_camera.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS camera ( + edge varchar NOT NULL, + protocol varchar NOT NULL, + source varchar NOT NULL, + PRIMARY KEY (edge, protocol, source) +); + +CREATE INDEX IF NOT EXISTS camera_edge_idx ON camera (edge); diff --git a/src/app.module.ts b/src/app.module.ts index 8c1f1b8..0503358 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { CameraModule } from './camera/camera.module'; import { CurrentModule } from './current/current.module'; import { DbModule } from './db/db.module'; import { EdgeModule } from './edge/edge.module'; @@ -10,6 +11,7 @@ import { TagModule } from './tag/tag.module'; @Module({ imports: [ DbModule, + CameraModule, IngestModule, EdgeModule, CurrentModule, diff --git a/src/camera/camera.controller.ts b/src/camera/camera.controller.ts new file mode 100644 index 0000000..d79d1d7 --- /dev/null +++ b/src/camera/camera.controller.ts @@ -0,0 +1,16 @@ +import { Controller, Get, Header, Query } from '@nestjs/common'; +import { CameraService } from './camera.service'; +import { GetCamerasDto } from './dto/get-cameras.dto'; + +@Controller('camera') +export class CameraController { + constructor(private readonly camera: CameraService) {} + + @Get() + @Header('Cache-Control', 'no-store') + @Header('Pragma', 'no-cache') + @Header('Expires', '0') + findByEdge(@Query() query: GetCamerasDto) { + return this.camera.findByEdge(query); + } +} diff --git a/src/camera/camera.mapper.ts b/src/camera/camera.mapper.ts new file mode 100644 index 0000000..9bb03c3 --- /dev/null +++ b/src/camera/camera.mapper.ts @@ -0,0 +1,12 @@ +import { CameraResponseDto } from './dto/camera-response.dto'; +import { CameraRow } from './camera.types'; + +export function createCameraResponse(edge: string, rows: CameraRow[]): CameraResponseDto { + return { + edge, + items: rows.map((row) => ({ + protocol: row.protocol, + source: row.source, + })), + }; +} diff --git a/src/camera/camera.module.ts b/src/camera/camera.module.ts new file mode 100644 index 0000000..2a1b5f1 --- /dev/null +++ b/src/camera/camera.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { CameraController } from './camera.controller'; +import { CameraRepository } from './camera.repository'; +import { CameraService } from './camera.service'; + +@Module({ + controllers: [CameraController], + providers: [CameraService, CameraRepository], +}) +export class CameraModule {} diff --git a/src/camera/camera.repository.ts b/src/camera/camera.repository.ts new file mode 100644 index 0000000..17945dc --- /dev/null +++ b/src/camera/camera.repository.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { DbService } from '../db/db.service'; +import { CameraRow } from './camera.types'; + +@Injectable() +export class CameraRepository { + constructor(private readonly db: DbService) {} + + async findByEdge(edge: string): Promise { + const result = await this.db.query( + ` + SELECT protocol, source + FROM camera + WHERE edge = $1 + ORDER BY source ASC + `, + [edge], + ); + + return result.rows; + } +} diff --git a/src/camera/camera.service.ts b/src/camera/camera.service.ts new file mode 100644 index 0000000..6e16ae4 --- /dev/null +++ b/src/camera/camera.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; +import { CameraResponseDto } from './dto/camera-response.dto'; +import { GetCamerasDto } from './dto/get-cameras.dto'; +import { createCameraResponse } from './camera.mapper'; +import { CameraRepository } from './camera.repository'; + +@Injectable() +export class CameraService { + constructor(private readonly repository: CameraRepository) {} + + async findByEdge(query: GetCamerasDto): Promise { + const rows = await this.repository.findByEdge(query.edge); + return createCameraResponse(query.edge, rows); + } +} diff --git a/src/camera/camera.types.ts b/src/camera/camera.types.ts new file mode 100644 index 0000000..8208605 --- /dev/null +++ b/src/camera/camera.types.ts @@ -0,0 +1,4 @@ +export type CameraRow = { + protocol: string; + source: string; +}; diff --git a/src/camera/dto/camera-response.dto.ts b/src/camera/dto/camera-response.dto.ts new file mode 100644 index 0000000..31645b0 --- /dev/null +++ b/src/camera/dto/camera-response.dto.ts @@ -0,0 +1,9 @@ +export type CameraItemDto = { + protocol: string; + source: string; +}; + +export type CameraResponseDto = { + edge: string; + items: CameraItemDto[]; +}; diff --git a/src/camera/dto/get-cameras.dto.ts b/src/camera/dto/get-cameras.dto.ts new file mode 100644 index 0000000..5a1e715 --- /dev/null +++ b/src/camera/dto/get-cameras.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class GetCamerasDto { + @IsNotEmpty() + @IsString() + edge!: string; +}