diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..fea252e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,20 @@ +version: '3.8' + +services: + db: + image: postgres:15 + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + + service: + build: . + ports: + - "9000:9000" + depends_on: + - db + +volumes: + postgres_data: + redis_data: \ No newline at end of file diff --git a/src/modules/device/commands/service.ts b/src/modules/device/commands/service.ts index d57ac72..c9a059c 100644 --- a/src/modules/device/commands/service.ts +++ b/src/modules/device/commands/service.ts @@ -1,42 +1,22 @@ -import { verify } from '~/helpers/signature' import { DeviceCommandBody } from '../schema' import { sendCommandToThirdParty } from './command' export default abstract class CommandService { - static async sendCommand(body: DeviceCommandBody, headers: Record): Promise<{ + static async sendCommand(body: DeviceCommandBody): Promise<{ status: 'success' message: 'Command accepted' receivedAt: string }> { - const apiKey = headers['x-api-key'] - - // For now, use apiKey as secretKey since DB validation is skipped - const secretKey = apiKey - - // Remove signature from body for verification - const { signature, ...bodyWithoutSignature } = body - - const isValid = verify(bodyWithoutSignature, signature, secretKey) - if (!isValid) { - throw new Error('invalid_signature') - } - - // TODO: Check nonce for replay attack when DB is implemented - - const result = { - status: 'success' as const, - message: 'Command accepted' as const, - receivedAt: new Date().toISOString(), - } - - // Generate topicData and send to third-party service try { const topicData = `${body.merchantName}/${body.floorName}_${body.unitNumber}-${body.deviceName}-${body.roomName}-${body.deviceType}-${body.commandType}-${body.towerNumber}` await sendCommandToThirdParty(topicData, body.payload.action) } catch { throw new Error('internal_error') } - - return result + return { + status: 'success' as const, + message: 'Command accepted' as const, + receivedAt: new Date().toISOString(), + } } } diff --git a/src/modules/device/queries/service.ts b/src/modules/device/queries/service.ts index e929c26..788f027 100644 --- a/src/modules/device/queries/service.ts +++ b/src/modules/device/queries/service.ts @@ -17,19 +17,6 @@ export default abstract class QueryService { payload: string } } | null> { - const apiKey = headers['x-api-key'] - - // For now, use apiKey as secretKey since DB validation is skipped - const secretKey = apiKey - - // Remove signature from query for verification - const { signature, ...queryWithoutSignature } = query - - const isValid = verify(queryWithoutSignature, signature, secretKey) - if (!isValid) { - throw new Error('invalid_signature') - } - // TODO: Check nonce for replay attack when DB is implemented const topic = `${query.merchantName}/${query.floorName}_${query.unitNumber}-${query.deviceName}-${query.roomName}-${query.deviceType}-S-${query.towerNumber}` diff --git a/src/modules/device/router.ts b/src/modules/device/router.ts index 9252e8d..76b19a8 100644 --- a/src/modules/device/router.ts +++ b/src/modules/device/router.ts @@ -1,19 +1,19 @@ import { Elysia } from 'elysia' -import { deviceGuard } from '~/middlewares/deviceGuard' import { deviceCommandBody, deviceCommandResponseSchema, deviceQueryParams, deviceQueryResponseSchema } from './schema' import CommandService from './commands/service' import QueryService from './queries/service' import { deviceErrorResponseMap } from '~/helpers/errors' +import { apiKeyAuthMacro } from '~/middlewares/apiKeyAuth' export const router = new Elysia({ name: 'modules.device', detail: { tags: ['Device'] }, prefix: '/device/v1', }) - .guard(deviceGuard()) - .post('/command', async ({ body, headers, set }) => { + .use(apiKeyAuthMacro) + .post('/command', async ({ body, set }) => { try { - const result = await CommandService.sendCommand(body, headers) + const result = await CommandService.sendCommand(body) return result } catch (error) { if (error instanceof Error && error.message in deviceErrorResponseMap) { diff --git a/src/modules/device/schema.ts b/src/modules/device/schema.ts index 703f7d9..7e91dec 100644 --- a/src/modules/device/schema.ts +++ b/src/modules/device/schema.ts @@ -10,12 +10,11 @@ export const deviceCommandBody = z.object({ deviceType: z.string(), commandType: z.string(), towerNumber: z.string(), - timestamp: z.string().datetime(), // ISO8601 + timestamp: z.string(), nonce: z.string(), payload: z.object({ action: z.string(), }), - signature: z.string(), }) export type DeviceCommandBody = z.infer @@ -29,9 +28,8 @@ export const deviceQueryParams = z.object({ deviceType: z.string(), commandType: z.string(), towerNumber: z.string(), - timestamp: z.string().datetime(), // ISO8601 + timestamp: z.string(), nonce: z.string(), - signature: z.string(), }) export type DeviceQueryParams = z.infer