refactor: rooms => zones

This commit is contained in:
Andy Burke 2025-09-09 15:32:07 -07:00
parent 525568d368
commit 20a5d1bc88
27 changed files with 2466 additions and 2044 deletions

View file

@ -1,23 +0,0 @@
# /api/rooms/:room_id
Interact with a specific room.
## GET /api/rooms/:room_id
Get the room specified by `:room_id`.
## PUT /api/rooms/:room_id
Update the rooms specified by `:room_id`.
Eg:
```
{
name?: string;
}
```
## DELETE /api/rooms/:room_id
Delete the room specified by `:room_id`.

View file

@ -1,15 +0,0 @@
# /api/rooms/:room_id/events
Interact with a events for a room.
## GET /api/rooms/:room_id/events
Get events for the given room.
## PUT /api/rooms/:room_id/events/:event_id
Update an event.
## DELETE /api/rooms/:room_id/events/:event_id
Delete an event.

View file

@ -1,28 +0,0 @@
# /api/rooms
Interact with rooms.
## POST /api/rooms
Create a new room.
```
export type ROOM = {
id: string; // unique id for this room
name: string; // the name of the room (max 128 characters)
icon_url?: string; // optional url for a room icon
topic?: string; // optional room topic
tags: string[]; // a list of tags for the room
meta: Record<string, any>;
limits: {
users: number;
user_messages_per_minute: number;
};
creator_id: string; // user_id of the room creator
emojis: Record<string, string>; // either: string: emoji eg: { 'rofl: 🤣, ... } or { 'rofl': 🤣, 'blap': 'https://somewhere.someplace/image.jpg' }
};
```
## GET /api/rooms
Get rooms.

View file

@ -13,7 +13,7 @@ const DEFAULT_USER_PERMISSIONS: string[] = [
'files.write.own',
'self.read',
'self.write',
'rooms.read',
'zones.read',
'users.read'
];

View file

@ -0,0 +1,23 @@
# /api/zones/:zone_id
Interact with a specific zone.
## GET /api/zones/:zone_id
Get the zone specified by `:zone_id`.
## PUT /api/zones/:zone_id
Update the zones specified by `:zone_id`.
Eg:
```
{
name?: string;
}
```
## DELETE /api/zones/:zone_id
Delete the zone specified by `:zone_id`.

View file

