Skip to content
Merged
23 changes: 21 additions & 2 deletions app/lib/devices-service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,27 @@ export const CreateBoxSchema = z.object({
.optional()
.default('unknown'),
location: z
.array(z.number())
.length(2, 'Location must be [longitude, latitude]'),
.union([
z
.array(z.number())
.min(
2,
'Location must be [longitude, latitude, (height)] (height is optional)',
)
.max(
3,
'Location must be [longitude, latitude, (height)] (height is optional)',
),
z.object({
lng: z.number(),
lat: z.number(),
height: z.number().optional(),
}),
])
.transform((loc) => {
if (Array.isArray(loc)) return loc
return [loc.lng, loc.lat, ...(loc.height ? [loc.height] : [])]
}),
grouptag: z.array(z.string()).optional().default([]),
model: z
.enum([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { type ActionFunctionArgs, type LoaderFunctionArgs } from 'react-router'
import { transformDeviceToApiFormat } from '~/lib/device-transform'
import { deleteDevice } from '~/lib/devices-service.server'
import { getUserFromJwt } from '~/lib/jwt'
import {
DeviceUpdateError,
getDevice,
updateDevice,
type UpdateDeviceArgs,
} from '~/models/device.server'
import { type Device, type User } from '~/schema'
import { StandardResponse } from '~/utils/response-utils'

/**
Expand Down Expand Up @@ -122,6 +124,8 @@ export async function action({ request, params }: ActionFunctionArgs) {
switch (request.method) {
case 'PUT':
return await put(request, jwtResponse, deviceId)
case 'DELETE':
return await del(request, jwtResponse, deviceId)
default:
return Response.json({ message: 'Method Not Allowed' }, { status: 405 })
}
Expand Down Expand Up @@ -252,3 +256,28 @@ async function put(request: Request, user: any, deviceId: string) {
)
}
}

async function del(request: Request, user: User, deviceId: string) {
const device = (await getDevice({ id: deviceId })) as unknown as Device

if (!device) throw StandardResponse.notFound('Device not found')

const body = await request.json()

if (!body.password)
throw StandardResponse.badRequest(
'Password is required for device deletion',
)

try {
const deleted = await deleteDevice(user, device, body.password)

if (deleted === 'unauthorized')
return StandardResponse.unauthorized('Password incorrect')

return StandardResponse.ok(null)
} catch (err) {
console.warn(err)
return StandardResponse.internalServerError()
}
}
55 changes: 51 additions & 4 deletions app/routes/api.boxes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { type ActionFunction, type ActionFunctionArgs } from 'react-router'
import {

Check warning on line 1 in app/routes/api.boxes.ts

View workflow job for this annotation

GitHub Actions / ⬣ Lint

All imports in the declaration are only used as types. Use `import type`
LoaderFunctionArgs,
type ActionFunction,
type ActionFunctionArgs,
} from 'react-router'
import { transformDeviceToApiFormat } from '~/lib/device-transform'
import { CreateBoxSchema } from '~/lib/devices-service.server'
import { BoxesQuerySchema, CreateBoxSchema } from '~/lib/devices-service.server'
import { getUserFromJwt } from '~/lib/jwt'
import { createDevice } from '~/models/device.server'
import { type User } from '~/schema'
import {
createDevice,
findDevices,
FindDevicesOptions,
} from '~/models/device.server'
import { Device, type User } from '~/schema'
import { StandardResponse } from '~/utils/response-utils'

/**
Expand Down Expand Up @@ -323,6 +331,45 @@
* type: string
* example: "25.13"
*/
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url)
const queryObj = Object.fromEntries(url.searchParams)
const parseResult = BoxesQuerySchema.safeParse(queryObj)

if (!parseResult.success) {
const { fieldErrors } = parseResult.error.flatten()
if (fieldErrors.format)
throw StandardResponse.unprocessableContent('Invalid format parameter')

throw StandardResponse.unprocessableContent(
`${parseResult.error.flatten()}`,
)
}

const params: FindDevicesOptions = parseResult.data

const devices = await findDevices(params)

if (params.format === 'geojson') {
const geojson = {
type: 'FeatureCollection',
features: devices.map((device: Device) => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [device.longitude, device.latitude],
},
properties: {
...device,
},
})),
}

return geojson
} else {
return devices
}
}

export const action: ActionFunction = async ({
request,
Expand Down
Loading
Loading