feat: add selectable forwarding modes and optional preview deployment
默认使用可信同源路径转发,支持界面切换隔离预览并按需部署。
This commit is contained in:
1 parent
fc7a0caf81
commit
5d65b34907
24 files changed
+680
-102
No files matched your search
+16
-11
@@ -5,7 +5,7 @@ import { parse, stringify } from 'smol-toml';
|
||||
import { ensureDatabase } from './cloudflare-d1.ts';
|
||||
import { createDeploymentConfig, createPreviewDeploymentConfig, readDeploymentSettings } from './deployment-config.ts';
|
||||
import { CloudflareApi, resolveAccountId } from './cloudflare-api.ts';
|
||||
import { resolveWorkersDevSubdomain } from './cloudflare-access.ts';
|
||||
import { resolveWorkerHostname, resolveWorkersDevSubdomain } from './cloudflare-access.ts';
|
||||
import { previewOrigin } from '../src/forwarding/security.ts';
|
||||
import { maskSecrets, prepareEncryptionSecret, readWorkerSecretNames, readWorkerVariable } from './deployment-secrets.ts';
|
||||
import { prepareAuthentication, requiredAuthSecrets } from './deployment-auth.ts';
|
||||
@@ -44,10 +44,13 @@ async function main(): Promise<void> {
|
||||
settings.accountId = await resolveAccountId(api, settings.accountId);
|
||||
process.env.CLOUDFLARE_ACCOUNT_ID = settings.accountId;
|
||||
const existingSecrets = await readWorkerSecretNames(api, settings);
|
||||
const workersSubdomain = await resolveWorkersDevSubdomain(api, settings);
|
||||
const hostname = settings.customDomain || `${settings.workerName}.${workersSubdomain}.workers.dev`;
|
||||
const previewHostname = settings.previewDomain || `${settings.workerName}-preview.${workersSubdomain}.workers.dev`;
|
||||
const preview = previewOrigin(`https://${previewHostname}`, `https://${hostname}`);
|
||||
const hostname = settings.customDomain || await resolveWorkerHostname(api, settings);
|
||||
let preview = await readWorkerVariable(api, settings, 'PREVIEW_ORIGIN');
|
||||
let previewHostname: string | undefined;
|
||||
if (settings.deployPreview) {
|
||||
previewHostname = settings.previewDomain || `${settings.workerName}-preview.${await resolveWorkersDevSubdomain(api, settings)}.workers.dev`;
|
||||
preview = previewOrigin(`https://${previewHostname}`, `https://${hostname}`);
|
||||
}
|
||||
const database = await ensureDatabase(settings);
|
||||
const workspace = await readWorkspaceState(api, settings, database);
|
||||
const deployedProvider = workspace.authProvider
|
||||
@@ -68,7 +71,7 @@ async function main(): Promise<void> {
|
||||
const secrets = { ...authentication.secrets, ...await prepareEncryptionSecret(api, settings, database, existingSecrets) };
|
||||
maskSecrets(secrets);
|
||||
const config = createDeploymentConfig(template, settings, database, {
|
||||
hostname, previewOrigin: preview, githubAdminId: authentication.githubAdminId,
|
||||
hostname, ...(preview ? { previewOrigin: preview } : {}), githubAdminId: authentication.githubAdminId,
|
||||
});
|
||||
// 临时配置放在仓库根目录,保持 assets、main、migrations_dir 的相对路径语义。
|
||||
await writeFile(new URL(`../${generatedConfig}`, import.meta.url), stringify(config), 'utf8');
|
||||
@@ -84,13 +87,15 @@ async function main(): Promise<void> {
|
||||
if (!deployedSecrets.has(name)) throw new Error(`部署后缺少 Worker Secret:${name}。`);
|
||||
}
|
||||
// 预览 Worker 只做代理入口和 DO 绑定,不执行迁移、不写入任何 Secret。
|
||||
const previewConfig = createPreviewDeploymentConfig(template, settings, preview);
|
||||
await writeFile(new URL(`../${previewGeneratedConfig}`, import.meta.url), stringify(previewConfig), 'utf8');
|
||||
await runWrangler(['deploy'], undefined, previewGeneratedConfig);
|
||||
console.log(`部署完成:https://${hostname},预览:https://${previewHostname}`);
|
||||
if (settings.deployPreview) {
|
||||
const previewConfig = createPreviewDeploymentConfig(template, settings, preview!);
|
||||
await writeFile(new URL(`../${previewGeneratedConfig}`, import.meta.url), stringify(previewConfig), 'utf8');
|
||||
await runWrangler(['deploy'], undefined, previewGeneratedConfig);
|
||||
}
|
||||
console.log(`部署完成:https://${hostname}${settings.deployPreview ? `,预览:https://${previewHostname}` : '(仅主 Worker)'}`);
|
||||
if (process.env.GITHUB_STEP_SUMMARY) {
|
||||
await appendFile(process.env.GITHUB_STEP_SUMMARY,
|
||||
`## EdgeSSH 部署完成\n\n入口:https://${hostname}\n\n登录方式:${settings.authProvider}\n\n`
|
||||
`## EdgeSSH 部署完成\n\n入口:https://${hostname}\n\n${settings.deployPreview ? `预览:https://${previewHostname}\n\n` : ''}登录方式:${settings.authProvider}\n\n`
|
||||
+ (settings.authProvider === 'github' ? `GitHub OAuth 回调地址:https://${hostname}/auth/callback\n\n` : '')
|
||||
+ 'D1 已迁移,运行时 Secret 已保存在 Cloudflare。后续部署保留原加密密钥与管理员资料。\n');
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface DeploymentSettings {
|
||||
databaseId?: string;
|
||||
customDomain?: string;
|
||||
previewDomain?: string;
|
||||
deployPreview?: boolean;
|
||||
authProvider: 'cloudflare' | 'github';
|
||||
githubClientId?: string;
|
||||
githubAdmin?: string;
|
||||
@@ -39,7 +40,10 @@ export function readDeploymentSettings(
|
||||
if (accountId && !/^[a-f0-9]{32}$/i.test(accountId)) throw new Error('CLOUDFLARE_ACCOUNT_ID 必须是 32 位十六进制账户 ID。');
|
||||
const workerName = env.WORKER_NAME?.trim() || String(template.name);
|
||||
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(workerName)) throw new Error('WORKER_NAME 必须是 1–63 位小写字母、数字或连字符,且不能以连字符开头。');
|
||||
if (`${workerName}-preview`.length > 63) throw new Error('WORKER_NAME 加上 -preview 后不能超过 63 位。');
|
||||
const deployPreviewValue = env.DEPLOY_PREVIEW_WORKER?.trim().toLowerCase() || 'false';
|
||||
if (deployPreviewValue !== 'true' && deployPreviewValue !== 'false') throw new Error('DEPLOY_PREVIEW_WORKER 只能是 true 或 false。');
|
||||
const deployPreview = deployPreviewValue === 'true';
|
||||
if (deployPreview && `${workerName}-preview`.length > 63) throw new Error('WORKER_NAME 加上 -preview 后不能超过 63 位。');
|
||||
|
||||
const databases = template.d1_databases as TomlTable[] | undefined;
|
||||
const database = databases?.find((binding) => binding.binding === 'DB');
|
||||
@@ -59,11 +63,11 @@ export function readDeploymentSettings(
|
||||
if (customDomain?.toLowerCase().endsWith('.workers.dev')) {
|
||||
throw new Error('CUSTOM_DOMAIN 不能填写 workers.dev 地址;使用 Worker 自带域名时请删除或留空该配置。');
|
||||
}
|
||||
const previewDomain = env.PREVIEW_DOMAIN?.trim();
|
||||
if (previewDomain && !/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i.test(previewDomain)) {
|
||||
const previewDomain = deployPreview ? env.PREVIEW_DOMAIN?.trim() : undefined;
|
||||
if (deployPreview && previewDomain && !/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i.test(previewDomain)) {
|
||||
throw new Error('PREVIEW_DOMAIN 只能填写完整域名,不能包含协议、路径或通配符。');
|
||||
}
|
||||
if (previewDomain?.toLowerCase().endsWith('.workers.dev')) {
|
||||
if (deployPreview && previewDomain?.toLowerCase().endsWith('.workers.dev')) {
|
||||
throw new Error('PREVIEW_DOMAIN 不能填写 workers.dev 地址;请使用自定义跨站域名,或留空自动生成预览地址。');
|
||||
}
|
||||
|
||||
@@ -107,7 +111,7 @@ export function readDeploymentSettings(
|
||||
|
||||
return {
|
||||
accountId, apiToken: env.CLOUDFLARE_API_TOKEN!.trim(), workerName,
|
||||
databaseName, databaseId, customDomain, previewDomain, authProvider, githubClientId, githubAdmin, githubAdminId,
|
||||
databaseName, databaseId, customDomain, previewDomain, deployPreview, authProvider, githubClientId, githubAdmin, githubAdminId,
|
||||
adminEmail, identityProviderIds, secrets,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user