@ -1,36 +1,36 @@
import { FSDB_COLLECTION } from '@andyburke/fsdb';
import { EVENT, get_events_collection_for_room } from '../../../../../../models/event.ts';
import { ROOM, ROOMS } from '../../../../../../models/room.ts';
import { EVENT, get_events_collection_for_zone } from '../../../../../../models/event.ts';
import { ZONE, ZONES } from '../../../../../../models/zone.ts';
import parse_body from '../../../../../../utils/bodyparser.ts';
import * as CANNED_RESPONSES from '../../../../../../utils/canned_responses.ts';
import { get_session, get_user, PRECHECK_TABLE, require_user } from '../../../../../../utils/prechecks.ts';
export const PRECHECKS: PRECHECK_TABLE = {};
// GET /api/rooms/:room_id/events/:id - Get an event
// GET /api/zones/:zone_id/events/:id - Get an event
PRECHECKS.GET = [get_session, get_user, require_user, async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const room_is_public = room.permissions.read.length === 0;
const user_has_read_for_room = room_is_public || room.permissions.read.includes(meta.user.id);
const room_has_public_events = user_has_read_for_room && (room.permissions.read_events.length === 0);
const user_has_read_events_for_room = user_has_read_for_room &&
(room_has_public_events || room.permissions.read_events.includes(meta.user.id));
meta.zone = zone;
const zone_is_public = zone.permissions.read.length === 0;
const user_has_read_for_zone = zone_is_public || zone.permissions.read.includes(meta.user.id);
const zone_has_public_events = user_has_read_for_zone && (zone.permissions.read_events.length === 0);
const user_has_read_events_for_zone = user_has_read_for_zone &&
(zone_has_public_events || zone.permissions.read_events.includes(meta.user.id));
if (!user_has_read_events_for_room) {
if (!user_has_read_events_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}];
export async function GET(_req: Request, meta: Record<string, any>): Promise<Response> {
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_room(meta.room.id);
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_zone(meta.zone.id);
const event: EVENT | null = await events.get(meta.params.event_id);
if (!event) {
@ -42,7 +42,7 @@ export async function GET(_req: Request, meta: Record<string, any>): Promise<Res
});
}
// PUT /api/rooms/:room_id/events/:event_id - Update event
// PUT /api/zones/:zone_id/events/:event_id - Update event
PRECHECKS.PUT = [
get_session,
get_user,
@ -53,23 +53,23 @@ PRECHECKS.PUT = [
}
},
async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const room_is_public: boolean = meta.room.permissions.read.length === 0;
const user_has_read_for_room = room_is_public || meta.room.permissions.read.includes(meta.user.id);
const room_events_are_publicly_writable = meta.room.permissions.write_events.length === 0;
const user_has_write_events_for_room = user_has_read_for_room &&
(room_events_are_publicly_writable || meta.room.permissions.write_events.includes(meta.user.id));
meta.zone = zone;
const zone_is_public: boolean = meta.zone.permissions.read.length === 0;
const user_has_read_for_zone = zone_is_public || meta.zone.permissions.read.includes(meta.user.id);
const zone_events_are_publicly_writable = meta.zone.permissions.write_events.length === 0;
const user_has_write_events_for_zone = user_has_read_for_zone &&
(zone_events_are_publicly_writable || meta.zone.permissions.write_events.includes(meta.user.id));
if (!user_has_write_events_for_room) {
if (!user_has_write_events_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}
@ -78,7 +78,7 @@ export async function PUT(req: Request, meta: Record<string, any>): Promise<Resp
const now = new Date().toISOString();
try {
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_room(meta.room.id);
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_zone(meta.zone.id);
const event: EVENT | null = await events.get(meta.params.event_id);
if (!event) {
@ -117,7 +117,7 @@ export async function PUT(req: Request, meta: Record<string, any>): Promise<Resp
}
}
// DELETE /api/rooms/:room_id/events/:event_id - Delete event
// DELETE /api/zones/:zone_id/events/:event_id - Delete event
PRECHECKS.DELETE = [
get_session,
get_user,
@ -128,29 +128,29 @@ PRECHECKS.DELETE = [
}
},
async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const room_is_public: boolean = meta.room.permissions.read.length === 0;
const user_has_read_for_room = room_is_public || meta.room.permissions.read.includes(meta.user.id);
const room_events_are_publicly_writable = meta.room.permissions.write_events.length === 0;
const user_has_write_events_for_room = user_has_read_for_room &&
(room_events_are_publicly_writable || meta.room.permissions.write_events.includes(meta.user.id));
meta.zone = zone;
const zone_is_public: boolean = meta.zone.permissions.read.length === 0;
const user_has_read_for_zone = zone_is_public || meta.zone.permissions.read.includes(meta.user.id);
const zone_events_are_publicly_writable = meta.zone.permissions.write_events.length === 0;
const user_has_write_events_for_zone = user_has_read_for_zone &&
(zone_events_are_publicly_writable || meta.zone.permissions.write_events.includes(meta.user.id));
if (!user_has_write_events_for_room) {
if (!user_has_write_events_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}
];
export async function DELETE(_req: Request, meta: Record<string, any>): Promise<Response> {
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_room(meta.room.id);
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_zone(meta.zone.id);
const event: EVENT | null = await events.get(meta.params.event_id);
if (!event) {
return CANNED_RESPONSES.not_found();

View file

@ -0,0 +1,15 @@
# /api/zones/:zone_id/events
Interact with a events for a zone.
## GET /api/zones/:zone_id/events
Get events for the given zone.
## PUT /api/zones/:zone_id/events/:event_id
Update an event.
## DELETE /api/zones/:zone_id/events/:event_id
Delete an event.

View file

@ -1,40 +1,40 @@
import lurid from '@andyburke/lurid';
import { get_session, get_user, PRECHECK_TABLE, require_user } from '../../../../../utils/prechecks.ts';
import { ROOM, ROOMS } from '../../../../../models/room.ts';
import { ZONE, ZONES } from '../../../../../models/zone.ts';
import * as CANNED_RESPONSES from '../../../../../utils/canned_responses.ts';
import { EVENT, get_events_collection_for_room } from '../../../../../models/event.ts';
import { EVENT, get_events_collection_for_zone } from '../../../../../models/event.ts';
import parse_body from '../../../../../utils/bodyparser.ts';
import { FSDB_COLLECTION, FSDB_SEARCH_OPTIONS, WALK_ENTRY } from '@andyburke/fsdb';
import * as path from '@std/path';
export const PRECHECKS: PRECHECK_TABLE = {};
// GET /api/rooms/:room_id/events - get room events
// GET /api/zones/:zone_id/events - get zone events
// query parameters:
// partial_id: the partial id subset you would like to match (remember, lurids are lexigraphically sorted)
PRECHECKS.GET = [get_session, get_user, require_user, async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const room_is_public: boolean = meta.room.permissions.read.length === 0;
const user_has_read_for_room = room_is_public || meta.room.permissions.read.includes(meta.user.id);
const room_events_are_public = meta.room.permissions.read_events.length === 0;
const user_has_read_events_for_room = user_has_read_for_room &&
(room_events_are_public || meta.room.permissions.read_events.includes(meta.user.id));
meta.zone = zone;
const zone_is_public: boolean = meta.zone.permissions.read.length === 0;
const user_has_read_for_zone = zone_is_public || meta.zone.permissions.read.includes(meta.user.id);
const zone_events_are_public = meta.zone.permissions.read_events.length === 0;
const user_has_read_events_for_zone = user_has_read_for_zone &&
(zone_events_are_public || meta.zone.permissions.read_events.includes(meta.user.id));
if (!user_has_read_events_for_room) {
if (!user_has_read_events_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}];
export async function GET(request: Request, meta: Record<string, any>): Promise<Response> {
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_room(meta.room.id);
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_zone(meta.zone.id);
const sorts = events.sorts;
const sort_name: string = meta.query.sort ?? 'newest';
@ -126,31 +126,31 @@ export async function GET(request: Request, meta: Record<string, any>): Promise<
});
}
// POST /api/rooms/:room_id/events - Create an event
// POST /api/zones/:zone_id/events - Create an event
PRECHECKS.POST = [get_session, get_user, require_user, async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const room_is_public: boolean = meta.room.permissions.read.length === 0;
const user_has_read_for_room = room_is_public || meta.room.permissions.read.includes(meta.user.id);
const room_events_are_publicly_writable = meta.room.permissions.write_events.length === 0;
const user_has_write_events_for_room = user_has_read_for_room &&
(room_events_are_publicly_writable || meta.room.permissions.write_events.includes(meta.user.id));
meta.zone = zone;
const zone_is_public: boolean = meta.zone.permissions.read.length === 0;
const user_has_read_for_zone = zone_is_public || meta.zone.permissions.read.includes(meta.user.id);
const zone_events_are_publicly_writable = meta.zone.permissions.write_events.length === 0;
const user_has_write_events_for_zone = user_has_read_for_zone &&
(zone_events_are_publicly_writable || meta.zone.permissions.write_events.includes(meta.user.id));
if (!user_has_write_events_for_room) {
if (!user_has_write_events_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}];
export async function POST(req: Request, meta: Record<string, any>): Promise<Response> {
try {
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_room(meta.room.id);
const events: FSDB_COLLECTION<EVENT> = get_events_collection_for_zone(meta.zone.id);
const now = new Date().toISOString();

View file

@ -1,50 +1,50 @@
import { get_session, get_user, PRECHECK_TABLE, require_user } from '../../../../utils/prechecks.ts';
import parse_body from '../../../../utils/bodyparser.ts';
import * as CANNED_RESPONSES from '../../../../utils/canned_responses.ts';
import { ROOM, ROOMS } from '../../../../models/room.ts';
import { ZONE, ZONES } from '../../../../models/zone.ts';
export const PRECHECKS: PRECHECK_TABLE = {};
// GET /api/rooms/:id - Get a room
// GET /api/zones/:id - Get a zone
PRECHECKS.GET = [get_session, get_user, require_user, async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const room_is_public = room.permissions.read.length === 0;
const user_has_read_for_room = room_is_public || room.permissions.read.includes(meta.user.id);
meta.zone = zone;
const zone_is_public = zone.permissions.read.length === 0;
const user_has_read_for_zone = zone_is_public || zone.permissions.read.includes(meta.user.id);
if (!user_has_read_for_room) {
if (!user_has_read_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}];
export function GET(_req: Request, meta: Record<string, any>): Response {
return Response.json(meta.room, {
return Response.json(meta.zone, {
status: 200
});
}
// PUT /api/rooms/:id - Update room
// PUT /api/zones/:id - Update zone
PRECHECKS.PUT = [get_session, get_user, require_user, async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const user_has_write_for_room = room.permissions.write.includes(meta.user.id);
meta.zone = zone;
const user_has_write_for_zone = zone.permissions.write.includes(meta.user.id);
if (!user_has_write_for_room) {
if (!user_has_write_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}];
@ -54,16 +54,16 @@ export async function PUT(req: Request, meta: Record<string, any>): Promise<Resp
try {
const body = await parse_body(req);
const updated = {
...meta.room,
...meta.zone,
...body,
id: meta.room.id,
id: meta.zone.id,
timestamps: {
created: meta.room.timestamps.created,
created: meta.zone.timestamps.created,
updated: now
}
};
await ROOMS.update(updated);
await ZONES.update(updated);
return Response.json(updated, {
status: 200
});
@ -79,31 +79,31 @@ export async function PUT(req: Request, meta: Record<string, any>): Promise<Resp
}
}
// DELETE /api/rooms/:id - Delete room
// DELETE /api/zones/:id - Delete zone
PRECHECKS.DELETE = [
get_session,
get_user,
require_user,
async (_req: Request, meta: Record<string, any>): Promise<Response | undefined> => {
const room_id: string = meta.params?.room_id?.toLowerCase().trim() ?? '';
const zone_id: string = meta.params?.zone_id?.toLowerCase().trim() ?? '';
// lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same"
const room: ROOM | null = room_id.length === 49 ? await ROOMS.get(room_id) : null;
const zone: ZONE | null = zone_id.length === 49 ? await ZONES.get(zone_id) : null;
if (!room) {
if (!zone) {
return CANNED_RESPONSES.not_found();
}
meta.room = room;
const user_has_write_for_room = room.permissions.write.includes(meta.user.id);
meta.zone = zone;
const user_has_write_for_zone = zone.permissions.write.includes(meta.user.id);
if (!user_has_write_for_room) {
if (!user_has_write_for_zone) {
return CANNED_RESPONSES.permission_denied();
}
}
];
export async function DELETE(_req: Request, meta: Record<string, any>): Promise<Response> {
await ROOMS.delete(meta.room);
await ZONES.delete(meta.zone);
return Response.json({
deleted: true

View file

@ -0,0 +1,28 @@
# /api/zones
Interact with zones.
## POST /api/zones
Create a new zone.
```
export type ZONE = {
id: string; // unique id for this zone
name: string; // the name of the zone (max 128 characters)
icon_url?: string; // optional url for a zone icon
topic?: string; // optional zone topic
tags: string[]; // a list of tags for the zone
meta: Record<string, any>;
limits: {
users: number;
user_messages_per_minute: number;
};
creator_id: string; // user_id of the zone creator
emojis: Record<string, string>; // either: string: emoji eg: { 'rofl: 🤣, ... } or { 'rofl': 🤣, 'blap': 'https://somewhere.someplace/image.jpg' }
};
```
## GET /api/zones
Get zones.

View file

@ -3,40 +3,40 @@ import parse_body from '../../../utils/bodyparser.ts';
import { get_session, get_user, require_user } from '../../../utils/prechecks.ts';
import * as CANNED_RESPONSES from '../../../utils/canned_responses.ts';
import { PRECHECK_TABLE } from '../../../utils/prechecks.ts';
import { ROOM, ROOMS } from '../../../models/room.ts';
import { ZONE, ZONES } from '../../../models/zone.ts';
import { WALK_ENTRY } from '@andyburke/fsdb';
export const PRECHECKS: PRECHECK_TABLE = {};
// GET /api/rooms - get rooms
// GET /api/zones - get zones
PRECHECKS.GET = [get_session, get_user, require_user, (_req: Request, meta: Record<string, any>): Response | undefined => {
const can_read_rooms = meta.user.permissions.includes('rooms.read');
const can_read_zones = meta.user.permissions.includes('zones.read');
if (!can_read_rooms) {
if (!can_read_zones) {
return CANNED_RESPONSES.permission_denied();
}
}];
export async function GET(_req: Request, meta: Record<string, any>): Promise<Response> {
const limit = Math.min(parseInt(meta.query.limit ?? '100'), 100);
const rooms = (await ROOMS.all({
const zones = (await ZONES.all({
limit,
filter: (entry: WALK_ENTRY<ROOM>) => {
// we push our event collections into the rooms, and fsdb
filter: (entry: WALK_ENTRY<ZONE>) => {
// we push our event collections into the zones, and fsdb
// doesn't yet filter that out in its all() logic
return entry.path.indexOf('/events/') === -1;
}
})).map((room_entry) => room_entry.load());
})).map((zone_entry) => zone_entry.load());
return Response.json(rooms, {
return Response.json(zones, {
status: 200
});
}
// POST /api/rooms - Create a room
// POST /api/zones - Create a zone
PRECHECKS.POST = [get_session, get_user, require_user, (_req: Request, meta: Record<string, any>): Response | undefined => {
const can_create_rooms = meta.user.permissions.includes('rooms.create');
const can_create_zones = meta.user.permissions.includes('zones.create');
if (!can_create_rooms) {
if (!can_create_zones) {
return CANNED_RESPONSES.permission_denied();
}
}];
@ -49,8 +49,8 @@ export async function POST(req: Request, meta: Record<string, any>): Promise<Res
if (typeof body.name !== 'string' || body.name.length === 0) {
return Response.json({
error: {
cause: 'missing_room_name',
message: 'You must specify a unique name for a room.'
cause: 'missing_zone_name',
message: 'You must specify a unique name for a zone.'
}
}, {
status: 400
@ -60,8 +60,8 @@ export async function POST(req: Request, meta: Record<string, any>): Promise<Res
if (body.name.length > 64) {
return Response.json({
error: {
cause: 'invalid_room_name',
message: 'Room names must be 64 characters or fewer.'
cause: 'invalid_zone_name',
message: 'zone names must be 64 characters or fewer.'
}
}, {
status: 400
@ -70,21 +70,21 @@ export async function POST(req: Request, meta: Record<string, any>): Promise<Res
const normalized_name = body.name.toLowerCase();
const existing_room = (await ROOMS.find({
const existing_zone = (await ZONES.find({
name: normalized_name
})).shift();
if (existing_room) {
if (existing_zone) {
return Response.json({
error: {
cause: 'room_name_conflict',
message: 'There is already a room with this name.'
cause: 'zone_name_conflict',
message: 'There is already a zone with this name.'
}
}, {
status: 400
});
}
const room: ROOM = {
const zone: ZONE = {
...body,
id: lurid(),
creator_id: meta.user.id,
@ -101,9 +101,9 @@ export async function POST(req: Request, meta: Record<string, any>): Promise<Res
}
};
await ROOMS.create(room);
await ZONES.create(zone);
return Response.json(room, {
return Response.json(zone, {
status: 201
});
} catch (error) {