--- tags: shortit --- # ShortIt! 設計文件 [TOC] --- ## 架構圖 https://drive.google.com/file/d/1IkCRMK1m2Rqbt5WZ9zItyQS0BEDSwRYb/view?usp=share_link <iframe frameborder="0" style="width:100%;height:400px;" src="https://viewer.diagrams.net/?tags=%7B%7D&highlight=0000ff&edit=_blank&layers=1&nav=1&title=ShortIt.drawio#Uhttps%3A%2F%2Fdrive.google.com%2Fuc%3Fid%3D1IkCRMK1m2Rqbt5WZ9zItyQS0BEDSwRYb%26export%3Ddownload"></iframe> ## 類別圖(前端) ![](https://i.imgur.com/pi5lu9S.png) ## 類別圖(後端) ![](https://i.imgur.com/HltUfxx.png) ## 資料模型 ### ShortUrl ```typescript= export type TShortUrl = { id: string; // is also path url: string; // target userId?: string; // email createdAt?: Date; views?: number; isOgCustom: boolean; ogTitle?: string; ogDescription?: string; ogImage?: string; }; ``` ### OpenGraphUrl ```typescript= export type TReqOpenGraphUrl = TOpenGraphUrl & { id?: string }; export type TResOpenGraphUrl = TOpenGraphUrl & { id: string }; export type TOpenGraphUrl = { url: string; ogTitle?: string; ogDescription?: string; ogImage?: string; }; ``` ### TAuth0User ```typescript= export type TAuth0User = { nickname: string; name: string; picture: string; update_at: string; email: string; email_verified: boolean; sub: string; sid: string; }; ``` ## API ### U-01 新增/編輯短網址 | Field | Define | | ---------------- | -------------------- | | API Version | v1 | | HTTP Method | POST | | Full Path | /api/v1/url | | Request Headers | | | Path Parameters | | | Query Parameters | | | Request Body | TReqOpenGraphUrl | | Response Body | TResOpenGraphUrl | | Status | 200, 400, 401, 403 | | Other | With credential(opt) | - Headers - 編輯短網址時Authorization為必須 - Status - 200 - Success - 400 - Incorrect body parameter - 401 - No user credential - 403 - User identity does not match the target record - Mode - Create - `id` not present in body - Edit - `id` present in body ### U-02 查詢短網址 | Field | Define | | ------------------ | --------------- | | API Version | v1 | | HTTP Method | GET | | Full Path | /api/v1/url/:id | | Request Headers | | | Path Parameters | id: url id | | Query Parameters | | | Request Body | | | Response Body | OpenGraphUrl | | Status | 200, 404 | ### U-03 查詢所有短網址 | Field | Define | | ---------------- | ---------------------- | | API Version | v1 | | HTTP Method | GET | | Full Path | /api/v1/url | | Request Headers | | | Path Parameters | | | Query Parameters | s?: skip n, t?: take n | | Request Body | | | Response Body | TShortUrl[] | | Status | 200, 401 | | Other | With credential | - Status - 200 - Ok - 401 - Invalid JWT ### U-04 刪除短網址 | Field | Define | | ---------------- | --------------- | | API Version | v1 | | HTTP Method | DELETE | | Full Path | /api/v1/url/:id | | Request Headers | | | Path Parameters | id: url id | | Query Parameters | | | Request Body | | | Response Body | | | Status | 204, 401, 403 | | Other | With credential | - Status - 204 - Success - 401 - No user credential - 403 - User identity does not match the target record ### U-05 重新導向連結 | Field | Define | | ---------------- | ---------- | | API Version | v1 | | HTTP Method | GET | | Full Path | /:id | | Request Headers | | | Path Parameters | id: url id | | Query Parameters | | | Request Body | | | Response Body | HTML | | Status | 200, 404 | - HTML Redirect https://stackoverflow.com/a/5411601/13234407 ### U-06 取得使用者資訊 | Field | Define | | ---------------- | --------------- | | API Version | v1 | | HTTP Method | GET | | Full Path | /api/v1/user | | Request Headers | | | Path Parameters | | | Query Parameters | | | Request Body | | | Response Body | TAuth0User | | Status | 200, 401 | | Other | With credential | - Status - 200 - Success - 401 - No user credential ### U-07 取得URL的OpenGraph資訊 | Field | Define | | ---------------- | --------------- | | API Version | v1 | | HTTP Method | GET | | Full Path | /api/v1/og | | Request Headers | | | Path Parameters | | | Query Parameters | url: target url | | Request Body | | | Response Body | TOpenGraphUrl | | Status | 200, 400 | | Other | | - Status - 200 - Success - 400 - Target url unreachable ### U-08 取得使用者URL的總數 | Field | Define | | ---------------- | ----------------- | | API Version | v1 | | HTTP Method | GET | | Full Path | /api/v1/url/count | | Request Headers | | | Path Parameters | | | Query Parameters | | | Request Body | | | Response Body | {count: number} | | Status | 200, 401 | | Other | With credential | - Status - 200 - Success - 401 - No user credential ## 流程設計 ### 新增/編輯短網址 (U-01) ```flow st=>start: 收到請求 opInsert=>operation: 新增短網址 opEdit=>operation: 編輯短網址 conBodyExist=>condition: Body存在 conIdExist=>condition: Body.id存在 conHasLogin=>condition: User有登入 conDbHasId=>condition: 資料庫存在Body.id的記錄 conIdMatches=>condition: userId == user.email conTargetExist=>condition: URL存在且使用者相符 end400_1=>end: 400 end400_2=>end: 400 end401_1=>end: 401 end403_1=>end: 403 end200_1=>end: 200 st->conBodyExist conBodyExist(no)->end400_1 conBodyExist(yes)->conIdExist conIdExist(no)->conTargetExist conTargetExist(no)->opInsert->end200_1 conTargetExist(yes)->end200_1 conIdExist(yes)->conHasLogin conHasLogin(no)->end401_1 conHasLogin(yes)->conDbHasId conDbHasId(no)->end400_2 conDbHasId(yes)->conIdMatches conIdMatches(no)->end403_1 conIdMatches(yes)->opEdit->end200_1 ``` ## 實作技術 ### 前端 - TypeScript - React - Mantine - Auth0 - react-joyride ### 後端 - TypeScript - Node.js - Prisma - Redis(Cache) - PostgreSQL ## 設計議題 ### 使用者登入 #### 選項 - Keycloak - Auth0 - 自己來 #### 分析 - 自己來太浪費時間,Email驗證過於麻煩。 - Keycloak有點複雜且必須自架。 - Auth0有免費方案,SDK還算方便。 - 採用Auth0。 ### 短網址重新導向 #### 選項 - 前端透過API取得資訊後重新導向。 - Server直接回傳一頁HTML。 #### 分析 - 前端是React的SPA,一定要載入JS才能運作,這樣OpenGraph的Metadata一定不會work,所以採用後端直接回傳一頁簡單的HTML。 ### 登入使用者是否可以編輯目標網址 #### 選項 - 可以:預期功能有一個會查詢已存在網址的功能,如果網址已存在則直接使用已存在的短網址。如果短網址的目標網址被更改了可能會影響到其他使用者。 - 不可以。 #### 分析 - 已存在短網址功能只套用同一個使用者或所有匿名使用者。 - 匿名使用者無法編輯短網址故無此問題。 - 登入的使用者建立的短網址都會掛在該用戶的帳號下,用戶去如果編輯目標網址則可以理解為該用戶希望所有該短網址的目標都該變更。 ### 重新導向運作方式 #### 選項 - 全部都用HTML重新導向(meta tag、js並行),並自動生成OpenGraph的Metadata。 - 使用者自定義的才生成HTML,其他直接302。 #### 分析 - 考量到各家平臺實作網址預覽的方法不一,我們的OpenGraph截取無法做到100%,例如推特的網址就會抓不到東西。預設狀況應該直接重新導向給原始網址,根據之前的經驗,多數社群平臺的網址預覽都會解析302重新導向後的原始網址內容。