mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 12:19:06 +00:00
Permanent room urls
This commit is contained in:
@@ -5,26 +5,22 @@ Revises: b9348748bbbc
|
|||||||
Create Date: 2024-07-31 16:41:29.415218
|
Create Date: 2024-07-31 16:41:29.415218
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '1340c04426b8'
|
revision: str = "1340c04426b8"
|
||||||
down_revision: Union[str, None] = 'b9348748bbbc'
|
down_revision: Union[str, None] = "b9348748bbbc"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
op.add_column("transcript", sa.Column("meeting_id", sa.String(), nullable=True))
|
||||||
pass
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
op.drop_column("transcript", "meeting_id")
|
||||||
pass
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from reflector.logger import logger
|
|||||||
from reflector.metrics import metrics_init
|
from reflector.metrics import metrics_init
|
||||||
from reflector.settings import settings
|
from reflector.settings import settings
|
||||||
from reflector.views.meetings import router as meetings_router
|
from reflector.views.meetings import router as meetings_router
|
||||||
|
from reflector.views.rooms import router as rooms_router
|
||||||
from reflector.views.rtc_offer import router as rtc_offer_router
|
from reflector.views.rtc_offer import router as rtc_offer_router
|
||||||
from reflector.views.transcripts import router as transcripts_router
|
from reflector.views.transcripts import router as transcripts_router
|
||||||
from reflector.views.transcripts_audio import router as transcripts_audio_router
|
from reflector.views.transcripts_audio import router as transcripts_audio_router
|
||||||
@@ -70,6 +71,7 @@ metrics_init(app, instrumentator)
|
|||||||
# register views
|
# register views
|
||||||
app.include_router(rtc_offer_router)
|
app.include_router(rtc_offer_router)
|
||||||
app.include_router(meetings_router, prefix="/v1")
|
app.include_router(meetings_router, prefix="/v1")
|
||||||
|
app.include_router(rooms_router, prefix="/v1")
|
||||||
app.include_router(transcripts_router, prefix="/v1")
|
app.include_router(transcripts_router, prefix="/v1")
|
||||||
app.include_router(transcripts_audio_router, prefix="/v1")
|
app.include_router(transcripts_audio_router, prefix="/v1")
|
||||||
app.include_router(transcripts_participants_router, prefix="/v1")
|
app.include_router(transcripts_participants_router, prefix="/v1")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ metadata = sqlalchemy.MetaData()
|
|||||||
|
|
||||||
# import models
|
# import models
|
||||||
import reflector.db.meetings # noqa
|
import reflector.db.meetings # noqa
|
||||||
|
import reflector.db.rooms # noqa
|
||||||
import reflector.db.transcripts # noqa
|
import reflector.db.transcripts # noqa
|
||||||
|
|
||||||
engine = sqlalchemy.create_engine(
|
engine = sqlalchemy.create_engine(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ meetings = sqlalchemy.Table(
|
|||||||
sqlalchemy.Column("start_date", sqlalchemy.DateTime),
|
sqlalchemy.Column("start_date", sqlalchemy.DateTime),
|
||||||
sqlalchemy.Column("end_date", sqlalchemy.DateTime),
|
sqlalchemy.Column("end_date", sqlalchemy.DateTime),
|
||||||
sqlalchemy.Column("user_id", sqlalchemy.String),
|
sqlalchemy.Column("user_id", sqlalchemy.String),
|
||||||
|
sqlalchemy.Column("room_id", sqlalchemy.String),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -28,10 +29,11 @@ class Meeting(BaseModel):
|
|||||||
start_date: datetime
|
start_date: datetime
|
||||||
end_date: datetime
|
end_date: datetime
|
||||||
user_id: str
|
user_id: str
|
||||||
|
room_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class MeetingController:
|
class MeetingController:
|
||||||
async def add(
|
async def create(
|
||||||
self,
|
self,
|
||||||
id: str,
|
id: str,
|
||||||
room_name: str,
|
room_name: str,
|
||||||
@@ -41,9 +43,10 @@ class MeetingController:
|
|||||||
start_date: datetime,
|
start_date: datetime,
|
||||||
end_date: datetime,
|
end_date: datetime,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
|
room_id: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add a new meeting
|
Create a new meeting
|
||||||
"""
|
"""
|
||||||
meeting = Meeting(
|
meeting = Meeting(
|
||||||
id=id,
|
id=id,
|
||||||
@@ -54,6 +57,7 @@ class MeetingController:
|
|||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
room_id=room_id,
|
||||||
)
|
)
|
||||||
query = meetings.insert().values(**meeting.model_dump())
|
query = meetings.insert().values(**meeting.model_dump())
|
||||||
await database.execute(query)
|
await database.execute(query)
|
||||||
@@ -73,6 +77,20 @@ class MeetingController:
|
|||||||
|
|
||||||
return Meeting(**result)
|
return Meeting(**result)
|
||||||
|
|
||||||
|
async def get_latest(self, room_id: str) -> Meeting:
|
||||||
|
"""
|
||||||
|
Get latest meeting for a room.
|
||||||
|
"""
|
||||||
|
start_date = getattr(meetings.c, "start_date").desc()
|
||||||
|
query = (
|
||||||
|
meetings.select().where(meetings.c.room_id == room_id).order_by(start_date)
|
||||||
|
)
|
||||||
|
result = await database.fetch_one(query)
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Meeting(**result)
|
||||||
|
|
||||||
async def get_by_id_for_http(self, meeting_id: str, user_id: str | None) -> Meeting:
|
async def get_by_id_for_http(self, meeting_id: str, user_id: str | None) -> Meeting:
|
||||||
"""
|
"""
|
||||||
Get a meeting by ID for HTTP request.
|
Get a meeting by ID for HTTP request.
|
||||||
|
|||||||
139
server/reflector/db/rooms.py
Normal file
139
server/reflector/db/rooms.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
from fastapi import HTTPException
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
from reflector.db import database, metadata
|
||||||
|
from reflector.db.transcripts import generate_uuid4
|
||||||
|
from sqlalchemy.sql import false
|
||||||
|
|
||||||
|
rooms = sqlalchemy.Table(
|
||||||
|
"room",
|
||||||
|
metadata,
|
||||||
|
sqlalchemy.Column("id", sqlalchemy.String, primary_key=True),
|
||||||
|
sqlalchemy.Column("name", sqlalchemy.String, nullable=False),
|
||||||
|
sqlalchemy.Column("user_id", sqlalchemy.String, nullable=False),
|
||||||
|
sqlalchemy.Column("created_at", sqlalchemy.DateTime, nullable=False),
|
||||||
|
sqlalchemy.Column(
|
||||||
|
"zulip_auto_post", sqlalchemy.Boolean, nullable=False, server_default=false()
|
||||||
|
),
|
||||||
|
sqlalchemy.Column("zulip_stream", sqlalchemy.String),
|
||||||
|
sqlalchemy.Column("zulip_topic", sqlalchemy.String),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Room(BaseModel):
|
||||||
|
id: str = Field(default_factory=generate_uuid4)
|
||||||
|
name: str
|
||||||
|
user_id: str
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
zulip_auto_post: bool = False
|
||||||
|
zulip_stream: str = ""
|
||||||
|
zulip_topic: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class RoomController:
|
||||||
|
async def get_all(
|
||||||
|
self,
|
||||||
|
user_id: str | None = None,
|
||||||
|
order_by: str | None = None,
|
||||||
|
return_query: bool = False,
|
||||||
|
) -> list[Room]:
|
||||||
|
"""
|
||||||
|
Get all rooms
|
||||||
|
|
||||||
|
If `user_id` is specified, only return rooms that belong to the user.
|
||||||
|
Otherwise, return all rooms.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `order_by`: field to order by, e.g. "-created_at"
|
||||||
|
"""
|
||||||
|
query = rooms.select()
|
||||||
|
if user_id is not None:
|
||||||
|
query = query.where(rooms.c.user_id == user_id)
|
||||||
|
|
||||||
|
if order_by is not None:
|
||||||
|
field = getattr(rooms.c, order_by[1:])
|
||||||
|
if order_by.startswith("-"):
|
||||||
|
field = field.desc()
|
||||||
|
query = query.order_by(field)
|
||||||
|
|
||||||
|
if return_query:
|
||||||
|
return query
|
||||||
|
|
||||||
|
results = await database.fetch_all(query)
|
||||||
|
return results
|
||||||
|
|
||||||
|
async def add(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
user_id: str,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Add a new room
|
||||||
|
"""
|
||||||
|
room = Room(
|
||||||
|
name=name,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
query = rooms.insert().values(**room.model_dump())
|
||||||
|
await database.execute(query)
|
||||||
|
return room
|
||||||
|
|
||||||
|
async def get_by_id(self, room_id: str, **kwargs) -> Room | None:
|
||||||
|
"""
|
||||||
|
Get a room by id
|
||||||
|
"""
|
||||||
|
query = rooms.select().where(rooms.c.id == room_id)
|
||||||
|
if "user_id" in kwargs:
|
||||||
|
query = query.where(rooms.c.user_id == kwargs["user_id"])
|
||||||
|
result = await database.fetch_one(query)
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
return Room(**result)
|
||||||
|
|
||||||
|
async def get_by_name(self, room_name: str, **kwargs) -> Room | None:
|
||||||
|
"""
|
||||||
|
Get a room by name
|
||||||
|
"""
|
||||||
|
query = rooms.select().where(rooms.c.name == room_name)
|
||||||
|
if "user_id" in kwargs:
|
||||||
|
query = query.where(rooms.c.user_id == kwargs["user_id"])
|
||||||
|
result = await database.fetch_one(query)
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
return Room(**result)
|
||||||
|
|
||||||
|
async def get_by_id_for_http(self, meeting_id: str, user_id: str | None) -> Room:
|
||||||
|
"""
|
||||||
|
Get a room by ID for HTTP request.
|
||||||
|
|
||||||
|
If not found, it will raise a 404 error.
|
||||||
|
"""
|
||||||
|
query = rooms.select().where(rooms.c.id == meeting_id)
|
||||||
|
result = await database.fetch_one(query)
|
||||||
|
if not result:
|
||||||
|
raise HTTPException(status_code=404, detail="Room not found")
|
||||||
|
|
||||||
|
room = Room(**result)
|
||||||
|
|
||||||
|
return room
|
||||||
|
|
||||||
|
async def remove_by_id(
|
||||||
|
self,
|
||||||
|
room_id: str,
|
||||||
|
user_id: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Remove a room by id
|
||||||
|
"""
|
||||||
|
room = await self.get_by_id(room_id, user_id=user_id)
|
||||||
|
if not room:
|
||||||
|
return
|
||||||
|
if user_id is not None and room.user_id != user_id:
|
||||||
|
return
|
||||||
|
query = rooms.delete().where(rooms.c.id == room_id)
|
||||||
|
await database.execute(query)
|
||||||
|
|
||||||
|
|
||||||
|
rooms_controller = RoomController()
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Annotated, Optional
|
from typing import Annotated, Optional
|
||||||
|
|
||||||
import reflector.auth as auth
|
import reflector.auth as auth
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from reflector.db.meetings import meetings_controller
|
from reflector.db.meetings import meetings_controller
|
||||||
|
from reflector.whereby import create_meeting
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -26,3 +27,30 @@ async def meeting_get(
|
|||||||
):
|
):
|
||||||
user_id = user["sub"] if user else None
|
user_id = user["sub"] if user else None
|
||||||
return await meetings_controller.get_by_id_for_http(meeting_id, user_id=user_id)
|
return await meetings_controller.get_by_id_for_http(meeting_id, user_id=user_id)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/meetings/", response_model=GetMeeting)
|
||||||
|
async def meeting_create(
|
||||||
|
room_id: str,
|
||||||
|
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
|
||||||
|
):
|
||||||
|
user_id = user["sub"] if user else None
|
||||||
|
meeting = await meetings_controller.get_latest(room_id)
|
||||||
|
if meeting is None:
|
||||||
|
start_date = datetime.now(timezone.utc)
|
||||||
|
end_date = start_date + timedelta(minutes=1)
|
||||||
|
meeting = await create_meeting("", start_date=start_date, end_date=end_date)
|
||||||
|
|
||||||
|
meeting = await meetings_controller.add(
|
||||||
|
id=meeting["meetingId"],
|
||||||
|
room_name=meeting["roomName"],
|
||||||
|
room_url=meeting["roomUrl"],
|
||||||
|
host_room_url=meeting["hostRoomUrl"],
|
||||||
|
viewer_room_url=meeting["viewerRoomUrl"],
|
||||||
|
start_date=datetime.fromisoformat(meeting["startDate"]),
|
||||||
|
end_date=datetime.fromisoformat(meeting["endDate"]),
|
||||||
|
user_id=user_id,
|
||||||
|
room_id=room_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
return await meetings_controller.get_by_id_for_http(meeting.id, user_id=user_id)
|
||||||
|
|||||||
107
server/reflector/views/rooms.py
Normal file
107
server/reflector/views/rooms.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
from http.client import HTTPException
|
||||||
|
from typing import Annotated, Optional
|
||||||
|
|
||||||
|
import reflector.auth as auth
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from fastapi_pagination import Page
|
||||||
|
from fastapi_pagination.ext.databases import paginate
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from reflector.db import database
|
||||||
|
from reflector.db.meetings import meetings_controller
|
||||||
|
from reflector.db.rooms import rooms_controller
|
||||||
|
from reflector.settings import settings
|
||||||
|
from reflector.views.meetings import GetMeeting
|
||||||
|
from reflector.whereby import create_meeting
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
class Room(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
user_id: str
|
||||||
|
created_at: datetime
|
||||||
|
|
||||||
|
|
||||||
|
class CreateRoom(BaseModel):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
class DeletionStatus(BaseModel):
|
||||||
|
status: str
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/rooms", response_model=Page[Room])
|
||||||
|
async def rooms_list(
|
||||||
|
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
|
||||||
|
) -> list[Room]:
|
||||||
|
user_id = user["sub"] if user else None
|
||||||
|
|
||||||
|
if not user and not settings.PUBLIC_MODE:
|
||||||
|
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||||
|
|
||||||
|
user_id = user["sub"] if user else None
|
||||||
|
return await paginate(
|
||||||
|
database,
|
||||||
|
await rooms_controller.get_all(
|
||||||
|
user_id=user_id, order_by="-created_at", return_query=True
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/rooms", response_model=Room)
|
||||||
|
async def rooms_create(
|
||||||
|
room: CreateRoom,
|
||||||
|
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
|
||||||
|
):
|
||||||
|
user_id = user["sub"] if user else None
|
||||||
|
|
||||||
|
return await rooms_controller.add(
|
||||||
|
name=room.name,
|
||||||
|
user_id=user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/rooms/{room_id}", response_model=DeletionStatus)
|
||||||
|
async def rooms_delete(
|
||||||
|
room_id: str,
|
||||||
|
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
|
||||||
|
):
|
||||||
|
user_id = user["sub"] if user else None
|
||||||
|
room = await rooms_controller.get_by_id(room_id, user_id=user_id)
|
||||||
|
if not room:
|
||||||
|
raise HTTPException(status_code=404, detail="Room not found")
|
||||||
|
await rooms_controller.remove_by_id(room.id, user_id=user_id)
|
||||||
|
return DeletionStatus(status="ok")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/rooms/{room_name}/meeting", response_model=GetMeeting)
|
||||||
|
async def rooms_create_meeting(
|
||||||
|
room_name: str,
|
||||||
|
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
|
||||||
|
):
|
||||||
|
user_id = user["sub"] if user else None
|
||||||
|
room = await rooms_controller.get_by_name(room_name)
|
||||||
|
if not room:
|
||||||
|
raise HTTPException(status_code=404, detail="Room not found")
|
||||||
|
|
||||||
|
meeting = await meetings_controller.get_latest(room_id=room.id)
|
||||||
|
if meeting is None:
|
||||||
|
start_date = datetime.now(timezone.utc)
|
||||||
|
end_date = start_date + timedelta(minutes=1)
|
||||||
|
meeting = await create_meeting("", start_date=start_date, end_date=end_date)
|
||||||
|
|
||||||
|
meeting = await meetings_controller.create(
|
||||||
|
id=meeting["meetingId"],
|
||||||
|
room_name=meeting["roomName"],
|
||||||
|
room_url=meeting["roomUrl"],
|
||||||
|
host_room_url=meeting["hostRoomUrl"],
|
||||||
|
viewer_room_url=meeting["viewerRoomUrl"],
|
||||||
|
start_date=datetime.fromisoformat(meeting["startDate"]),
|
||||||
|
end_date=datetime.fromisoformat(meeting["endDate"]),
|
||||||
|
user_id=user_id,
|
||||||
|
room_id=room.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
return meeting
|
||||||
@@ -121,7 +121,7 @@ async def transcripts_create_meeting(
|
|||||||
end_date = start_date + timedelta(minutes=1)
|
end_date = start_date + timedelta(minutes=1)
|
||||||
meeting = await create_meeting("", start_date=start_date, end_date=end_date)
|
meeting = await create_meeting("", start_date=start_date, end_date=end_date)
|
||||||
|
|
||||||
meeting = await meetings_controller.add(
|
meeting = await meetings_controller.create(
|
||||||
id=meeting["meetingId"],
|
id=meeting["meetingId"],
|
||||||
room_name=meeting["roomName"],
|
room_name=meeting["roomName"],
|
||||||
room_url=meeting["roomUrl"],
|
room_url=meeting["roomUrl"],
|
||||||
@@ -133,7 +133,7 @@ async def transcripts_create_meeting(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return await transcripts_controller.add(
|
return await transcripts_controller.add(
|
||||||
info.name,
|
"",
|
||||||
source_language=info.source_language,
|
source_language=info.source_language,
|
||||||
target_language=info.target_language,
|
target_language=info.target_language,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
|
|||||||
@@ -65,6 +65,15 @@ async def process_recording(bucket_name: str, object_key: str):
|
|||||||
room_name = f"/{object_key[:36]}"
|
room_name = f"/{object_key[:36]}"
|
||||||
meeting = await meetings_controller.get_by_room_name(room_name)
|
meeting = await meetings_controller.get_by_room_name(room_name)
|
||||||
transcript = await transcripts_controller.get_by_meeting_id(meeting.id)
|
transcript = await transcripts_controller.get_by_meeting_id(meeting.id)
|
||||||
|
if transcript is None:
|
||||||
|
transcript = await transcripts_controller.add(
|
||||||
|
"",
|
||||||
|
source_language="en",
|
||||||
|
target_language="en",
|
||||||
|
user_id=meeting.user_id,
|
||||||
|
meeting_id=meeting.id,
|
||||||
|
share_mode="public",
|
||||||
|
)
|
||||||
|
|
||||||
_, extension = os.path.splitext(object_key)
|
_, extension = os.path.splitext(object_key)
|
||||||
upload_filename = transcript.data_path / f"upload{extension}"
|
upload_filename = transcript.data_path / f"upload{extension}"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ type LayoutProps = {
|
|||||||
|
|
||||||
export default async function RootLayout({ children, params }: LayoutProps) {
|
export default async function RootLayout({ children, params }: LayoutProps) {
|
||||||
const config = await getConfig(params.domain);
|
const config = await getConfig(params.domain);
|
||||||
const { requireLogin, privacy, browse } = config.features;
|
const { requireLogin, privacy, browse, rooms } = config.features;
|
||||||
const hasAuthCookie = !!cookies().get(SESSION_COOKIE_NAME);
|
const hasAuthCookie = !!cookies().get(SESSION_COOKIE_NAME);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -154,6 +154,21 @@ export default async function RootLayout({ children, params }: LayoutProps) {
|
|||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
{rooms ? (
|
||||||
|
<>
|
||||||
|
·
|
||||||
|
<Link
|
||||||
|
href="/rooms"
|
||||||
|
as={NextLink}
|
||||||
|
className="hover:underline focus-within:underline underline-offset-2 decoration-[.5px] font-light px-2"
|
||||||
|
prefetch={false}
|
||||||
|
>
|
||||||
|
Rooms
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
·
|
·
|
||||||
<About buttonText="About" />
|
<About buttonText="About" />
|
||||||
{privacy ? (
|
{privacy ? (
|
||||||
|
|||||||
33
www/app/[domain]/rooms/[roomName]/page.tsx
Normal file
33
www/app/[domain]/rooms/[roomName]/page.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import "@whereby.com/browser-sdk/embed";
|
||||||
|
import { useCallback, useEffect, useRef } from "react";
|
||||||
|
import useRoomMeeting from "../../rooms/useRoomMeeting";
|
||||||
|
|
||||||
|
export type RoomDetails = {
|
||||||
|
params: {
|
||||||
|
roomName: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Room(details: RoomDetails) {
|
||||||
|
const wherebyRef = useRef<HTMLElement>(null);
|
||||||
|
const roomName = details.params.roomName;
|
||||||
|
const meeting = useRoomMeeting(roomName);
|
||||||
|
|
||||||
|
const roomUrl = meeting?.response?.host_room_url
|
||||||
|
? meeting?.response?.host_room_url
|
||||||
|
: meeting?.response?.room_url;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{roomUrl && (
|
||||||
|
<whereby-embed
|
||||||
|
ref={wherebyRef}
|
||||||
|
room={roomUrl}
|
||||||
|
style={{ width: "100%", height: "98%" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
171
www/app/[domain]/rooms/page.tsx
Normal file
171
www/app/[domain]/rooms/page.tsx
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
Flex,
|
||||||
|
FormControl,
|
||||||
|
FormHelperText,
|
||||||
|
FormLabel,
|
||||||
|
Grid,
|
||||||
|
Heading,
|
||||||
|
Input,
|
||||||
|
Link,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
ModalOverlay,
|
||||||
|
Spacer,
|
||||||
|
Spinner,
|
||||||
|
useDisclosure,
|
||||||
|
VStack,
|
||||||
|
Text,
|
||||||
|
Menu,
|
||||||
|
MenuButton,
|
||||||
|
MenuList,
|
||||||
|
MenuItem,
|
||||||
|
AlertDialog,
|
||||||
|
IconButton,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import NextLink from "next";
|
||||||
|
import React, { ReactNode, useState } from "react";
|
||||||
|
import { Container } from "@chakra-ui/react";
|
||||||
|
import { PlusSquareIcon } from "@chakra-ui/icons";
|
||||||
|
import useApi from "../../lib/useApi";
|
||||||
|
import useRoomList from "./useRoomList";
|
||||||
|
import { FaEllipsisVertical, FaTrash } from "react-icons/fa6";
|
||||||
|
import next from "next";
|
||||||
|
|
||||||
|
export default function RoomsList() {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
const [roomName, setRoomName] = useState("");
|
||||||
|
const api = useApi();
|
||||||
|
const [page, setPage] = useState<number>(1);
|
||||||
|
const { loading, response, refetch } = useRoomList(page);
|
||||||
|
|
||||||
|
const handleAddRoom = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api?.v1RoomsCreate({
|
||||||
|
requestBody: { name: roomName },
|
||||||
|
});
|
||||||
|
setRoomName("");
|
||||||
|
refetch();
|
||||||
|
} catch (err) {}
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteRoom = async (roomId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await api?.v1RoomsDelete({
|
||||||
|
roomId,
|
||||||
|
});
|
||||||
|
refetch();
|
||||||
|
} catch (err) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRoomNameChange = (e) => {
|
||||||
|
setRoomName(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading && !response)
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" align="center" justify="center" h="100%">
|
||||||
|
<Spinner size="xl" />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Container maxW={"container.lg"}>
|
||||||
|
<Flex
|
||||||
|
flexDir="row"
|
||||||
|
justify="flex-end"
|
||||||
|
align="center"
|
||||||
|
flexWrap={"wrap-reverse"}
|
||||||
|
mb={2}
|
||||||
|
>
|
||||||
|
<Heading>Rooms</Heading>
|
||||||
|
<Spacer />
|
||||||
|
<Button colorScheme="blue" onClick={onOpen}>
|
||||||
|
Add Room
|
||||||
|
</Button>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>Add Room</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Room name</FormLabel>
|
||||||
|
<Input
|
||||||
|
placeholder="room-name"
|
||||||
|
value={roomName}
|
||||||
|
onChange={handleRoomNameChange}
|
||||||
|
/>
|
||||||
|
<FormHelperText>Please enter room name</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button variant="ghost" mr={3} onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button colorScheme="blue" onClick={handleAddRoom}>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<VStack>
|
||||||
|
{response?.items && response.items.length > 0 ? (
|
||||||
|
response.items.map((room) => (
|
||||||
|
<Card w={"full"}>
|
||||||
|
<CardBody>
|
||||||
|
<Flex align={"center"}>
|
||||||
|
<Heading size="md">
|
||||||
|
<Link
|
||||||
|
// as={NextLink}
|
||||||
|
href={`/rooms/${room.name}`}
|
||||||
|
noOfLines={2}
|
||||||
|
>
|
||||||
|
{room.name}
|
||||||
|
</Link>
|
||||||
|
</Heading>
|
||||||
|
<Spacer />
|
||||||
|
<Menu closeOnSelect={true}>
|
||||||
|
<MenuButton
|
||||||
|
as={IconButton}
|
||||||
|
icon={<FaEllipsisVertical />}
|
||||||
|
aria-label="actions"
|
||||||
|
/>
|
||||||
|
<MenuList>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => handleDeleteRoom(room.id)}
|
||||||
|
icon={<FaTrash color={"red.500"} />}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</Flex>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Flex flexDir="column" align="center" justify="center" h="100%">
|
||||||
|
<Text>No rooms found</Text>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
47
www/app/[domain]/rooms/useRoomList.tsx
Normal file
47
www/app/[domain]/rooms/useRoomList.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useError } from "../../(errors)/errorContext";
|
||||||
|
import useApi from "../../lib/useApi";
|
||||||
|
import { Page_Room_ } from "../../api";
|
||||||
|
|
||||||
|
type RoomList = {
|
||||||
|
response: Page_Room_ | null;
|
||||||
|
loading: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
refetch: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
//always protected
|
||||||
|
const useRoomList = (page: number): RoomList => {
|
||||||
|
const [response, setResponse] = useState<Page_Room_ | null>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [error, setErrorState] = useState<Error | null>(null);
|
||||||
|
const { setError } = useError();
|
||||||
|
const api = useApi();
|
||||||
|
const [refetchCount, setRefetchCount] = useState(0);
|
||||||
|
|
||||||
|
const refetch = () => {
|
||||||
|
setLoading(true);
|
||||||
|
setRefetchCount(refetchCount + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!api) return;
|
||||||
|
setLoading(true);
|
||||||
|
api
|
||||||
|
.v1RoomsList({ page })
|
||||||
|
.then((response) => {
|
||||||
|
setResponse(response);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setResponse(null);
|
||||||
|
setLoading(false);
|
||||||
|
setError(err);
|
||||||
|
setErrorState(err);
|
||||||
|
});
|
||||||
|
}, [!api, page, refetchCount]);
|
||||||
|
|
||||||
|
return { response, loading, error, refetch };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRoomList;
|
||||||
70
www/app/[domain]/rooms/useRoomMeeting.tsx
Normal file
70
www/app/[domain]/rooms/useRoomMeeting.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useError } from "../../(errors)/errorContext";
|
||||||
|
import { GetMeeting } from "../../api";
|
||||||
|
import { shouldShowError } from "../../lib/errorUtils";
|
||||||
|
import useApi from "../../lib/useApi";
|
||||||
|
|
||||||
|
type ErrorMeeting = {
|
||||||
|
error: Error;
|
||||||
|
loading: false;
|
||||||
|
response: null;
|
||||||
|
reload: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LoadingMeeting = {
|
||||||
|
response: null;
|
||||||
|
loading: true;
|
||||||
|
error: false;
|
||||||
|
reload: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SuccessMeeting = {
|
||||||
|
response: GetMeeting;
|
||||||
|
loading: false;
|
||||||
|
error: null;
|
||||||
|
reload: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useRoomMeeting = (
|
||||||
|
roomName: string | null | undefined,
|
||||||
|
): ErrorMeeting | LoadingMeeting | SuccessMeeting => {
|
||||||
|
const [response, setResponse] = useState<GetMeeting | null>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [error, setErrorState] = useState<Error | null>(null);
|
||||||
|
const [reload, setReload] = useState(0);
|
||||||
|
const { setError } = useError();
|
||||||
|
const api = useApi();
|
||||||
|
const reloadHandler = () => setReload((prev) => prev + 1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!roomName || !api) return;
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
api
|
||||||
|
.v1RoomsCreateMeeting({ roomName })
|
||||||
|
.then((result) => {
|
||||||
|
setResponse(result);
|
||||||
|
setLoading(false);
|
||||||
|
console.debug("Meeting Loaded:", result);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const shouldShowHuman = shouldShowError(error);
|
||||||
|
if (shouldShowHuman) {
|
||||||
|
setError(error, "There was an error loading the meeting");
|
||||||
|
} else {
|
||||||
|
setError(error);
|
||||||
|
}
|
||||||
|
setErrorState(error);
|
||||||
|
});
|
||||||
|
}, [roomName, !api, reload]);
|
||||||
|
|
||||||
|
return { response, loading, error, reload: reloadHandler } as
|
||||||
|
| ErrorMeeting
|
||||||
|
| LoadingMeeting
|
||||||
|
| SuccessMeeting;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRoomMeeting;
|
||||||
@@ -20,18 +20,6 @@ export default function TranscriptMeeting(details: TranscriptDetails) {
|
|||||||
? meeting?.response?.host_room_url
|
? meeting?.response?.host_room_url
|
||||||
: meeting?.response?.room_url;
|
: meeting?.response?.room_url;
|
||||||
|
|
||||||
const handleLeave = useCallback((event) => {
|
|
||||||
console.log("LEFT", event);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
wherebyRef.current?.addEventListener("leave", handleLeave);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
wherebyRef.current?.removeEventListener("leave", handleLeave);
|
|
||||||
};
|
|
||||||
}, [handleLeave]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{roomUrl && (
|
{roomUrl && (
|
||||||
|
|||||||
@@ -53,6 +53,18 @@ export const $CreateParticipant = {
|
|||||||
title: "CreateParticipant",
|
title: "CreateParticipant",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const $CreateRoom = {
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: "string",
|
||||||
|
title: "Name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: "object",
|
||||||
|
required: ["name"],
|
||||||
|
title: "CreateRoom",
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const $CreateTranscript = {
|
export const $CreateTranscript = {
|
||||||
properties: {
|
properties: {
|
||||||
name: {
|
name: {
|
||||||
@@ -529,6 +541,62 @@ export const $Page_GetTranscript_ = {
|
|||||||
title: "Page[GetTranscript]",
|
title: "Page[GetTranscript]",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const $Page_Room_ = {
|
||||||
|
properties: {
|
||||||
|
items: {
|
||||||
|
items: {
|
||||||
|
$ref: "#/components/schemas/Room",
|
||||||
|
},
|
||||||
|
type: "array",
|
||||||
|
title: "Items",
|
||||||
|
},
|
||||||
|
total: {
|
||||||
|
type: "integer",
|
||||||
|
minimum: 0,
|
||||||
|
title: "Total",
|
||||||
|
},
|
||||||
|
page: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
minimum: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "null",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Page",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
minimum: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "null",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Size",
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: "integer",
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "null",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Pages",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: "object",
|
||||||
|
required: ["items", "total", "page", "size"],
|
||||||
|
title: "Page[Room]",
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const $Participant = {
|
export const $Participant = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
@@ -556,6 +624,31 @@ export const $Participant = {
|
|||||||
title: "Participant",
|
title: "Participant",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const $Room = {
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: "string",
|
||||||
|
title: "Id",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: "string",
|
||||||
|
title: "Name",
|
||||||
|
},
|
||||||
|
user_id: {
|
||||||
|
type: "string",
|
||||||
|
title: "User Id",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: "string",
|
||||||
|
format: "date-time",
|
||||||
|
title: "Created At",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: "object",
|
||||||
|
required: ["id", "name", "user_id", "created_at"],
|
||||||
|
title: "Room",
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const $RtcOffer = {
|
export const $RtcOffer = {
|
||||||
properties: {
|
properties: {
|
||||||
sdp: {
|
sdp: {
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ import type {
|
|||||||
MetricsResponse,
|
MetricsResponse,
|
||||||
V1MeetingGetData,
|
V1MeetingGetData,
|
||||||
V1MeetingGetResponse,
|
V1MeetingGetResponse,
|
||||||
|
V1MeetingCreateData,
|
||||||
|
V1MeetingCreateResponse,
|
||||||
|
V1RoomsListData,
|
||||||
|
V1RoomsListResponse,
|
||||||
|
V1RoomsCreateData,
|
||||||
|
V1RoomsCreateResponse,
|
||||||
|
V1RoomsDeleteData,
|
||||||
|
V1RoomsDeleteResponse,
|
||||||
|
V1RoomsCreateMeetingData,
|
||||||
|
V1RoomsCreateMeetingResponse,
|
||||||
V1TranscriptsListData,
|
V1TranscriptsListData,
|
||||||
V1TranscriptsListResponse,
|
V1TranscriptsListResponse,
|
||||||
V1TranscriptsCreateData,
|
V1TranscriptsCreateData,
|
||||||
@@ -93,6 +103,117 @@ export class DefaultService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meeting Create
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.roomId
|
||||||
|
* @returns GetMeeting Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public v1MeetingCreate(
|
||||||
|
data: V1MeetingCreateData,
|
||||||
|
): CancelablePromise<V1MeetingCreateResponse> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/v1/meetings/",
|
||||||
|
query: {
|
||||||
|
room_id: data.roomId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rooms List
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.page Page number
|
||||||
|
* @param data.size Page size
|
||||||
|
* @returns Page_Room_ Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public v1RoomsList(
|
||||||
|
data: V1RoomsListData = {},
|
||||||
|
): CancelablePromise<V1RoomsListResponse> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: "GET",
|
||||||
|
url: "/v1/rooms",
|
||||||
|
query: {
|
||||||
|
page: data.page,
|
||||||
|
size: data.size,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rooms Create
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.requestBody
|
||||||
|
* @returns Room Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public v1RoomsCreate(
|
||||||
|
data: V1RoomsCreateData,
|
||||||
|
): CancelablePromise<V1RoomsCreateResponse> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/v1/rooms",
|
||||||
|
body: data.requestBody,
|
||||||
|
mediaType: "application/json",
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rooms Delete
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.roomId
|
||||||
|
* @returns DeletionStatus Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public v1RoomsDelete(
|
||||||
|
data: V1RoomsDeleteData,
|
||||||
|
): CancelablePromise<V1RoomsDeleteResponse> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/v1/rooms/{room_id}",
|
||||||
|
path: {
|
||||||
|
room_id: data.roomId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rooms Create Meeting
|
||||||
|
* @param data The data for the request.
|
||||||
|
* @param data.roomName
|
||||||
|
* @returns GetMeeting Successful Response
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public v1RoomsCreateMeeting(
|
||||||
|
data: V1RoomsCreateMeetingData,
|
||||||
|
): CancelablePromise<V1RoomsCreateMeetingResponse> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: "POST",
|
||||||
|
url: "/v1/rooms/{room_name}/meeting",
|
||||||
|
path: {
|
||||||
|
room_name: data.roomName,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: "Validation Error",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transcripts List
|
* Transcripts List
|
||||||
* @param data The data for the request.
|
* @param data The data for the request.
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ export type CreateParticipant = {
|
|||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CreateRoom = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type CreateTranscript = {
|
export type CreateTranscript = {
|
||||||
name: string;
|
name: string;
|
||||||
source_language?: string;
|
source_language?: string;
|
||||||
@@ -103,12 +107,27 @@ export type Page_GetTranscript_ = {
|
|||||||
pages?: number | null;
|
pages?: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Page_Room_ = {
|
||||||
|
items: Array<Room>;
|
||||||
|
total: number;
|
||||||
|
page: number | null;
|
||||||
|
size: number | null;
|
||||||
|
pages?: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
export type Participant = {
|
export type Participant = {
|
||||||
id: string;
|
id: string;
|
||||||
speaker: number | null;
|
speaker: number | null;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Room = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
user_id: string;
|
||||||
|
created_at: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type RtcOffer = {
|
export type RtcOffer = {
|
||||||
sdp: string;
|
sdp: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -184,6 +203,43 @@ export type V1MeetingGetData = {
|
|||||||
|
|
||||||
export type V1MeetingGetResponse = GetMeeting;
|
export type V1MeetingGetResponse = GetMeeting;
|
||||||
|
|
||||||
|
export type V1MeetingCreateData = {
|
||||||
|
roomId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type V1MeetingCreateResponse = GetMeeting;
|
||||||
|
|
||||||
|
export type V1RoomsListData = {
|
||||||
|
/**
|
||||||
|
* Page number
|
||||||
|
*/
|
||||||
|
page?: number;
|
||||||
|
/**
|
||||||
|
* Page size
|
||||||
|
*/
|
||||||
|
size?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type V1RoomsListResponse = Page_Room_;
|
||||||
|
|
||||||
|
export type V1RoomsCreateData = {
|
||||||
|
requestBody: CreateRoom;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type V1RoomsCreateResponse = Room;
|
||||||
|
|
||||||
|
export type V1RoomsDeleteData = {
|
||||||
|
roomId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type V1RoomsDeleteResponse = DeletionStatus;
|
||||||
|
|
||||||
|
export type V1RoomsCreateMeetingData = {
|
||||||
|
roomName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type V1RoomsCreateMeetingResponse = GetMeeting;
|
||||||
|
|
||||||
export type V1TranscriptsListData = {
|
export type V1TranscriptsListData = {
|
||||||
/**
|
/**
|
||||||
* Page number
|
* Page number
|
||||||
@@ -374,6 +430,79 @@ export type $OpenApiTs = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
"/v1/meetings/": {
|
||||||
|
post: {
|
||||||
|
req: V1MeetingCreateData;
|
||||||
|
res: {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: GetMeeting;
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HTTPValidationError;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/v1/rooms": {
|
||||||
|
get: {
|
||||||
|
req: V1RoomsListData;
|
||||||
|
res: {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: Page_Room_;
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HTTPValidationError;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
post: {
|
||||||
|
req: V1RoomsCreateData;
|
||||||
|
res: {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: Room;
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HTTPValidationError;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/v1/rooms/{room_id}": {
|
||||||
|
delete: {
|
||||||
|
req: V1RoomsDeleteData;
|
||||||
|
res: {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: DeletionStatus;
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HTTPValidationError;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"/v1/rooms/{room_name}/meeting": {
|
||||||
|
post: {
|
||||||
|
req: V1RoomsCreateMeetingData;
|
||||||
|
res: {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: GetMeeting;
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HTTPValidationError;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
"/v1/transcripts": {
|
"/v1/transcripts": {
|
||||||
get: {
|
get: {
|
||||||
req: V1TranscriptsListData;
|
req: V1TranscriptsListData;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const localConfig = {
|
|||||||
privacy: true,
|
privacy: true,
|
||||||
browse: true,
|
browse: true,
|
||||||
sendToZulip: true,
|
sendToZulip: true,
|
||||||
|
rooms: true,
|
||||||
},
|
},
|
||||||
api_url: "http://127.0.0.1:1250",
|
api_url: "http://127.0.0.1:1250",
|
||||||
websocket_url: "ws://127.0.0.1:1250",
|
websocket_url: "ws://127.0.0.1:1250",
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ export async function middleware(request: NextRequest) {
|
|||||||
) {
|
) {
|
||||||
// Feature-flag protedted paths
|
// Feature-flag protedted paths
|
||||||
if (
|
if (
|
||||||
!config.features.browse &&
|
(!config.features.browse &&
|
||||||
request.nextUrl.pathname.startsWith("/browse")
|
request.nextUrl.pathname.startsWith("/browse")) ||
|
||||||
|
(!config.features.rooms && request.nextUrl.pathname.startsWith("/rooms"))
|
||||||
) {
|
) {
|
||||||
return NextResponse.redirect(request.nextUrl.origin);
|
return NextResponse.redirect(request.nextUrl.origin);
|
||||||
}
|
}
|
||||||
@@ -27,7 +28,8 @@ export async function middleware(request: NextRequest) {
|
|||||||
if (
|
if (
|
||||||
request.nextUrl.pathname == "/" ||
|
request.nextUrl.pathname == "/" ||
|
||||||
request.nextUrl.pathname.startsWith("/transcripts") ||
|
request.nextUrl.pathname.startsWith("/transcripts") ||
|
||||||
request.nextUrl.pathname.startsWith("/browse")
|
request.nextUrl.pathname.startsWith("/browse") ||
|
||||||
|
request.nextUrl.pathname.startsWith("/rooms")
|
||||||
) {
|
) {
|
||||||
if (!fiefResponse.headers.get("x-middleware-rewrite")) {
|
if (!fiefResponse.headers.get("x-middleware-rewrite")) {
|
||||||
fiefResponse.headers.set(
|
fiefResponse.headers.set(
|
||||||
|
|||||||
Reference in New Issue
Block a user