# 原站回寫：保留原站正式資料的接法

本頁只供維護既有「原站確認正式答案」的特殊接駁。新流程使用[三種 UI 接入方式](integration-modes.md)；外站保留評審或其他業務資料時，用[流程資料同步](workflow-sync.md)取得副本，不需要改用 intake。兩邊都能填表或看資料，本身不要求回寫。

OpenDashboard 可以為這類連接應用提供選用的免登入入口。這個功能需要管理員配置通知目的地及原站接收程式；建立表單或匯入資料不會自動開放入口，也不會自動建立雙向同步。

## 參賽者會看到甚麼？

公開入口只建立新隊伍。開始後，參賽者保存一條私人連結，用來填寫隊伍報名及作品。作品一、作品二可放在同一張作品表，不必拆成兩張表。

連結持有人可查看及修改該隊資料，不必登入。私人連結必須妥善保存；不可放入公開報告、分析工具或日誌。匯入的舊隊伍不會被公開入口按電郵、姓名或原站編號認領。

## 資料如何到達原站？

1. 參賽者保存草稿或正式提交。OpenDashboard 檢查欄位、附件權限和版本，保存草稿及待處理指令。
2. OpenDashboard 以簽名通知喚醒原站工作程序。通知只有應用、模式和版本號，不含參賽者答案。
3. 原站讀取待處理指令，在交易內檢查報名時段、作品要求及隊伍所有權，保存到原站資料庫。
4. 原站回覆接受或拒絕。OpenDashboard 才顯示「原站已保存」或修正提示。
5. 原站原有的變更通知再同步正式資料到 OpenDashboard 的表單及儀表板。

選用原站回寫的網站需要自己的欄位對應及回寫程式；中央保存模式不需要這套回寫程序。請勿將連接金鑰放在瀏覽器；原站評分、管理員欄位及系統編號不應出現在參賽者輸入定義中。

## 與中央保存模式的界線

目前 `/apply/{slug}` intake 固定走待處理指令及原站確認，沒有 direct 模式開關。它不會自動轉換成 OpenDashboard 中央保存的公共新報名入口。中央保存的新流程使用 `/start/{slug}` 及私人 `/w/<token>` 頁，或流程填表 API；在流程的「網站接入」配置入口。

原站同步來的舊隊伍不會自動取得 intake 連結。若要跨兩個 UI 修改同一隊伍，需先完成安全身份對應及版本核對；現有新隊伍入口不提供以姓名或電郵認領。

## 建立入口

以下為合成範例。使用伺服器端連接應用金鑰，POST `/api/apps/v1/intakes`。應用必須啟用已識別資料模式；每個 form_id 必須屬於相同應用及測試／正式模式。入口建立時保持停用。

```json
{
  "id": "ink_demo_intake",
  "slug": "demo-team-entry",
  "definition": {
    "title": "示範比賽報名",
    "description": "開始後請保存本隊私人連結。",
    "forms": [{
      "key": "registration",
      "form_id": "evt_demo_registration",
      "title": "隊伍報名",
      "questions": [{"key":"team_name","type":"short_text","title":"隊伍名稱","personal":false}],
      "actions": [{"key":"save","label":"保存草稿","submit":false}]
    }]
  }
}
```

定義沿用一般欄位類型及重複群組，但與匯入表單的內部欄位分開。每個表單 key、action key 唯一；動作的 submit 標記只影響呈現，原站仍必須自行執行正式提交驗證。

GET `/api/apps/v1/intakes/{id}` 讀取配置。配置不可原地換綁表單或改題目；必要時建立新版本入口，保留舊隊伍的定義。管理員先私密配置 HTTPS 通知目的地及簽名密鑰，再 PATCH 同一路徑 `{"enabled":true}`。開放入口為 `/apply/{slug}`；停用使用 `{"enabled":false}`。

## 原站接收與確認

GET `/api/apps/v1/intake-commands` 每次最多讀取 25 筆本應用、本模式的待處理指令。每筆包含 id、entry_id、intake_id、revoked 及 payload。payload 包含 form、action、answers、expected_revision、source_ref、source_version。

原站只可用自己的私密對應表查找此前由該 entry_id 建立的隊伍，不能用參賽者輸入的電郵連回既有帳戶。首次 source_ref 為空；成功後固定綁定同一隊伍。revoked 為 true 時不得繼續修改來源資料。

將指令 id、payload 雜湊及結果保存在與資料更新相同的交易中。遇上逾時或重播，回覆同一結果，不能另建隊伍。原站與 OpenDashboard 的版本衝突都應保留草稿並要求核對，不可盲目覆寫。

POST `/api/apps/v1/intake-commands/{id}/ack`：

```json
{"status":"accepted","source_ref":"demo_team_001","source_version":"revision_1"}
```

或拒絕：

```json
{"status":"rejected","code":"invalid_answers","fields":{"team_name":"請填寫隊伍名稱。"}}
```

拒絕代碼包括 invalid_answers、window_closed、version_conflict、entry_closed。拒絕不可帶 source_ref 或 source_version。相同確認可以重播；不同結果回覆衝突。

## 通知、重試與附件

通知 JSON 為 type=intake.commands.pending、app_id、test、generation。HTTP 標頭 X-OpenDashboard-Intake-Timestamp 及 X-OpenDashboard-Intake-Signature 分別為 Unix 秒和 HMAC-SHA256 十六進位值。簽名輸入是時間字串、句點及原始 body bytes；接收端需固定時間比較、檢查時間差及 app/mode，耐久保存待處理標記後才回覆成功。

共用重試工作只讀有待辦的索引，每次最多三個目的地，並限制逾時和退避；不會每五分鐘逐個應用掃描整份資料。原站程序也需設定單一工作鎖、批次上限、記憶體及時間限制。另可每日查一次待辦指令補漏。

附件透過現有私密上傳 API 取得，external_id 必須以 `intake:{entry_id}:` 開頭。原站應再次檢查檔案所屬隊伍、完成狀態、大小、SHA-256、實際檔案類型及可信儲存來源；不能下載參賽者提供的任意 URL。本入口接受 JPG、PNG 和 PDF，單檔最多 20 MB；原站可設定更小的整批限制。

## 與表單管理、儀表板的關係

表單管理仍顯示原站同步來的正式回覆。啟用入口後，可從表單的選單開啟新報名／作品入口。匯入的正式回覆保持由原站管理，避免 OpenDashboard 與原站各自改寫同一筆資料。

儀表板使用同步完成後的正式資料；待原站確認的草稿不應算成報名完成。報告範本只決定如何展示資料，不能代替填表入口或授予隊伍修改權。

[自己的填表介面](custom-ui.md) · [API 參考](reference.md) · [常見問題](FAQ.md)

## 在管理介面找到回寫入口

完成 intake 設定後，到「流程管理」選擇已連接的原站報名項目。頁面直接讀取既有 intake，列出關聯表單及報名網址，不會新增重複表單。表單列表及回覆頁也會連回所屬流程。

新報名狀態只包含經該 intake 建立的申請；早前從原站匯入的隊伍仍在關聯表單中。待原站確認、已接受、已拒絕和草稿分開顯示。只有原站確認接受，才代表回寫完成；正式回覆的變更同步仍按原接駁流程處理。

需要完全由 OpenDashboard 保存的新活動，使用[中央公開入口](workflows.md#section-8)。不能直接把原站 intake 當作中央 workflow，或停掉來源同步後假設舊資料已完成轉換。
