# 網程期末專題 關於一個線上的及時 問答網站,用徒是當老師要發問的時候,學生可以用QR code 或是密碼的方式登陸該教室的問答系統裡,且登入時會要求輸入學號及姓名,當老師要發問時學生可以搶答,並且會把最先回答的最先顯示在頂端,並且顯示回答內容,老師可以自己設定要不要顯示學號或姓名或匿名顯示,且老師可以點開每一個回答檢視,並且可以按加分或是不加分,並且課堂結束後會在老師帳號生成一個Exel顯示哪些學生有加分,加給分,回答哪些題之類的。關於老師發問的內容可以是現場打或是預先打號到後台裡在顯示 ## 10/11 討論 資源: https://hackmd.io/@ItisCaleb/Syt8lG6Cv https://wadehuanglearning.blogspot.com/2019/05/commit-commit-commit-why-what-commit.html github node.js express github JQuery websocket SQL MongoDB 先條例功能 分配工作 開github repo 要盡量寫詳細一點 改一個功能就傳一次 12/26 期末報告 ## 條列功能 ### 老師端 #### 帳號 可以用email 或google 或自己建立 內容會存在雲端 #### 學生頁面 可以看到每個學生的資料,資料有總共加多少分,參與了哪些搶答,每次的搶答內容等 可以有一些篩選和排序功,例如照加分多寡排 #### 即時發起問題 不用建立問題詳細內容,馬上建立答題框讓學生搶答,可以事後補上問題詳細內容,內容可以是照片或文字 #### 事先建立問題 事先建立好問題內容,當按下開始搶答後學生才可以搶答 #### 搶答框內容頁面 * 回答者顯示狀態為匿名,但在課堂結算那邊可以看到真實資料 * 如果有題目敘述顯示在最上面 * 可以看到有多少人回答 * 先回答的人會在越上面,若同時(假設有這種狀況)加分少的人在上面 * 可以點開這個人的回答框,會有加分和不加分兩個選項,當選擇其中一個後會變成取消 * 選完後可以按返回或其他地方回到所有人的問答框,且檢視過後且被執行加分或不加分後外觀會變成半透明或暗掉之類的顯示已經閱讀過此回答 * 結束搶答 #### 問題總覽 * 會有問題編號、班級、日期....之類的屬性 * 可以看到有提過那些問題,點進去後就是所有人的搶答內容 * 可以刪除問題 * 編輯問題內容 #### 課堂結算 選擇問題,產生出表格頁面顯示加分詳情,還可以下載excel檔 下載excel檔,裡面是學生的加分詳情,學號姓名、加多少分 ### 學生端 #### 帳號 可以用email 或google 或自己建立 內容會存在雲端 #### 加入教室 加入老師的教室,可以透過QRcode或一串密碼之類的加入 只要有新的問題發起時可以點進去立即搶答 #### 搶答內容 可以一直回答,但會以最新的回答時間進行總體排序 回答內容可以是文字或是照片 #### 可以看到搶答框內容 * 類似老師的搶答框內容頁面,但只能看到回答內容而已 * 每個人搶答出去的內容顯示都是匿名,自己回答的內容框外觀會特別標示 #### 加分狀態總覽 * 可以看到參與哪些老師的課程 * 每個老師的總共加分為多少,以及每次加分的細項 orz ## 11/20 討論 ### 前端 figma react ### 後端 登入系統 API document SQL SQL lite javascript ### 功能 加入課程 即時發起提問 事先規劃提問 學生端個人資料(有哪些課程,加多少分..) 老師端課程管理系統 搶答頁面(查看回答,要不要加分) 學生端回答問題 顯示在 搶答頁面(websocket) 加分紀錄統整&匯出功能 帳號登入 後端資料庫 Server ### dead line 11/27 前端頁面樣式設計(全部, figma) **by Y** 11/27 登入系統、API document **by deer** 12/4 database,帳密登入,老師端課程管理系統學生端個人資料 結合前端 12/11 即時發起提問,搶答頁面(查看回答,要不要加分),加分紀錄統整&匯出功能 12/14 佈署到server 12/16 dead line https://medium.com/麥克的半路出家筆記/筆記-認識-oauth-2-0-一次了解各角色-各類型流程的差異-c42da83a6015 ## 11/27 討論 docker postman OAuth SQLite early return 12/1(五) Login Sign up main page Html+css+rwd **by Y** 12/1(五) 把login sign up後端做完,要用上面的東西 **by deer** 禮拜五下課串接完login + signup 前後端 12/4 websocket串接到main page 完成簡易版功能前後端串接 ### mid login post 帳號 ``` /auth/login ``` google帳號 ``` /auth/google ``` :3000/updateinfo POST :4000/profile {username,realname,studentId} # 前端Env ``` # BROWSER=none REACT_APP_API_URL=http://localhost:4000 ``` # 重要 backend .env 更新(12/22 ) ``` frontUrl = http://localhost:3000 backUrl = http://localhost:4000 mongoDB = mongodb+srv://deerufin:ismemario@cluster0.wpbsliy.mongodb.net/?retryWrites=true&w=majority GoogleclientID = 869287145272-5j91drelnj6u3d2ds7h0l78slvv5nhpj.apps.googleusercontent.com GoogleclientSecret = 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 secretToken = 5k42l42u3g4gk6ak7g03vu0455555 PORT = 4000 cookieKey = sdadnalkdnwlkndlsdjsbdkjasbdsks ``` # 前端API ## everywhere ### GetUserInfo #### req ``` GET "http://localhost:4000/api/getUserInfo" ``` #### res ``` res.json({ _id : req.session.passport.user._id, username: req.session.passport.user.username, thumbnail: req.session.passport.user.thumbnail, googleid: req.session.passport.user.googleid, realname: req.session.passport.user.realname, studentID: req.session.passport.user.studentID }) ``` ## Login ### googleLoginButton 跳轉google登入 #### req ``` window.location.href = 'http://localhost:4000/auth/google' //跳轉 ``` ## Updateinfo ### 取得isLogin & isCompleteCreate #### req ``` GET /auth/auth-state ``` #### res ``` res.json = { loginState : A , completeCreateState : B } A: "LoginSuccess" => login成功 "LoginFailed" => login失敗 B: "FinishCompleteCreate" => 首次註冊完成 "UnFinishCompleteCreate" => 未完成首次註冊 ``` ### O 輸入註冊資料 updategoogleUserinfoPage handleConfirm() #### req ``` POST /api/updateinfo req.json={ username: username, realname: realName, studentID: studentID } ``` #### res ``` res.json{ usernameState: A realnameError: B studentIDError: C isTermsAcceptedError: D } A,B,C,D: "Success" =>成功更新 "{Error message}" =>錯誤訊息 ``` ## Home ### Logout #### res ``` GET /api/logout res.json={ logoutState: B } B: logoutState : 'LogoutSuccess' => logout成功 logoutState : 'LogoutFailed' => logout失敗 要把loginState改成"LoginFailed"後回傳LogoutFailed" ``` ### 取得Joined Class #### req ``` GET http://localhost:4000/course/getJoinedClass json { } ``` #### res ``` array of classInfo [ { "classID": "26959", "authorID": "65836b3453a2bc9ab4862eec", "state": "true", "title": "fsdfs", "description": "fsdfsdfs", "createDate": "2023-12-22T14:51:37.594Z" } ] ``` ### Join Class by ID #### req ``` POST http://localhost:4000/course/userAddJoinedClass json { classID : classID; } ``` #### res 三種情況 ``` return res.send('CourseNotFound'); return res.send('SuccessfullyJoinCourse'); return res.send('FailedJoinCourse'); ``` ### 退出joined課程 #### req ``` POST http://localhost:4000/course/userPullJoinedClass json { classID : classID; } ``` #### res ``` 有錯誤會送 err ``` ### 參與者進入問答頁面 ### 取得Created Class ### Create Prebuild Class ### Create Quick Class #### req ``` POST http://localhost:4000/course/create json { userID : userID, description: description, roomTitle : roomTitle, state : state.gilad, username : username } ``` #### res ``` res.send(_classID); ``` ### Delete a CreatedClass 請注意他同時也會發 DeleteClassIDToUser req ``` POST http://localhost:4000/course/deleteClass json { classID : classID } ``` #### res ``` yes or error ``` ### set CreatedClass State(start/end) req ``` POST http://localhost:4000/course/changeState json { classID state; } ``` #### res ``` ``` ### 創建者進入問答頁面 ```json const RoomData = { id: 1,//backend給 infoData: { "title": "Card1",//frontend給 "classID": "001",//backend給 "state": "close",//frontend給 "ownerID": "1234556",//frontend給 "creat date": "2021/10/10",//frontend給 "description": "this is a description"//frontend給 } }; RoomDataBase{ RoomData:{ ... } RoomMessage{ ... } } ``` Schema ```json= { info : { classID : String, authorID : String, state : String, title : String, description : String, createDate : Date }, member : [ { userid : String } ], message : [ { user: String, message: String, timestamp: Date } ] } ``` ## Discussion 頁面 ### 送出Message #### req ``` POST http://localhost:4000/sendMessage json { "classID" : "26959", "isAnonymous" : "true", "message" : "postmanTEXT222", "score" : "null" } ``` #### res ``` res.json({ messageId: _uuid }); ``` ### 加扣分按紐 #### req ``` POST http://localhost:4000/ json { classID messageID score : string("incorrect" or "correct" or "null") } ``` ### Loading all Message #### req ``` POST http://localhost:4000/ json { classID } ``` #### res ``` response = Array[{messgeINFO},{...},...] ``` messageINFO 要有 username userAvtar messageID isAnonymous messageID content selected ### ## Profile ## npm install --legacy-peer-deps ### 測試用 ## create POST ``` http://localhost:4000/course/create ``` ``` { "classid" : "MyClassID", "authorID" : "MyUserID", "author" : "MyName", "state" : "open", "title" : "Mytitle" } ``` ## commend POST ``` /course/:id/addComment ``` ``` { "classid" : "MyClassID", "authorID" : "MyUserID", "author" : "MyName", "state" : "open", "title" : "Mytitle" } ``` ### To do 未登入狀態跳進room會被跳到Login但style跑掉 設置classState的時候案太快會錯,要設置cd時間 Solid字體要改 Error Input 要加 Login Page 要改 JoinedClassDialog要檔已加入課程和不存在課程 JoinedClassByidDialog : handleJoin, handleNewJoinedClass Context: useEffect加上 setJoinedClassData JoinedDiscussion的fetch ClassData Stream MessageCard: handleDeleteMessage,handleChaneAnonymous # 12/24 Deer完成了 ## api ### Join Class by ID ### 退出joined課程 ### 取得Joined Class ### 更新錯誤訊息 讓你看得懂 ![image](https://hackmd.io/_uploads/SyANl_SPa.png) ## bugfixed ### 修好不存在課程BUG ## 需要討論的 ### 用戶在joined課程的刪除留言 ### static 頁面 ``` import io from 'socket.io-client' let socket = io.connect('http://localhost:5000') useEffect(()=>{ socket.emit('join_room',classID); socket.on('refresh',(data)=>{ console.log(data); }) },[socket]) socket.emit('send_message',classID); ``` ``` const ws = new WebSocket('ws://localhost:8080'); ws.onmessage = function(event) { console.log(`Received: ${event.data}`); console.log('tttttt') fetchClassData(); }; ``` ``` const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); const clients = new Set(); wss.on('connection', ws => { console.log('A client connected to the chat room!'); clients.add(ws); // 新增客戶端到集合中 ws.send('You are connected!'); // 確認訊息 ws.on('message', message => { console.log(`Received: ${message}`); // 發送訊息給所有客戶端 clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(`Hello, you sent -> ${message}`); } }); }); }); ``` ``` const corsOptions = { origin: [ 'http://localhost:3000', 'http://localhost:8080' ], methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', credentials: true, allowedHeaders: ['Content-Type', 'Authorization', 'X-CSRF-TOKEN', 'Cookie', 'X-Requested-With', 'Accept'], } app.use(cors(corsOptions)); ``` ## Deploy 注意事項 ``` [Interface] PrivateKey = ePbpr5j9eJVAbHzYAVAR/KHj/cm0hRWGSfHgXxOqA2w= Address = 10.8.0.4/32 [Peer] PublicKey = NHXkMVi45gdFtQ1mrNzT8xczLSxmxfFEz32mLsPwLEY= AllowedIPs = 140.121.0.0/16, 10.8.0.0/24 Endpoint = 140.121.80.5:3306 PersistentKeepalive = 25 ``` ### connect ``` ssh deerufin@140.121.80.152 caleborz ``` ## git pull step ``` git pull npm run build sudo !! /backend sudo run npm start screen -r ctrl A D ``` ## demo 影片步驟 1. 登入 2. 創建帳號,要帶到限制(User name 不能超過15個字,如果沒有註冊好下次登入要重新註冊 3. 更改使用者的資訊 4. 加入課程&如何加入課程 5. 創建課程&如何創建課程,設置open close 狀態,刪除 6. 加入課程 刪除加入課程 7. 多人線上搶答展示:事先設置匿名老師評分 事後設置匿名 刪除 資訊欄 8.