'use strict'; /** * 判斷環境變數值是否視為「未設定」。 * * 下列任一情況皆視為未設定:undefined、null、空字串、字面字串 "null"。 * * @param {*} value - 欲檢查的值(通常為環境變數字串)。 * @returns {boolean} 視為未設定時回傳 true,否則回傳 false。 */ function isUnset(value) { return value === undefined || value === null || value === '' || value === 'null'; } /** * 驗證必填環境變數;未設定時拋出錯誤。 * * @param {string} name - 環境變數名稱,用於組出錯誤訊息。 * @param {*} value - 環境變數的值。 * @returns {*} 驗證通過後原樣回傳的 value。 * @throws {Error} 當 value 被視為未設定(undefined/null/空字串/"null")時拋出,訊息為 `${name} 未設定`。 */ function requireEnv(name, value) { if (isUnset(value)) { throw new Error(`${name} 未設定`); } return value; } /** * 將 beta 旗標正規化為布林值。 * * 未設定時預設為 false;僅當值嚴格等於字面字串 "true" 時回傳 true。 * * @param {*} value - beta 旗標環境變數的值。 * @returns {boolean} 啟用 beta 時回傳 true,否則回傳 false。 */ function normalizeBetaFlag(value) { if (isUnset(value)) { return false; } return value === 'true'; } // 驗證字串為合法的 http/https URL,否則拋出錯誤(避免指向非預期協定或格式錯誤的位址) function assertHttpUrl(name, value) { let parsed; try { parsed = new URL(value); } catch { throw new Error(`${name} 格式錯誤,必須為合法的 URL`); } if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { throw new Error(`${name} 必須使用 http 或 https 協定`); } } // repository 每段名稱的最大長度與允許字元(字母數字與 . _ -) const MAX_REPO_NAME_LENGTH = 100; const REPO_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/; // 單一 repository 區段是否合法:長度在範圍內、僅含允許字元,且非 . 或 .. 路徑穿越段 function isValidRepoSegment(part) { return part.length > 0 && part.length <= MAX_REPO_NAME_LENGTH && REPO_SEGMENT_PATTERN.test(part) && part !== '.' && part !== '..'; } // 驗證 repository 為 owner/repo 格式(恰兩段,且每段皆為合法區段) function assertRepository(name, value) { const parts = value.split('/'); const isRepoFormatValid = parts.length === 2 && parts.every(isValidRepoSegment); if (!isRepoFormatValid) { throw new Error(`${name} 格式錯誤,必須為 owner/repo`); } } /** * 從環境變數載入並驗證執行所需的設定。 * * GITEA_SERVER_URL 與 GITEA_REPOSITORY 為必填,未設定時會拋出錯誤;GITEA_SERVER_URL * 另需為合法的 http/https URL,GITEA_REPOSITORY 另需為 owner/repo 格式; * GITEA_TOKEN 為非必填,未設定時為 null;IS_BETA 會被正規化為布林值。 * * @param {Object} [env=process.env] - 環境變數來源物件,預設為 process.env。 * @returns {{ serverUrl: string, repository: string, token: (string|null), isBeta: boolean }} 已驗證的設定物件。 * @throws {Error} 當必填項未設定、GITEA_SERVER_URL 非合法 http/https URL,或 GITEA_REPOSITORY 非 owner/repo 格式時拋出。 */ function loadConfig(env = process.env) { const serverUrl = requireEnv('GITEA_SERVER_URL', env.GITEA_SERVER_URL); assertHttpUrl('GITEA_SERVER_URL', serverUrl); const repository = requireEnv('GITEA_REPOSITORY', env.GITEA_REPOSITORY); assertRepository('GITEA_REPOSITORY', repository); const token = isUnset(env.GITEA_TOKEN) ? null : env.GITEA_TOKEN; const isBeta = normalizeBetaFlag(env.IS_BETA); return { serverUrl, repository, token, isBeta }; } module.exports = { isUnset, requireEnv, normalizeBetaFlag, loadConfig };