繼上周搭建了一個線上行事曆轉檔工具後,最近有點沉迷在行事曆管理這件事,也幸運的得到了滿意的結果,就是能與Google日曆雙向同步了,同時也順便搭建一個猶如事件列表的簡易行事曆可以嵌入學校網站,不然原本嵌入的Google日曆實在給人很大的閱讀障礙。

紀錄一下幾個重要環節與功能
一、登入方式:
學校可以拿來做為單一嵌入的系統有兩套,AD以及Google Wordspace,既然行事曆是Google的系統就直接走Google認證相信路會好走一點。溪洲行事曆的編輯權限一直都只給行政,我們的Google Workspace後台也有區分行政(admin)與教師(teacher)兩個群組,考量行事曆本來就是給人看的東西,瀏覽檢視就不要綁到後台了,因此登入後台管理要操作的身分就綁在管理員與學校行政同仁群組的身上,熟悉的畫面,身分正確只要同意就取得權限。

如果身分不對,或未使用本校Google Workspace登入就會被拒絕

要完成Google帳號授權有幾點事項要注意…
1.透過Google Cloud console建立專案
2.新增底下3個API與服務
API 名稱 | 功能說明 | 是否必要 |
---|---|---|
Google Calendar API | 讀取、建立、修改 Google 日曆與事件 | ✅ 必要 |
Google People API | 取得使用者基本資訊(如 email、姓名) | ✅ 建議 |
Google OAuth 2.0 API | 授權流程本身(由 Google Identity Platform 提供) | ✅ 必要 |
3.設定憑證(須同意)
4.設定回呼導向url網址(callback)
callback1帳號授權用:Oauth授權後直接回到後台
callback2日曆自動同步用: Webhook 回呼(事件變動通知)
5.集中保管各類id與secret
有幾個不同用途的id與secret要認識並保管好,包含Google OAuth 憑證需要的
CALENDAR_ID、Client ID、Client Secret

二、管理維護
登入之後可以做的主要有2件事,一件是手動新增單一事件,新增的事件幾乎是瞬間同步就出現在Google日曆上,另外一個功能是手動同步行事曆,主要是透過拉取(fetch)來同步兩邊的內容。本來還想在本地端繼續增加匯入的功能再讓資料同步回去,但目前沒有做,因為已經做了轉檔工具,直接進Google日曆匯入就完成工作,會覺得再增加匯入好像有點多此一舉…

原本以為做到這邊差不多了,但沒料到竟然越陷越深,因為魔鏡建議增加自動同步的功能。
三、自動同步
決定了要做雙向自動同步,就需要先釐清觀念,知道誰負責什麼工作?彼此之間的關聯為何?這件事大概有4種角色各司其職,watch、webhook、sync、token…
WAtch透過API建立一條效期為7天的專屬通道(channel id)來跟webhook溝通與發送通知,webhook負責接收通知與觸發同步,sync收到webhook的通知後負責更新資料,token負責oauth授權與refresh token藉此讓webhook維持有效狀態,token效期只有1小時,為了避免webhook發生逾期失效或漏接通知,sync需要定期排程來觸發,同時補做同步工作。
這邊的操作細節會接觸到幾個不同的id需要了解,watch會建立channel id做為與webhook溝通的專屬通道,Google API 會回傳resource_id確認webhook收到的通知來源正確,另外同樣會反覆使用到的就是client id、secret與calendar id這幾項,所以最好是將常用的id與secret集中在一個檔案,需要授權時統一藉由 require_once去讀取驗證資訊,集中管理也方便維護。
四、狀態監控
自動同步打通之後,問題來了…我怎麼知道現在watch建立的chennel id失效沒?我怎麼知道token是否有效或webhook是否能夠正常運作?如果要手動自己比對多個log看紀錄有無更新感覺好像少了點什麼!好幾個檔案要進server撈下來再逐一比對那不如就回到手動同步不要再往自動更新繼續做下去,所以透過排程讓channel id(效期7天)每12小時renew刷新一次、TOKEN(效期1hr)則是每30分鐘renew來確保自動同步可以正常運行。
# 每 5 分鐘同步 Google Calendar 活動
*/5 * * * * /usr/bin/php /var/www/html/xxx/sipsCalendar/fetch_events.php >> /var/log/cron_fetch_events.log 2>&1
# 每天 03:00 與 15:00 續訂 webhook channel
0 3,15 * * * /usr/bin/php /var/www/html/xxx/sipsCalendar/renew_watch.php >> /var/www/html/xxx/sipsCalendar/renew_cron.log 2>&1
# 每 30 分鐘刷新 access_token
*/30 * * * * /usr/bin/php /var/www/html/xxx/sipsCalendar/refresh_token.php >> /var/www/html/xxx/sipsCalendar/renew_watch.log 2>&1