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:
Morgan
2025-06-25 18:01:40 +03:00
committed by GitHub
co-authored by Alex van Andel
parent 2303eb7908
commit 2f1f989266
2 changed files with 46 additions and 8 deletions
@@ -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(
+1
View File
@@ -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",