fix: handling data error from internal crestron
This commit is contained in:
@@ -1,3 +1,26 @@
|
|||||||
export const passwordHash = (password: string) => Bun.password.hashSync(password, { algorithm: 'bcrypt' })
|
export const passwordHash = (password: string) => Bun.password.hashSync(password, { algorithm: 'bcrypt' })
|
||||||
|
|
||||||
export const passwordVerify = (password: string, hash: string) => Bun.password.verifySync(password, hash)
|
export const passwordVerify = (password: string, hash: string) => Bun.password.verifySync(password, hash)
|
||||||
|
|
||||||
|
export const dataMapping = [
|
||||||
|
{
|
||||||
|
deviceName: 'AC',
|
||||||
|
command: 'AC=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deviceName: 'L',
|
||||||
|
command: 'Light=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deviceName: 'DL',
|
||||||
|
command: 'Doorlock=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deviceName: 'BL',
|
||||||
|
command: 'Blind=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deviceName: 'BL',
|
||||||
|
command: 'Blind=',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { httpConfig } from '~/config'
|
import { httpConfig } from '~/config'
|
||||||
import logger from '~/plugins/logger'
|
import logger from '~/plugins/logger'
|
||||||
|
|
||||||
export async function sendCommandToThirdParty(topicData: string, payload: string): Promise<void> {
|
export async function sendCommandToThirdParty(topicData: string, payload: string) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${httpConfig.baseUrl}/command`, {
|
const response = await fetch(`${httpConfig.baseUrl}/command`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -13,10 +13,21 @@ export async function sendCommandToThirdParty(topicData: string, payload: string
|
|||||||
payload,
|
payload,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info({ msg: 'Third-party command sent successfully', topic: topicData, status: response.status })
|
logger.info({ msg: 'Third-party command sent successfully', topic: topicData, status: response.status })
|
||||||
|
if (response.status != 200) {
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
message: response.statusText,
|
||||||
|
data: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
message: response.statusText,
|
||||||
|
data: await response.json(),
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error({ msg: 'Failed to send command to third-party service', topic: topicData, error: (error as Error).message })
|
logger.error({ msg: 'Failed to send command to third-party service', topic: topicData, error: (error as Error).message })
|
||||||
throw new Error('third_party_command_failed')
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,22 @@
|
|||||||
|
import { dataMapping } from '~/helpers/utils'
|
||||||
import { DeviceCommandBody } from '../schema'
|
import { DeviceCommandBody } from '../schema'
|
||||||
import { sendCommandToThirdParty } from './command'
|
import { sendCommandToThirdParty } from './command'
|
||||||
|
|
||||||
export default abstract class CommandService {
|
export default abstract class CommandService {
|
||||||
static async sendCommand(body: DeviceCommandBody): Promise<{
|
static async sendCommand(body: DeviceCommandBody): Promise<{
|
||||||
status: 'success'
|
status: string
|
||||||
message: 'Command accepted'
|
message: string
|
||||||
receivedAt: string
|
receivedAt: string
|
||||||
}> {
|
}> {
|
||||||
try {
|
const topicData = `${body.merchantName}/${body.floorName}_${body.unitNumber}-${body.deviceName}-${body.roomName}-${body.deviceType}-${body.commandType}-${body.towerNumber}`
|
||||||
const topicData = `${body.merchantName}/${body.floorName}_${body.unitNumber}-${body.deviceName}-${body.roomName}-${body.deviceType}-${body.commandType}-${body.towerNumber}`
|
const cmd = dataMapping.find((device) => device.deviceName == body.deviceName)
|
||||||
|
const request = await sendCommandToThirdParty(topicData, `${cmd?.command}${body.payload.action}`)
|
||||||
if (topicData == 'SAVY/L2_01-L-LV-S-C-T1' && ['On', 'Off'].includes(body.payload.action)) {
|
if (request.status !== 200) {
|
||||||
return {
|
return {
|
||||||
status: 'success',
|
status: 'error' as const,
|
||||||
message: 'Command accepted',
|
message: request.status == 404 ? 'Device not found' : 'Command failed' as const,
|
||||||
receivedAt: new Date().toISOString(),
|
receivedAt: new Date().toISOString(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (topicData == 'SAVY/L2_01-BL-LV-S-C-T1' && ['Open', 'Closed'].includes(body.payload.action)) {
|
|
||||||
return {
|
|
||||||
status: 'success',
|
|
||||||
message: 'Command accepted',
|
|
||||||
receivedAt: new Date().toISOString(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await sendCommandToThirdParty(topicData, body.payload.action)
|
|
||||||
} catch {
|
|
||||||
throw new Error('internal_error')
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
status: 'success' as const,
|
status: 'success' as const,
|
||||||
|
|||||||
@@ -12,19 +12,12 @@ export const router = new Elysia({
|
|||||||
})
|
})
|
||||||
.use(apiKeyAuthMacro)
|
.use(apiKeyAuthMacro)
|
||||||
.post('/command', async ({ body, set }) => {
|
.post('/command', async ({ body, set }) => {
|
||||||
try {
|
const result = await CommandService.sendCommand(body)
|
||||||
const result = await CommandService.sendCommand(body)
|
if (result.status === 'error' && result.message === 'Device not found') {
|
||||||
return result
|
set.status = 404
|
||||||
} catch (error) {
|
return { status: 'not_found', message: 'Device not found' }
|
||||||
if (error instanceof Error && error.message in deviceErrorResponseMap) {
|
|
||||||
const { status, response } = deviceErrorResponseMap[error.message]
|
|
||||||
set.status = status
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
return response as any
|
|
||||||
}
|
|
||||||
set.status = 500
|
|
||||||
return { status: 'internal_error', message: 'Server error' }
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}, {
|
}, {
|
||||||
body: deviceCommandBody,
|
body: deviceCommandBody,
|
||||||
response: deviceCommandResponseSchema,
|
response: deviceCommandResponseSchema,
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ export type DeviceQueryParams = z.infer<typeof deviceQueryParams>
|
|||||||
// Response schemas
|
// Response schemas
|
||||||
export const deviceCommandResponseSchema = {
|
export const deviceCommandResponseSchema = {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
status: z.literal('success'),
|
status: z.string(),
|
||||||
message: z.literal('Command accepted'),
|
message: z.string(),
|
||||||
receivedAt: z.string().datetime(),
|
receivedAt: z.string().datetime(),
|
||||||
}),
|
}),
|
||||||
400: z.object({
|
400: z.object({
|
||||||
@@ -56,7 +56,7 @@ export const deviceCommandResponseSchema = {
|
|||||||
}),
|
}),
|
||||||
404: z.object({
|
404: z.object({
|
||||||
status: z.literal('not_found'),
|
status: z.literal('not_found'),
|
||||||
message: z.literal('Device status not found'),
|
message: z.literal('Device not found'),
|
||||||
}),
|
}),
|
||||||
500: z.object({
|
500: z.object({
|
||||||
status: z.literal('internal_error'),
|
status: z.literal('internal_error'),
|
||||||
|
|||||||
Reference in New Issue
Block a user