{"_id":"@1campus/gadget-dev","_rev":"2-edb99d41cf204c2ac60dea21850ee34b","name":"@1campus/gadget-dev","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@1campus/gadget-dev","version":"0.1.0","license":"UNLICENSED","_id":"@1campus/gadget-dev@0.1.0","maintainers":[{"name":"yaoming","email":"yaoming.huang@ischool.com.tw"}],"bin":{"gadget-dev":"bin/gadget-dev.js"},"dist":{"shasum":"48079692a71231d641053190360496999cb11000","tarball":"https://registry.npmjs.org/@1campus/gadget-dev/-/gadget-dev-0.1.0.tgz","fileCount":21,"integrity":"sha512-DxTIJijJJfMiL+1sjfwwVLBnX+fJFJtmqRuoNGIO3BAtoHm0twkhNtygJQTE0LZulqMvo5AUj5co1WCP+drbNQ==","signatures":[{"sig":"MEQCIG42+rzJfmumMk67T9rg++Ls3R/XeSo3GKooAZ1pR6RzAiAIAwc9vEdsAwS95QLKeAfeH8gPXI7JAeeZcC1q1rJmAQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":644433},"engines":{"node":">=18"},"gitHead":"12745efd76b9f398d09a45139b3e23bee6d9ed23","scripts":{"build":"cd client && pnpm build","start":"node bin/gadget-dev.js"},"_npmUser":{"name":"yaoming","email":"yaoming.huang@ischool.com.tw"},"_npmVersion":"10.8.2","description":"Gadget 本機開發工具 — 讓上線版程式碼在本機也能直接開發測試","directories":{},"_nodeVersion":"20.19.4","dependencies":{"koa":"^2.15.0","koa-router":"^12.0.1","koa-static":"^5.0.0","http-proxy-middleware":"^3.0.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/gadget-dev_0.1.0_1775307684038_0.549156471480462","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@1campus/gadget-dev","version":"0.1.1","description":"Gadget 本機開發工具 — 讓上線版程式碼在本機也能直接開發測試","bin":{"gadget-dev":"bin/gadget-dev.js"},"scripts":{"build":"cd client && pnpm build","start":"node bin/gadget-dev.js"},"dependencies":{"http-proxy":"^1.18.1","koa":"^2.15.0","koa-mount":"^4.2.0","koa-router":"^12.0.1","koa-static":"^5.0.0"},"engines":{"node":">=18"},"license":"UNLICENSED","_id":"@1campus/gadget-dev@0.1.1","gitHead":"37500561d798bb0732b7d85297155b04d44db009","_nodeVersion":"24.2.0","_npmVersion":"11.3.0","dist":{"integrity":"sha512-bucpIw3sMrCubXWGqUtvjRinlHB7M7ne3Pccfm4zQ8CXHntaI1qfjoorTW2cr+rAPP7nOHhLDNXBVzhuSst75g==","shasum":"b0ea37527b50141cf8fdedbad1b53bccb6509b63","tarball":"https://registry.npmjs.org/@1campus/gadget-dev/-/gadget-dev-0.1.1.tgz","fileCount":21,"unpackedSize":689316,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC4RP0OqfhXtW7Sgo7ph4hXXMKz9ZrTVQROs4DoCE9aBAIgK9IPChAUZMhu2G7/ofi0k9Zq75a8eQhQrf07NnqvLLo="}]},"_npmUser":{"name":"yaoming","email":"yaoming.huang@ischool.com.tw"},"directories":{},"maintainers":[{"name":"yaoming","email":"yaoming.huang@ischool.com.tw"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/gadget-dev_0.1.1_1775447755902_0.5775350520371083"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-04T13:01:23.888Z","modified":"2026-04-06T03:55:56.481Z","0.1.0":"2026-04-04T13:01:24.201Z","0.1.1":"2026-04-06T03:55:56.060Z"},"license":"UNLICENSED","description":"Gadget 本機開發工具 — 讓上線版程式碼在本機也能直接開發測試","maintainers":[{"name":"yaoming","email":"yaoming.huang@ischool.com.tw"}],"readme":"# @1campus/gadget-dev\n\nGadget 本機開發工具 — 讓上線版程式碼在本機也能直接開發測試。\n\n## 使用方式\n\n```bash\n# 啟動 dev server\nnpx @1campus/gadget-dev <gadget-目錄>\nnpx @1campus/gadget-dev <gadget-目錄> --port 3000\n\n# 範例\nnpx @1campus/gadget-dev /path/to/coregadget\nnpx @1campus/gadget-dev ./deployment --port 3000\n```\n\n啟動後在瀏覽器開啟終端顯示的網址（預設 `http://localhost:9876`）。\n\n## 登入機制\n\n- 首次啟動自動開瀏覽器登入 1Campus（透過 1campus.net OAuth 中轉）\n- Token 持久化到 `~/.gadget-dev/token.json`，下次啟動自動檢查：\n  - 有效 → 免登入\n  - 過期 → 自動開瀏覽器重新登入\n- 切換帳號：主畫面右上角「重新登入」按鈕\n\n## 目錄結構\n\n```\ngadget-dev-cli/\n├── bin/\n│   └── gadget-dev.js         # CLI 入口\n├── src/\n│   ├── server.js             # Koa server（API + 靜態檔案 + reverse proxy）\n│   └── auth.js               # 登入流程（token 持久化 + 驗證）\n├── client/                   # Angular 20 前端\n│   ├── src/\n│   │   ├── app/\n│   │   │   ├── services/     # AuthService, SchoolService, GadgetService, GadgetProxyService\n│   │   │   ├── pages/        # login/, main/\n│   │   │   ├── components/   # gadget-modal/\n│   │   │   └── callback.ts   # OAuth callback 頁面\n│   │   ├── assets/lib/       # DSA 連線庫（同步自 1campus.net client/src/lib/）\n│   │   ├── index.html        # 入口 HTML（載入 DSA lib script）\n│   │   └── styles.css        # 全域樣式（Tailwind v4 + DaisyUI 5 winter 主題）\n│   ├── .postcssrc.json       # PostCSS 設定（@tailwindcss/postcss）\n│   ├── angular.json\n│   └── package.json\n├── package.json              # CLI 套件設定\n└── README.md\n```\n\n## 開發方式\n\n```bash\n# 安裝依賴\ncd gadget-dev-cli && pnpm install\ncd client && pnpm install && cd ..\n\n# Build 前端\npnpm build\n\n# 啟動 dev server（從 gadget-dev-cli 目錄）\nnode bin/gadget-dev.js /path/to/gadget-目錄\n\n# 從專案根目錄啟動\nnode gadget-dev-cli/bin/gadget-dev.js /Users/yaoming/src/web/web2.ischool.com.tw/deployment\n```\n\n## 發佈到 npm\n\n```bash\n# 1. 確保前端已 build\ncd client && pnpm build && cd ..\n\n# 2. 更新 package.json 的 version\n\n# 3. 產生 Granular Access Token（因為 @1campus org 強制要求 2FA）\n#    去 https://www.npmjs.com/settings/<username>/tokens\n#    → Generate New Token → Granular Access Token\n#    → Packages: Read and write\n#    → 勾選 \"Allow publishing with this token without 2FA\"\n\n# 4. 發佈\nnpm publish --access public --registry https://registry.npmjs.org/ --//registry.npmjs.org/:_authToken=<你的token>\n\n# 5. 發佈完成後去 npmjs 網頁刪掉該 token（一次性使用）\n```\n\n### 預覽發佈內容\n\n```bash\nnpm pack --dry-run\n```\n\n發佈內容由 `package.json` 的 `files` 欄位 + `client/.npmignore` 控制，只包含 `bin/`、`src/`、`client/dist/`、`README.md`。\n\n## Gadget 三種載入模式\n\n| 模式 | 說明 | 使用情境 |\n|------|------|----------|\n| 靜態 | 從本機 gadget 目錄讀取檔案 | 有原始碼在本機 |\n| Proxy | 轉發到本機 dev server port | 框架 gadget（Angular/React/Vue）開發 |\n| 遠端 | 從遠端 URL 載入 | 沒有原始碼，用正式站 gadget 測試 |\n\n遠端模式預設 URL：`https://legacy-web2.ischool.com.tw/deployment`\n\n## 架構說明\n\n### Koa Server（src/server.js）\n\n- 靜態檔案：serve Angular build 產出（`client/dist/client/browser/`）\n- SPA fallback：非 API/gadget 路徑回傳 `index.html`\n- API 路由：\n  - `GET/POST /api/token` — Token 持久化（讀取/儲存本機檔案，含 dsa_token）\n  - `GET /api/userinfo` — 代理 auth.1campus.net（避免 CORS）\n  - `GET /api/gadgets` — 掃描 gadget 目錄\n  - `POST/GET/DELETE /api/proxy` — 管理 reverse proxy 設定（port 或 remoteUrl）\n- Gadget 檔案：`/gadget/:guid/*`（靜態 / proxy / 遠端三種模式）\n- 開發模式不快取（`Cache-Control: no-store, no-cache`）\n\n### Angular 前端（client/）\n\n- `/login` — 登入頁面（未登入時顯示）\n- `/main` — 主畫面（學校/角色選擇 + gadget 列表 + iframe）\n- `/callback` — OAuth 登入完成後接收 token\n- AuthGuard：未登入自動導向 /login，已登入自動跳過 /login\n\n### GadgetProxy\n\n同步對應 `1campus.net client/src/app/school/gadget-host/gadget-proxy.ts`，提供 `window.getGadgetProxy()` API，必須與正式站行為完全一致。\n\nDSA 連線使用 `window.auth`（authentication.js）的 `connectTo()` 方法，透過 dsa_token（無 OAuth scope 限制）認證。每個 gadget 可設定 params JSON，透過 `gadget.params` 提供給 gadget 使用。\n\n### DSA 連線庫（client/src/assets/lib/）\n\n同步自 `1campus.net/client/src/lib/`，10 年沒變。如果主專案的 lib 有更新，這裡也要同步複製。\n\n包含：dsutil.js、authentication.js、parseXml.js、xml2json.js、xmlWriter.js、dsnsLookup.js、config.js、jQuery 3.7.1。\n","readmeFilename":"README.md"}