19 KiB
راهنمای گرفتن توکن و ورود کلاینت به کلاسهای plugNmeet
این راهنما خلاصه میکند که برای سناریوی استاد/دانشجو چگونه از سرویس plugNmeet توکن بگیریم و کلاینت فرانتاند (client/) با آن وارد کلاس شود.
پیشنیازها
- آدرس سرویس:
window.PLUG_N_MEET_SERVER_URL = "https://meet.newhorizonco.uk"(درconfig.js). api_keyوsecretاز فایل پیکربندی بکاند (services/plugnmeet-server/config.yaml).- بدنهٔ درخواستها باید با پروتکل JSON متناظر با پیامهای پروتوباف (
plugnmeet-protocol) ارسال شود؛ سرور طبقHandleAuthHeaderCheckهدرهای امنیتی را بررسی میکند.
گام ۱: ایجاد یا فعال بودن اتاق
API Endpoint برای Django Backend:
POST /api/courses/<course-slug>/online/room/create/
بدنه درخواست از فرانت به Django:
{
"subject": "کلاس جبر فصل ۱" // اختیاری - عنوان روم
}
⚠️ نکات مهم:
- فرانت نباید
metadataارسال کند! - بکاند Django (در
apps/course/views/live_session.py) بهطور خودکار تنظیمات امنیتی را اعمال میکند - این تضمین میکند که تنظیمات امنیتی بهصورت متمرکز و یکسان اعمال شود
🎯 تنظیمات ضروری برای نمایش فیچرها:
- برای نمایش Whiteboard: باید
whiteboardFeatures.allowedWhiteboard: trueباشد - برای نمایش SharedNotePad: باید
sharedNotePadFeatures.allowedSharedNotePad: trueباشد و Etherpad service فعال باشد - برای نمایش BreakoutRoom: باید
breakoutRoomFeatures.isAllow: trueباشد (فقط در منوی admin)
بدنه درخواست از Django به PlugNMeet (خودکار):
بکاند Django این بدنه را خودش به PlugNMeet ارسال میکند:
⚠️ توجه به نامگذاری:
- در Python میتوانید از
snake_caseاستفاده کنید - اما حتماً قبل از ارسال به PlugNMeet API باید به
camelCaseتبدیل شود - مثال:
default_lock_settings→defaultLockSettings - مثال:
room_features→roomFeatures
{
"room_id": "algebra-1402",
"metadata": {
"room_title": "کلاس جبر فصل ۱",
"defaultLockSettings": {
"lockMicrophone": true, // 🔒 قفل - فقط میزبان میتواند باز کند
"lockWebcam": true, // 🔒 قفل - فقط میزبان میتواند باز کند
"lockScreenSharing": true, // 🔒 قفل - فقط میزبان میتواند باز کند
"lockWhiteboard": false, // ✅ همه میتوانند ویرایش کنند
"lockSharedNotepad": false, // ✅ همه میتوانند ویرایش کنند
"lockChat": false,
"lockChatSendMessage": false,
"lockChatFileShare": false,
"lockPrivateChat": false
},
"roomFeatures": {
"allowWebcams": true,
"muteOnStart": true, // 🔇 همه با میک خاموش وارد میشوند
"allowScreenSharing": true,
"allowRecording": true,
"allowRtmp": false,
"allowViewOtherWebcams": true,
"allowViewOtherParticipantsList": true,
"adminOnlyWebcams": false,
"allowPolls": true,
"roomDuration": 0,
"chatFeatures": {
"allowChat": true,
"allowFileUpload": true
},
"sharedNotePadFeatures": {
"allowedSharedNotePad": true
},
"whiteboardFeatures": {
"allowedWhiteboard": true
},
"breakoutRoomFeatures": {
"isAllow": true,
"allowedNumberRooms": 6
},
"waitingRoomFeatures": {
"isActive": false
},
"recordingFeatures": {
"isAllow": true,
"isAllowCloud": true,
"enableAutoCloudRecording": false
}
}
}
}
چرا بکاند این کار را میکند؟
- ✅ امنیت متمرکز: تنظیمات امنیتی در یک جا کنترل میشود
- ✅ جلوگیری از دستکاری: فرانت نمیتواند تنظیمات را تغییر دهد
- ✅ یکپارچگی: همه کلاسها با تنظیمات یکسان ساخته میشوند
- 🔒 طبق تابع
AssignLockSettingsToUserدرpkg/models/user_lock.goاین مقادیر برای کاربران غیر-admin اعمال میشود
گام ۲: گرفتن توکن ورود
API Endpoint برای Django Backend:
POST /api/courses/online/room/token/
درخواست از فرانت به Django:
Headers:
Authorization: Token <USER_TOKEN>
Content-Type: application/json
Body:
{
"course_slug": "algebra-10"
}
⚠️ نکات مهم:
- فرانت فقط
course_slugارسال میکند! - بکاند Django از
Authorizationheader کاربر را شناسایی میکند - بکاند خودش live session فعال دوره را پیدا میکند:
# 1. پیدا کردن دوره course = Course.objects.get(slug=course_slug) # 2. پیدا کردن live session فعال session = CourseLiveSession.objects.get( course=course, ended_at__isnull=True # session هایی که هنوز به پایان نرسیدهاند ) # 3. گرفتن room_id room_id = session.room_id - بکاند خودش همه اطلاعات کاربر را میسازد:
user_idازrequest.usernameازuser.get_full_name()یاuser.emailis_adminازuser.can_manage_course(course)profilePicازuser.avatarlock_settingsبرای غیر-admin
بدنه درخواست از Django به PlugNMeet (خودکار):
بکاند Django این payload را خودش میسازد و به PlugNMeet میفرستد:
برای استاد:
{
"room_id": "algebra-1402",
"user_info": {
"user_id": "10", // 🔐 از request.user
"name": "استاد نمونه", // 🔐 از user.get_full_name()
"is_admin": true, // 🔐 از user.can_manage_course()
"user_metadata": {
"is_hidden": false,
"profilePic": "https://..." // 🔐 از user.avatar
}
}
}
برای دانشجو:
{
"room_id": "algebra-1402",
"user_info": {
"user_id": "27", // 🔐 از request.user
"name": "دانشجو نمونه", // 🔐 از user.get_full_name()
"is_admin": false, // 🔐 از user.can_manage_course()
"user_metadata": {
"profilePic": "https://...", // 🔐 از user.avatar
"lock_settings": { // 🔒 خودکار برای غیر-admin
"lock_microphone": true,
"lock_screen_sharing": true,
"lock_webcam": true,
"lock_whiteboard": false, // ✅ میتواند روی whiteboard بنویسد
"lock_shared_notepad": false, // ✅ میتواند در notepad بنویسد
"lock_chat": false,
"lock_chat_send_message": false,
"lock_chat_file_share": false,
"lock_private_chat": false
}
}
}
}
نحوه کار بکاند Django:
# 1. شناسایی کاربر از token
user = request.user # از Authorization header
# 2. پیدا کردن دوره و session فعال
course = Course.objects.get(slug=course_slug)
session = CourseLiveSession.objects.get(course=course, ended_at__isnull=True)
room_id = session.room_id
# 3. تشخیص نقش
is_admin = user.can_manage_course(course) # استاد یا مالک دوره
# 4. ساخت user_info
user_info = {
'user_id': str(user.id),
'name': user.get_full_name() or user.email,
'is_admin': is_admin,
}
# 4. اضافه کردن profilePic
profile_pic = request.build_absolute_uri(user.avatar.url)
user_metadata['profilePic'] = profile_pic
# 5. اضافه کردن lock_settings برای غیر-admin
if not is_admin:
user_metadata['lock_settings'] = {
'lock_microphone': True,
'lock_screen_sharing': True,
'lock_webcam': True,
'lock_whiteboard': False, # دانشجو میتواند روی whiteboard بنویسد
'lock_shared_notepad': False, # دانشجو میتواند در notepad بنویسد
'lock_chat': False,
'lock_chat_send_message': False,
'lock_chat_file_share': False,
'lock_private_chat': False,
}
ارسال به PlugNMeet:
بکاند Django با هدرهای امنیتی به PlugNMeet ارسال میکند:
API-KEY: از settingsHASH-SIGNATURE:HMAC_SHA256(body, secret)- این توکن JWT اختصاصی plugNmeet است که در
GeneratePNMJoinTokenساخته میشود is_admin: trueباعث میشود درGetPNMJoinTokenکاربر به عنوان presenter با تمام دسترسیها ثبت شودlock_settingsباعث میشود در فرانتاند PlugNMeet دکمههای میکروفون/وبکم غیرفعال شوند
پاسخ Django به فرانت:
{
"room_id": "algebra-1402",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"plugnmeet": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires": 300,
...
}
}
فرانت با این token میتواند کاربر را به PlugNMeet وارد کند:
https://meet.newhorizonco.uk/?access_token=<TOKEN>
گام ۳: ورود کلاینت با توکن
۱. توکن را در URL یا کوکی قرار دهید؛ کلاینت مقدار را از access_token در کوئریاسترینگ یا از کوکی pnm_access_token میخواند (getAccessToken در client/src/helpers/utils.ts).
۲. آدرس ورود: https://meet.newhorizonco.uk/?access_token=<TOKEN>.
۳. اپلیکیشن React موجود در client/src/components/app/index.tsx پس از بارگذاری:
- درخواست
POST /api/verifyTokenرا با هدرAuthorization: <TOKEN>میفرستد (HandleVerifyToken). - اگر توکن معتبر باشد، لیست آدرسهای NATS و موضوعات لازم را میگیرد و اتصال را آغاز میکند (
startNatsConn). ۴. پس از اتصال، وضعیت کاربر و اتاق در Redux ذخیره میشود (sessionSlice). اگر کاربر ادمین باشد، تمام امکانات بدون محدودیت فعال است؛ در غیر این صورت مقدارهایlock_settingsتعیین میکنند چه دکمههایی فعال باشند.
کنترل حالت صحبت/شنیدن برای استاد و دانشجو
استاد (Moderator/Host):
- ✅ در توکن
is_admin: trueارسال میشود - ✅ بکاند Django در
apps/course/views/live_session.pyاین را تشخیص میدهد:is_admin = user.can_manage_course(course) # استاد یا مالک دوره - ✅ سرور PlugNMeet در
GetPNMJoinTokenرول presenter را فعال میکند - ✅ هیچ قفلی روی میکروفون، وبکم یا اشتراک صفحه اعمال نمیشود
- 🎤 استاد میتواند بلافاصله صحبت کند و به دانشجو اجازه صحبت دهد
دانشجو (Participant):
- 🔒 در توکن
is_admin: falseارسال میشود - 🔒 بکاند Django خودکار lock_settings را اضافه میکند:
if not is_admin: user_metadata['lock_settings'] = { 'lock_microphone': True, 'lock_screen_sharing': True, 'lock_webcam': True, 'lock_whiteboard': False, # میتواند روی whiteboard بنویسد 'lock_shared_notepad': False, # میتواند در notepad بنویسد 'lock_chat': False, 'lock_chat_send_message': False, 'lock_chat_file_share': False, 'lock_private_chat': False, } - 🔇 دکمههای میکروفون، وبکم و اشتراک صفحه غیرفعال هستند
- 👂 فقط میتواند گوش دهد تا میزبان اجازه دهد
- ✅ اما میتواند در Whiteboard و SharedNotePad بنویسد و چت کند
- این منطق در
joinModal.tsxبا متغیرisMicLockپیادهسازی شده است
نحوه دادن اجازه به دانشجو:
- میزبان باید از داخل کلاس از طریق UI کنترل کند
- یا از API
/api/updateLockSettingsیاswitchPresenterاستفاده کند
نکات تکمیلی
توکنها و انقضا:
- توکنها زمان انقضای مفهومی دارند (
client.token_validityدر YAML) - در صورت نزدیک شدن به انقضا، کلاینت خودکار با
REQ_RENEW_PNM_TOKENدرخواست تمدید میدهد
Authorization:
- برای درخواستهای بعدی به
/api/...همان هدرAuthorizationرا ست کنید - کلاینت این کار را در
helpers/api/plugNmeetAPI.tsانجام میدهد
مدیریت دسترسیها:
- اگر میخواهید دانشجو را به صحبتکننده ارتقا دهید:
/api/updateLockSettingsیاswitchPresenter - این کار فقط توسط میزبان امکانپذیر است
🔐 جمعبندی امنیت
❌ چیزهایی که فرانت نباید انجام دهد:
موقع ساخت روم:
- ❌ ارسال
metadata - ❌ ارسال
default_lock_settings - ❌ ارسال
room_features
موقع گرفتن توکن:
- ❌ ارسال
room_id(بکاند خودش از session فعال میگیرد) - ❌ ارسال
user_info - ❌ ارسال
is_admin - ❌ ارسال
lock_settings - ❌ ارسال
user_idیاname
✅ چیزهایی که فرانت فقط ارسال میکند:
موقع ساخت روم:
{
"room_id": "algebra-1402", // اختیاری
"subject": "کلاس جبر" // اختیاری
}
موقع گرفتن توکن:
{
"course_slug": "algebra-10" // فقط این!
}
Authorization: Token <USER_TOKEN>در header
✅ چیزهایی که بکاند Django خودش انجام میدهد:
برای همه درخواستها:
- ✅ شناسایی کاربر از
Authorizationheader - ✅ بررسی دسترسی با
user.can_manage_course()یاParticipant.objects.filter()
موقع ساخت روم:
- ✅ تعیین
defaultLockSettings(همهtrueبه جز whiteboard/notepad) - ✅ تعیین
roomFeaturesکامل شامل:- ✅
sharedNotePadFeatures.allowedSharedNotePad: true - ✅
whiteboardFeatures.allowedWhiteboard: true - ✅
breakoutRoomFeatures.isAllow: true - ✅
chatFeatures,recordingFeatures, و سایر فیچرها
- ✅
- ✅ تبدیل نامهای
snake_caseبهcamelCaseقبل از ارسال به PlugNMeet - ✅ ساخت
metadataکامل برای PlugNMeet
موقع گرفتن توکن:
- ✅ پیدا کردن live session فعال از
course_slug - ✅ گرفتن
room_idاز session - ✅ ساخت
user_idازrequest.user.id - ✅ ساخت
nameازuser.get_full_name()یاuser.email - ✅ تشخیص
is_adminازuser.can_manage_course(course) - ✅ گرفتن
profilePicازuser.avatar - ✅ اضافه کردن
lock_settingsکامل برای غیر-admin شامل:- ✅
lock_microphone,lock_webcam,lock_screen_sharing(همهTrue) - ✅
lock_whiteboard,lock_shared_notepad(همهFalse- میتوانند بنویسند) - ✅
lock_chat,lock_chat_send_message,lock_private_chat(همهFalse)
- ✅
- ✅ تبدیل نامهای
snake_caseبهcamelCaseقبل از ارسال - ✅ ساخت
user_infoکامل برای PlugNMeet
نتیجه:
- 🔒 امنیت کامل: فرانت نمیتواند هیچ تنظیمات امنیتی را دستکاری کند
- ✅ متمرکز: همه logic در بکاند Django است
- 🎯 ساده: فرانت فقط
course_slugوAuthorizationheader ارسال میکند - 🔐 قابل کنترل: بکاند تعیین میکند کدام session فعال است
🐛 عیبیابی
مشکل: Whiteboard/SharedNotePad نمایش داده نمیشود
علائم:
- آیکون Whiteboard در footer نمایش داده نمیشود
- گزینه Enable/Disable SharedNotePad در منوی admin نیست
- گزینه Manage Breakout Room در منوی admin نیست
راه حلها:
-
بررسی
roomFeaturesدر room creation:"roomFeatures": { "sharedNotePadFeatures": { "allowedSharedNotePad": true // ✅ باید true باشد }, "whiteboardFeatures": { "allowedWhiteboard": true // ✅ باید true باشد }, "breakoutRoomFeatures": { "isAllow": true // ✅ باید true باشد } } -
بررسی نامگذاری فیلدها:
- ❌
shared_note_pad_features(snake_case) - اشتباه - ✅
sharedNotePadFeatures(camelCase) - صحیح
- ❌
-
بررسی
config.yamlدر plugnmeet-server:shared_notepad: enabled: true # ✅ باید true باشد etherpad_hosts: - id: "etherpad_node_01" host: "http://plugnmeet-etherpad:9001" client_id: "plugNmeet" client_secret: "..." -
بررسی Etherpad service:
docker ps | grep etherpad # باید یک container با نام plugnmeet-etherpad اجرا باشد -
بررسی
defaultLockSettings:- اگر
lockWhiteboard: trueباشد، فقط admin میتواند ویرایش کند - اگر
lockSharedNotepad: trueباشد، فقط admin میتواند ویرایش کند
- اگر
-
بررسی user
lock_settingsدر توکن:"lock_settings": { "lock_whiteboard": false, // false = میتواند ویرایش کند "lock_shared_notepad": false // false = میتواند ویرایش کند }
مشکل: دانشجو نمیتواند در Whiteboard بنویسد
علت:
lock_whiteboard: trueدر توکن کاربر
راه حل:
- در هنگام ساخت توکن برای دانشجو،
lock_whiteboardراfalseکنید - یا از منوی admin، lock را برای آن کاربر خاص باز کنید
مشکل: SharedNotePad آیکون دارد اما باز نمیشود
علت:
- Etherpad service اجرا نیست یا در دسترس نیست
راه حل:
# بررسی وضعیت Etherpad
docker-compose -f docker-compose.plugnmeet.yml ps etherpad
# اگر اجرا نیست، راهاندازی کنید
docker-compose -f docker-compose.plugnmeet.yml up -d plugnmeet-etherpad
# بررسی logs
docker-compose -f docker-compose.plugnmeet.yml logs -f plugnmeet-etherpad