feat: allow setting DAILY VIDEO region via env (#21939)
* feat: allow setting DAILY VIDEO region via env * add comments * Cleaner approach to Daily video region type assertion --------- Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
@@ -42,6 +42,35 @@ export interface DailyVideoCallData {
|
||||
url: string;
|
||||
}
|
||||
|
||||
// Regions available to create DailyVideo Rooms in.
|
||||
const REGION_CODES = [
|
||||
"af-south-1",
|
||||
"ap-northeast-2",
|
||||
"ap-southeast-1",
|
||||
"ap-southeast-2",
|
||||
"ap-south-1",
|
||||
"eu-central-1",
|
||||
"eu-west-2",
|
||||
"sa-east-1",
|
||||
"us-east-1",
|
||||
"us-west-2",
|
||||
] as const;
|
||||
|
||||
type RoomGeo = (typeof REGION_CODES)[number];
|
||||
|
||||
function getDailyVideoRegionFromEnv(): RoomGeo | undefined {
|
||||
if (!process?.env?.DAILY_VIDEO_REGION) return;
|
||||
const isRoomGeo = (value: string): value is RoomGeo => REGION_CODES.includes(value as RoomGeo);
|
||||
function assertIsDailyVideoRegion(value: string): asserts value is RoomGeo {
|
||||
if (!isRoomGeo(value)) {
|
||||
throw new Error(`Invalid region code: ${value}. Must be one of: ${REGION_CODES.join(", ")}`);
|
||||
}
|
||||
}
|
||||
const region = process.env.DAILY_VIDEO_REGION;
|
||||
assertIsDailyVideoRegion(region);
|
||||
return region;
|
||||
}
|
||||
|
||||
const isS3StorageEnabled =
|
||||
process.env.CAL_VIDEO_BUCKET_NAME &&
|
||||
process.env.CAL_VIDEO_BUCKET_REGION &&
|
||||
@@ -203,11 +232,15 @@ export const setEnableRecordingUIAndUserIdForOrganizer = async (
|
||||
};
|
||||
|
||||
const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
async function createOrUpdateMeeting(endpoint: string, event: CalendarEvent): Promise<VideoCallData> {
|
||||
async function createOrUpdateMeeting(
|
||||
endpoint: string,
|
||||
event: CalendarEvent,
|
||||
region?: RoomGeo
|
||||
): Promise<VideoCallData> {
|
||||
if (!event.uid) {
|
||||
throw new Error("We need need the booking uid to create the Daily reference in DB");
|
||||
}
|
||||
const body = await translateEvent(event);
|
||||
const body = await translateEvent(event, region);
|
||||
const dailyEvent = await postToDailyAPI(endpoint, body).then(dailyReturnTypeSchema.parse);
|
||||
const meetingToken = await postToDailyAPI("/meeting-tokens", {
|
||||
properties: {
|
||||
@@ -226,7 +259,7 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
});
|
||||
}
|
||||
|
||||
const translateEvent = async (event: CalendarEvent) => {
|
||||
const translateEvent = async (event: CalendarEvent, region?: RoomGeo) => {
|
||||
// Documentation at: https://docs.daily.co/reference#list-rooms
|
||||
// Adds 14 days from the end of the booking as the expiration date
|
||||
const exp = Math.round(new Date(event.endTime).getTime() / 1000) + 60 * 60 * 24 * 14;
|
||||
@@ -248,6 +281,7 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
return {
|
||||
privacy: "public",
|
||||
properties: {
|
||||
...(region ? { geo: region } : {}),
|
||||
enable_prejoin_ui: true,
|
||||
enable_knocking: true,
|
||||
enable_screenshare: true,
|
||||
@@ -284,7 +318,7 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
};
|
||||
};
|
||||
|
||||
async function createInstantMeeting(endTime: string) {
|
||||
async function createInstantMeeting(endTime: string, region?: RoomGeo) {
|
||||
// added a 1 hour buffer for room expiration
|
||||
const exp = Math.round(new Date(endTime).getTime() / 1000) + 60 * 60;
|
||||
const { scale_plan: scalePlan } = await getDailyAppKeys();
|
||||
@@ -296,6 +330,7 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
const body = {
|
||||
privacy: "public",
|
||||
properties: {
|
||||
...(region ? { geo: region } : {}),
|
||||
enable_prejoin_ui: true,
|
||||
enable_knocking: true,
|
||||
enable_screenshare: true,
|
||||
@@ -347,20 +382,22 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
url: dailyEvent.url,
|
||||
});
|
||||
}
|
||||
|
||||
// Region on which the DailyVideo room is created can be controlled by ENV var
|
||||
// undefined region leaves the choice to DailyVideo
|
||||
const region = getDailyVideoRegionFromEnv();
|
||||
return {
|
||||
/** Daily doesn't need to return busy times, so we return empty */
|
||||
getAvailability: () => {
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
createMeeting: async (event: CalendarEvent): Promise<VideoCallData> =>
|
||||
createOrUpdateMeeting("/rooms", event),
|
||||
createOrUpdateMeeting("/rooms", event, region),
|
||||
deleteMeeting: async (uid: string): Promise<void> => {
|
||||
await fetcher(`/rooms/${uid}`, { method: "DELETE" });
|
||||
return Promise.resolve();
|
||||
},
|
||||
updateMeeting: (bookingRef: PartialReference, event: CalendarEvent): Promise<VideoCallData> =>
|
||||
createOrUpdateMeeting(`/rooms/${bookingRef.uid}`, event),
|
||||
createOrUpdateMeeting(`/rooms/${bookingRef.uid}`, event, region),
|
||||
getRecordings: async (roomName: string): Promise<GetRecordingsResponseSchema> => {
|
||||
try {
|
||||
const res = await fetcher(`/recordings?room_name=${roomName}`).then(
|
||||
@@ -371,7 +408,7 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
||||
throw new Error("Something went wrong! Unable to get recording");
|
||||
}
|
||||
},
|
||||
createInstantCalVideoRoom: (endTime: string) => createInstantMeeting(endTime),
|
||||
createInstantCalVideoRoom: (endTime: string) => createInstantMeeting(endTime, region),
|
||||
getRecordingDownloadLink: async (recordingId: string): Promise<GetAccessLinkResponseSchema> => {
|
||||
try {
|
||||
const res = await fetcher(`/recordings/${recordingId}/access-link?valid_for_secs=43200`).then(
|
||||
|
||||
@@ -281,6 +281,7 @@
|
||||
"DAILY_SCALE_PLAN",
|
||||
"DAILY_WEBHOOK_SECRET",
|
||||
"DAILY_MEETING_ENDED_WEBHOOK_SECRET",
|
||||
"DAILY_VIDEO_REGION",
|
||||
"DATABASE_CHUNK_SIZE",
|
||||
"DATABASE_DIRECT_URL",
|
||||
"DATABASE_URL",
|
||||
|
||||
Reference in New Issue
Block a user