alepha 0.19.3 → 0.19.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/dist/api/invitations/index.d.ts +790 -0
- package/dist/api/invitations/index.d.ts.map +1 -0
- package/dist/api/invitations/index.js +665 -0
- package/dist/api/invitations/index.js.map +1 -0
- package/dist/api/issues/index.d.ts +810 -0
- package/dist/api/issues/index.d.ts.map +1 -0
- package/dist/api/issues/index.js +447 -0
- package/dist/api/issues/index.js.map +1 -0
- package/dist/api/jobs/index.browser.js +8 -9
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +99 -43
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +257 -40
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +0 -1
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +3 -3
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +0 -1
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +112 -1
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +90 -3
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +79 -12
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/{billing → api/payments}/index.d.ts +67 -49
- package/dist/api/payments/index.d.ts.map +1 -0
- package/dist/{billing → api/payments}/index.js +108 -74
- package/dist/api/payments/index.js.map +1 -0
- package/dist/api/subscriptions/index.d.ts +1692 -0
- package/dist/api/subscriptions/index.d.ts.map +1 -0
- package/dist/api/subscriptions/index.js +1870 -0
- package/dist/api/subscriptions/index.js.map +1 -0
- package/dist/api/users/index.d.ts +24 -2
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +176 -36
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +13 -13
- package/dist/api/workflows/index.browser.js +246 -0
- package/dist/api/workflows/index.browser.js.map +1 -0
- package/dist/api/workflows/index.d.ts +1618 -0
- package/dist/api/workflows/index.d.ts.map +1 -0
- package/dist/api/workflows/index.js +1504 -0
- package/dist/api/workflows/index.js.map +1 -0
- package/dist/captcha/index.d.ts +142 -0
- package/dist/captcha/index.d.ts.map +1 -0
- package/dist/captcha/index.js +177 -0
- package/dist/captcha/index.js.map +1 -0
- package/dist/cli/core/index.d.ts +126 -30
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +106 -67
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +84 -10
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +92 -4
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +60 -10
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js +177 -45
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2 -3
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +21 -2
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +33 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +21 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +21 -2
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +21 -2
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/email/smtp/index.js +24 -8
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +0 -18
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +6 -23
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +1 -13
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +6 -23
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +3 -3
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +3 -3
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +1 -0
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +8 -4
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/router/index.browser.js +25 -3
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +16 -1
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +25 -3
- package/dist/react/router/index.js.map +1 -1
- package/dist/security/index.d.ts +28 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +28 -0
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +145 -2
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +364 -63
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js.map +1 -1
- package/package.json +47 -20
- package/src/api/invitations/__tests__/InvitationService.spec.ts +439 -0
- package/src/api/invitations/controllers/AdminInvitationController.ts +86 -0
- package/src/api/invitations/controllers/InvitationController.ts +84 -0
- package/src/api/invitations/entities/invitations.ts +33 -0
- package/src/api/invitations/index.ts +65 -0
- package/src/api/invitations/jobs/InvitationJobs.ts +37 -0
- package/src/api/invitations/providers/InvitationProvider.ts +45 -0
- package/src/api/invitations/schemas/createInvitationSchema.ts +12 -0
- package/src/api/invitations/schemas/invitationConfigAtom.ts +20 -0
- package/src/api/invitations/schemas/invitationQuerySchema.ts +15 -0
- package/src/api/invitations/schemas/invitationResourceSchema.ts +6 -0
- package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +22 -0
- package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +10 -0
- package/src/api/invitations/services/InvitationService.ts +556 -0
- package/src/api/issues/__tests__/IssueService.spec.ts +263 -0
- package/src/api/issues/controllers/AdminIssueController.ts +149 -0
- package/src/api/issues/controllers/IssueController.ts +44 -0
- package/src/api/issues/entities/issues.ts +49 -0
- package/src/api/issues/index.ts +53 -0
- package/src/api/issues/schemas/createIssueSchema.ts +13 -0
- package/src/api/issues/schemas/issueConfigAtom.ts +13 -0
- package/src/api/issues/schemas/issueQuerySchema.ts +18 -0
- package/src/api/issues/schemas/issueResourceSchema.ts +6 -0
- package/src/api/issues/schemas/myIssueQuerySchema.ts +10 -0
- package/src/api/issues/schemas/updateIssueSchema.ts +13 -0
- package/src/api/issues/services/IssueService.ts +264 -0
- package/src/api/jobs/__tests__/$job.spec.ts +876 -0
- package/src/api/jobs/controllers/AdminJobController.ts +44 -0
- package/src/api/jobs/entities/jobExecutionEntity.ts +0 -2
- package/src/api/jobs/index.ts +0 -3
- package/src/api/jobs/primitives/$job.ts +22 -11
- package/src/api/jobs/providers/JobProvider.ts +229 -19
- package/src/api/jobs/schemas/jobConfigAtom.ts +4 -0
- package/src/api/jobs/schemas/jobCronInfoSchema.ts +1 -0
- package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +0 -1
- package/src/api/jobs/schemas/jobQueueDepthSchema.ts +1 -0
- package/src/api/jobs/schemas/jobRegistrationSchema.ts +1 -6
- package/src/api/jobs/services/JobService.ts +51 -12
- package/src/api/notifications/schemas/notificationQuerySchema.ts +0 -1
- package/src/api/parameters/__tests__/$parameter.spec.ts +327 -0
- package/src/api/parameters/controllers/AdminParameterController.ts +29 -3
- package/src/api/parameters/index.browser.ts +12 -0
- package/src/api/parameters/primitives/$parameter.ts +20 -3
- package/src/api/parameters/services/ParameterProvider.ts +48 -7
- package/src/{billing → api/payments}/__tests__/PaymentMethodService.spec.ts +32 -6
- package/src/api/payments/__tests__/PaymentService.spec.ts +279 -0
- package/src/{billing/controllers/AdminBillingController.ts → api/payments/controllers/AdminPaymentController.ts} +26 -21
- package/src/{billing/controllers/BillingController.ts → api/payments/controllers/PaymentController.ts} +23 -11
- package/src/{billing → api/payments}/entities/paymentIntents.ts +1 -0
- package/src/{billing/errors/BillingError.ts → api/payments/errors/PaymentError.ts} +1 -1
- package/src/{billing → api/payments}/index.ts +31 -25
- package/src/{billing/providers/MemoryBillingProvider.ts → api/payments/providers/MemoryPaymentProvider.ts} +4 -4
- package/src/{billing/providers/BillingProvider.ts → api/payments/providers/PaymentProvider.ts} +9 -2
- package/src/{billing → api/payments}/services/PaymentMethodService.ts +5 -5
- package/src/{billing/services/BillingService.ts → api/payments/services/PaymentService.ts} +94 -18
- package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
- package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
- package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
- package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
- package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
- package/src/api/subscriptions/entities/subscriptions.ts +68 -0
- package/src/api/subscriptions/index.ts +144 -0
- package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
- package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
- package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
- package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
- package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
- package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
- package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
- package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
- package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
- package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
- package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
- package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
- package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
- package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
- package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
- package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
- package/src/api/subscriptions/services/BillingService.ts +437 -0
- package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
- package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
- package/src/api/subscriptions/services/UsageService.ts +118 -0
- package/src/api/users/__tests__/AdminUserController.spec.ts +80 -1
- package/src/api/users/__tests__/CredentialService.spec.ts +177 -0
- package/src/api/users/__tests__/EmailVerification.spec.ts +29 -18
- package/src/api/users/__tests__/PasswordReset.spec.ts +3 -0
- package/src/api/users/__tests__/RegistrationService.spec.ts +148 -1
- package/src/api/users/__tests__/SessionService.spec.ts +142 -1
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -1
- package/src/api/users/controllers/UserController.ts +3 -8
- package/src/api/users/notifications/UserNotifications.ts +23 -0
- package/src/api/users/primitives/$realm.ts +24 -0
- package/src/api/users/schemas/loginSchema.ts +1 -1
- package/src/api/users/services/CredentialService.ts +57 -7
- package/src/api/users/services/RegistrationService.ts +50 -11
- package/src/api/users/services/SessionService.ts +64 -9
- package/src/api/users/services/UserService.ts +21 -12
- package/src/api/workflows/__tests__/$workflow.spec.ts +616 -0
- package/src/api/workflows/controllers/AdminWorkflowController.ts +191 -0
- package/src/api/workflows/entities/workflowExecutions.ts +74 -0
- package/src/api/workflows/entities/workflowStepExecutions.ts +74 -0
- package/src/api/workflows/entities/workflowStepLogs.ts +13 -0
- package/src/api/workflows/index.browser.ts +22 -0
- package/src/api/workflows/index.ts +124 -0
- package/src/api/workflows/jobs/WorkflowJobs.ts +77 -0
- package/src/api/workflows/primitives/$workflow.ts +202 -0
- package/src/api/workflows/providers/WorkflowProvider.ts +1284 -0
- package/src/api/workflows/schemas/workflowActivitySchema.ts +15 -0
- package/src/api/workflows/schemas/workflowConfigAtom.ts +51 -0
- package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +18 -0
- package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +26 -0
- package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +30 -0
- package/src/api/workflows/schemas/workflowRegistrationSchema.ts +26 -0
- package/src/api/workflows/schemas/workflowStatsSchema.ts +16 -0
- package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +15 -0
- package/src/api/workflows/services/WorkflowService.ts +382 -0
- package/src/captcha/__tests__/MemoryCaptchaProvider.spec.ts +74 -0
- package/src/captcha/index.ts +33 -0
- package/src/captcha/providers/CaptchaProvider.ts +17 -0
- package/src/captcha/providers/MemoryCaptchaProvider.ts +65 -0
- package/src/captcha/providers/TurnstileCaptchaProvider.ts +125 -0
- package/src/cli/core/atoms/buildOptions.ts +57 -0
- package/src/cli/core/commands/build.ts +2 -0
- package/src/cli/core/providers/ViteDevServerProvider.ts +1 -1
- package/src/cli/core/services/ViteUtils.ts +5 -2
- package/src/cli/core/tasks/BuildClientTask.ts +3 -1
- package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -0
- package/src/cli/core/tasks/BuildPwaTask.ts +81 -0
- package/src/cli/core/templates/webAppRouterTs.ts +5 -58
- package/src/cli/platform/adapters/CloudflareAdapter.ts +24 -0
- package/src/cli/platform/atoms/platformOptions.ts +19 -3
- package/src/cli/platform/hooks/PlatformHook.ts +51 -0
- package/src/cli/platform/index.ts +1 -0
- package/src/cli/platform/services/CloudflareApi.ts +22 -1
- package/src/cli/platform/services/PlatformOrchestrator.ts +67 -2
- package/src/cli/vendor/__tests__/VendorService.spec.ts +322 -178
- package/src/cli/vendor/commands/VendorCommand.ts +41 -38
- package/src/cli/vendor/services/VendorService.ts +234 -31
- package/src/command/__tests__/CliProvider.spec.ts +45 -0
- package/src/command/providers/CliProvider.ts +3 -4
- package/src/core/__tests__/TypeProvider.spec.ts +4 -2
- package/src/core/providers/SchemaValidator.ts +1 -1
- package/src/core/providers/TypeProvider.ts +46 -3
- package/src/orm/__tests__/enums.spec.ts +22 -29
- package/src/orm/__tests__/orm-showcase-tests.ts +430 -0
- package/src/orm/__tests__/orm-showcase.spec.ts +167 -0
- package/src/orm/core/providers/DatabaseTypeProvider.ts +0 -29
- package/src/orm/core/services/Repository.ts +20 -6
- package/src/orm/postgres/services/PostgresModelBuilder.ts +3 -6
- package/src/react/i18n/__tests__/I18nProvider.spec.ts +83 -0
- package/src/react/i18n/providers/I18nProvider.ts +12 -10
- package/src/react/router/__tests__/$page.browser.spec.tsx +157 -0
- package/src/react/router/providers/ReactBrowserProvider.ts +39 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +22 -0
- package/src/security/__tests__/$secure-combinations.spec.ts +945 -0
- package/src/security/primitives/$issuer.ts +3 -1
- package/src/security/primitives/$secure.ts +28 -0
- package/src/server/auth/index.ts +7 -0
- package/src/server/auth/primitives/$auth.ts +37 -3
- package/src/server/auth/primitives/$authApple.ts +114 -4
- package/src/server/auth/primitives/$authFacebook.ts +98 -0
- package/src/server/auth/primitives/$authFranceConnect.ts +105 -0
- package/src/server/auth/primitives/$authGithub.ts +22 -16
- package/src/server/auth/primitives/$authMicrosoft.ts +88 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +197 -72
- package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -0
- package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +1 -1
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -1
- package/dist/billing/index.d.ts.map +0 -1
- package/dist/billing/index.js.map +0 -1
- package/src/billing/__tests__/BillingService.spec.ts +0 -136
- /package/src/{billing → api/payments}/entities/paymentMethods.ts +0 -0
- /package/src/{billing → api/payments}/entities/refunds.ts +0 -0
- /package/src/{billing → api/payments}/schemas/intentSchemas.ts +0 -0
- /package/src/{billing → api/payments}/schemas/paymentMethodSchemas.ts +0 -0
- /package/src/{billing → api/payments}/schemas/refundSchemas.ts +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Captcha verification provider interface.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that a user-submitted captcha token is valid. Implementations
|
|
5
|
+
* call the relevant captcha service (Turnstile, reCAPTCHA, hCaptcha, etc.)
|
|
6
|
+
* to validate the token server-side.
|
|
7
|
+
*/
|
|
8
|
+
export abstract class CaptchaProvider {
|
|
9
|
+
/**
|
|
10
|
+
* Verify a captcha token.
|
|
11
|
+
*
|
|
12
|
+
* @param token - The captcha response token submitted by the client.
|
|
13
|
+
* @param ip - Optional client IP address for additional validation.
|
|
14
|
+
* @returns Whether the token is valid.
|
|
15
|
+
*/
|
|
16
|
+
public abstract verify(token: string, ip?: string): Promise<boolean>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { $logger } from "alepha/logger";
|
|
2
|
+
import type { CaptchaProvider } from "./CaptchaProvider.ts";
|
|
3
|
+
|
|
4
|
+
export interface CaptchaRecord {
|
|
5
|
+
token: string;
|
|
6
|
+
ip?: string;
|
|
7
|
+
verifiedAt: Date;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* In-memory captcha provider for testing.
|
|
12
|
+
*
|
|
13
|
+
* Accepts all tokens by default. Use `reject()` to make verification fail,
|
|
14
|
+
* and `accept()` to restore. All verification attempts are recorded for assertions.
|
|
15
|
+
*/
|
|
16
|
+
export class MemoryCaptchaProvider implements CaptchaProvider {
|
|
17
|
+
protected readonly log = $logger();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* All verification attempts.
|
|
21
|
+
*/
|
|
22
|
+
public records: CaptchaRecord[] = [];
|
|
23
|
+
|
|
24
|
+
protected shouldAccept = true;
|
|
25
|
+
|
|
26
|
+
public async verify(token: string, ip?: string): Promise<boolean> {
|
|
27
|
+
this.log.debug("Verifying captcha in memory store", { token, ip });
|
|
28
|
+
|
|
29
|
+
this.records.push({
|
|
30
|
+
token,
|
|
31
|
+
ip,
|
|
32
|
+
verifiedAt: new Date(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return this.shouldAccept;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Make all subsequent verifications fail.
|
|
40
|
+
*/
|
|
41
|
+
public reject(): void {
|
|
42
|
+
this.shouldAccept = false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Make all subsequent verifications pass (default behavior).
|
|
47
|
+
*/
|
|
48
|
+
public accept(): void {
|
|
49
|
+
this.shouldAccept = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether a token was verified.
|
|
54
|
+
*/
|
|
55
|
+
public wasVerified(token: string): boolean {
|
|
56
|
+
return this.records.some((r) => r.token === token);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the last verification attempt.
|
|
61
|
+
*/
|
|
62
|
+
public get last(): CaptchaRecord | undefined {
|
|
63
|
+
return this.records[this.records.length - 1];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { $context, AlephaError, t } from "alepha";
|
|
2
|
+
import { $logger } from "alepha/logger";
|
|
3
|
+
import type { CaptchaProvider } from "./CaptchaProvider.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cloudflare Turnstile captcha verification provider.
|
|
7
|
+
*
|
|
8
|
+
* Validates captcha tokens against the Cloudflare Turnstile siteverify API.
|
|
9
|
+
* Free, privacy-friendly, and supports invisible mode.
|
|
10
|
+
*
|
|
11
|
+
* ## Setup
|
|
12
|
+
*
|
|
13
|
+
* 1. Create a Turnstile widget at https://dash.cloudflare.com/?to=/:account/turnstile
|
|
14
|
+
* 2. Copy the **Site Key** (public, for the client) and **Secret Key** (private, for the server)
|
|
15
|
+
* 3. Set `TURNSTILE_SECRET_KEY` in your environment
|
|
16
|
+
*
|
|
17
|
+
* ## Client-side integration
|
|
18
|
+
*
|
|
19
|
+
* Add the Turnstile script and widget to your form:
|
|
20
|
+
*
|
|
21
|
+
* ```html
|
|
22
|
+
* <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
|
23
|
+
* <form>
|
|
24
|
+
* <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY"></div>
|
|
25
|
+
* <button type="submit">Submit</button>
|
|
26
|
+
* </form>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* The widget injects a hidden `cf-turnstile-response` input into the form.
|
|
30
|
+
* Send this value as the `captchaToken` in your registration request.
|
|
31
|
+
*
|
|
32
|
+
* For explicit rendering (React, SPA):
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* turnstile.render("#container", {
|
|
36
|
+
* sitekey: "YOUR_SITE_KEY",
|
|
37
|
+
* callback: (token) => setCaptchaToken(token),
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* ## Server-side usage
|
|
42
|
+
*
|
|
43
|
+
* Register the provider in your app:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { CaptchaProvider } from "alepha/captcha";
|
|
47
|
+
* import { TurnstileCaptchaProvider } from "alepha/captcha";
|
|
48
|
+
*
|
|
49
|
+
* alepha.with({ provide: CaptchaProvider, use: TurnstileCaptchaProvider });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* ## Test keys (for development)
|
|
53
|
+
*
|
|
54
|
+
* - Always passes: site `1x00000000000000000000AA`, secret `1x0000000000000000000000000000000AA`
|
|
55
|
+
* - Always blocks: site `2x00000000000000000000AB`, secret `2x0000000000000000000000000000000AB`
|
|
56
|
+
* - Forces interactive: site `3x00000000000000000000FF`
|
|
57
|
+
*
|
|
58
|
+
* ## Environment Variables
|
|
59
|
+
*
|
|
60
|
+
* - `TURNSTILE_SECRET_KEY`: The secret key from the Cloudflare Turnstile dashboard.
|
|
61
|
+
*
|
|
62
|
+
* @see https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
|
|
63
|
+
*/
|
|
64
|
+
export class TurnstileCaptchaProvider implements CaptchaProvider {
|
|
65
|
+
protected readonly log = $logger();
|
|
66
|
+
protected readonly secretKey: string;
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
const { alepha } = $context();
|
|
70
|
+
|
|
71
|
+
const env = alepha.parseEnv(
|
|
72
|
+
t.object({
|
|
73
|
+
TURNSTILE_SECRET_KEY: t.text({
|
|
74
|
+
description:
|
|
75
|
+
"The secret key from the Cloudflare Turnstile dashboard.",
|
|
76
|
+
}),
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
this.secretKey = env.TURNSTILE_SECRET_KEY;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public async verify(token: string, ip?: string): Promise<boolean> {
|
|
84
|
+
const body = new URLSearchParams();
|
|
85
|
+
body.set("secret", this.secretKey);
|
|
86
|
+
body.set("response", token);
|
|
87
|
+
|
|
88
|
+
if (ip) {
|
|
89
|
+
body.set("remoteip", ip);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const res = await fetch(
|
|
94
|
+
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
|
95
|
+
{
|
|
96
|
+
method: "POST",
|
|
97
|
+
body,
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const data = (await res.json()) as TurnstileResponse;
|
|
102
|
+
|
|
103
|
+
if (!data.success) {
|
|
104
|
+
this.log.debug("Turnstile verification failed", {
|
|
105
|
+
errorCodes: data["error-codes"],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return data.success;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw new AlephaError("Failed to verify Turnstile captcha token", {
|
|
112
|
+
cause: error,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface TurnstileResponse {
|
|
119
|
+
success: boolean;
|
|
120
|
+
"error-codes"?: string[];
|
|
121
|
+
challenge_ts?: string;
|
|
122
|
+
hostname?: string;
|
|
123
|
+
action?: string;
|
|
124
|
+
cdata?: string;
|
|
125
|
+
}
|
|
@@ -207,6 +207,63 @@ export const buildOptions = $atom({
|
|
|
207
207
|
}),
|
|
208
208
|
),
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* PWA (Progressive Web App) configuration.
|
|
212
|
+
*
|
|
213
|
+
* Generates a web app manifest and enables installability.
|
|
214
|
+
* Requires a client-side bundle (React).
|
|
215
|
+
*/
|
|
216
|
+
pwa: t.optional(
|
|
217
|
+
t.object({
|
|
218
|
+
/**
|
|
219
|
+
* Full application name displayed on the splash screen
|
|
220
|
+
* and in the OS app switcher.
|
|
221
|
+
*/
|
|
222
|
+
name: t.string(),
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Short name displayed on the home screen icon.
|
|
226
|
+
* Falls back to `name` if omitted.
|
|
227
|
+
*/
|
|
228
|
+
shortName: t.optional(t.string()),
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Theme color used for the browser toolbar and OS chrome.
|
|
232
|
+
*
|
|
233
|
+
* @default "#ffffff"
|
|
234
|
+
*/
|
|
235
|
+
themeColor: t.optional(t.string()),
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Background color for the splash screen.
|
|
239
|
+
*
|
|
240
|
+
* @default "#ffffff"
|
|
241
|
+
*/
|
|
242
|
+
backgroundColor: t.optional(t.string()),
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Display mode for the installed PWA.
|
|
246
|
+
*
|
|
247
|
+
* - `standalone` - Looks like a native app (default)
|
|
248
|
+
* - `fullscreen` - Uses entire screen (games, immersive)
|
|
249
|
+
* - `minimal-ui` - Like standalone with minimal browser UI
|
|
250
|
+
* - `browser` - Standard browser tab
|
|
251
|
+
*
|
|
252
|
+
* @default "standalone"
|
|
253
|
+
*/
|
|
254
|
+
display: t.optional(
|
|
255
|
+
t.enum(["standalone", "fullscreen", "minimal-ui", "browser"]),
|
|
256
|
+
),
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Enable offline support via service worker.
|
|
260
|
+
*
|
|
261
|
+
* TODO: Not yet implemented.
|
|
262
|
+
*/
|
|
263
|
+
offline: t.optional(t.boolean()),
|
|
264
|
+
}),
|
|
265
|
+
),
|
|
266
|
+
|
|
210
267
|
/**
|
|
211
268
|
* Sitemap generation configuration.
|
|
212
269
|
*/
|
|
@@ -17,6 +17,7 @@ import { BuildCloudflareTask } from "../tasks/BuildCloudflareTask.ts";
|
|
|
17
17
|
import { BuildCompressTask } from "../tasks/BuildCompressTask.ts";
|
|
18
18
|
import { BuildDockerTask } from "../tasks/BuildDockerTask.ts";
|
|
19
19
|
import { BuildPrerenderTask } from "../tasks/BuildPrerenderTask.ts";
|
|
20
|
+
import { BuildPwaTask } from "../tasks/BuildPwaTask.ts";
|
|
20
21
|
import { BuildServerTask } from "../tasks/BuildServerTask.ts";
|
|
21
22
|
import { BuildSitemapTask } from "../tasks/BuildSitemapTask.ts";
|
|
22
23
|
import { BuildStaticTask } from "../tasks/BuildStaticTask.ts";
|
|
@@ -43,6 +44,7 @@ export class BuildCommand {
|
|
|
43
44
|
$inject(BuildServerTask),
|
|
44
45
|
$inject(BuildAssetsTask),
|
|
45
46
|
$inject(BuildSitemapTask),
|
|
47
|
+
$inject(BuildPwaTask),
|
|
46
48
|
$inject(BuildPrerenderTask),
|
|
47
49
|
$inject(BuildVercelTask),
|
|
48
50
|
$inject(BuildCloudflareTask),
|
|
@@ -594,7 +594,7 @@ if (import.meta.hot) {
|
|
|
594
594
|
</script>`);
|
|
595
595
|
|
|
596
596
|
if (style) {
|
|
597
|
-
tags.push(`<
|
|
597
|
+
tags.push(`<script type="module">import "/${style}";</script>`);
|
|
598
598
|
}
|
|
599
599
|
if (browser) {
|
|
600
600
|
tags.push(`<script type="module" src="/${browser}"></script>`);
|
|
@@ -359,16 +359,19 @@ export class ViteUtils {
|
|
|
359
359
|
// HTML template
|
|
360
360
|
// ---------------------------------------------------------------------------------------------------------------
|
|
361
361
|
|
|
362
|
-
public generateIndexHtml(entry: AppEntry): string {
|
|
362
|
+
public generateIndexHtml(entry: AppEntry, opts?: { pwa?: boolean }): string {
|
|
363
363
|
const style = entry.style;
|
|
364
364
|
const browser = entry.browser ?? entry.server;
|
|
365
|
+
const manifestLink = opts?.pwa
|
|
366
|
+
? '\n<link rel="manifest" href="/manifest.webmanifest" />'
|
|
367
|
+
: "";
|
|
365
368
|
return `
|
|
366
369
|
<!DOCTYPE html>
|
|
367
370
|
<html lang="en">
|
|
368
371
|
<head>
|
|
369
372
|
<meta charset="UTF-8" />
|
|
370
373
|
<title>App</title>
|
|
371
|
-
<meta name="viewport" content="width=device-width, initial-scale=1"
|
|
374
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>${manifestLink}
|
|
372
375
|
${style ? `<link rel="stylesheet" href="/${style}" />` : ""}
|
|
373
376
|
</head>
|
|
374
377
|
<body>
|
|
@@ -29,7 +29,9 @@ export class BuildClientTask extends BuildTask {
|
|
|
29
29
|
const isCI = this.alepha.isCI();
|
|
30
30
|
|
|
31
31
|
// Write index.html template for Vite to consume
|
|
32
|
-
const template = this.viteUtils.generateIndexHtml(ctx.entry
|
|
32
|
+
const template = this.viteUtils.generateIndexHtml(ctx.entry, {
|
|
33
|
+
pwa: !!ctx.options.pwa,
|
|
34
|
+
});
|
|
33
35
|
await this.fs.mkdir(this.fs.join(ctx.root, "node_modules/.alepha"));
|
|
34
36
|
const indexHtmlPath = this.fs.join(
|
|
35
37
|
ctx.root,
|
|
@@ -141,11 +141,13 @@ export class BuildCloudflareTask extends BuildTask {
|
|
|
141
141
|
|
|
142
142
|
const [dbName, id] = url.replace("d1://", "").replace("d1:", "").split(":");
|
|
143
143
|
const binding = BuildCloudflareTask.D1_BINDING;
|
|
144
|
+
const jurisdiction = process.env.CLOUDFLARE_JURISDICTION;
|
|
144
145
|
wrangler.d1_databases = wrangler.d1_databases || [];
|
|
145
146
|
wrangler.d1_databases.push({
|
|
146
147
|
binding,
|
|
147
148
|
database_name: dbName,
|
|
148
149
|
database_id: id,
|
|
150
|
+
...(jurisdiction ? { jurisdiction } : {}),
|
|
149
151
|
});
|
|
150
152
|
wrangler.vars ??= {};
|
|
151
153
|
wrangler.vars.DATABASE_URL = `d1://${binding}`;
|
|
@@ -177,10 +179,12 @@ export class BuildCloudflareTask extends BuildTask {
|
|
|
177
179
|
return;
|
|
178
180
|
}
|
|
179
181
|
|
|
182
|
+
const jurisdiction = process.env.CLOUDFLARE_JURISDICTION;
|
|
180
183
|
wrangler.r2_buckets = wrangler.r2_buckets || [];
|
|
181
184
|
wrangler.r2_buckets.push({
|
|
182
185
|
binding: bucketName,
|
|
183
186
|
bucket_name: bucketName,
|
|
187
|
+
...(jurisdiction ? { jurisdiction } : {}),
|
|
184
188
|
});
|
|
185
189
|
wrangler.vars ??= {};
|
|
186
190
|
wrangler.vars.R2_BUCKET_NAME = bucketName;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { $inject } from "alepha";
|
|
2
|
+
import { FileSystemProvider } from "alepha/system";
|
|
3
|
+
import { BuildTask, type BuildTaskContext } from "./BuildTask.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate PWA web app manifest.
|
|
7
|
+
*
|
|
8
|
+
* Produces a `manifest.webmanifest` in the public output directory
|
|
9
|
+
* from the `pwa` section of build options. Detects icons from `public/`.
|
|
10
|
+
*/
|
|
11
|
+
export class BuildPwaTask extends BuildTask {
|
|
12
|
+
protected readonly fs = $inject(FileSystemProvider);
|
|
13
|
+
|
|
14
|
+
async run(ctx: BuildTaskContext): Promise<void> {
|
|
15
|
+
const pwa = ctx.options.pwa;
|
|
16
|
+
if (!pwa || !ctx.hasClient) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const distDir = ctx.options.output?.dist ?? "dist";
|
|
21
|
+
const publicDir = ctx.options.output?.public ?? "public";
|
|
22
|
+
const outputDir = this.fs.join(ctx.root, distDir, publicDir);
|
|
23
|
+
|
|
24
|
+
await ctx.run({
|
|
25
|
+
name: "generate pwa manifest",
|
|
26
|
+
handler: async () => {
|
|
27
|
+
const icons = await this.detectIcons(outputDir);
|
|
28
|
+
|
|
29
|
+
const manifest: Record<string, unknown> = {
|
|
30
|
+
name: pwa.name,
|
|
31
|
+
short_name: pwa.shortName ?? pwa.name,
|
|
32
|
+
start_url: "/",
|
|
33
|
+
display: pwa.display ?? "standalone",
|
|
34
|
+
theme_color: pwa.themeColor ?? "#ffffff",
|
|
35
|
+
background_color: pwa.backgroundColor ?? "#ffffff",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (icons.length > 0) {
|
|
39
|
+
manifest.icons = icons;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const output = this.fs.join(outputDir, "manifest.webmanifest");
|
|
43
|
+
await this.fs.writeFile(output, JSON.stringify(manifest, null, 2));
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Detect icon files in the public output directory.
|
|
50
|
+
*
|
|
51
|
+
* Looks for common icon filenames and generates
|
|
52
|
+
* manifest icon entries with appropriate sizes and types.
|
|
53
|
+
*/
|
|
54
|
+
protected async detectIcons(
|
|
55
|
+
publicDir: string,
|
|
56
|
+
): Promise<Array<{ src: string; sizes: string; type: string }>> {
|
|
57
|
+
const icons: Array<{ src: string; sizes: string; type: string }> = [];
|
|
58
|
+
|
|
59
|
+
const candidates: Array<{
|
|
60
|
+
file: string;
|
|
61
|
+
sizes: string;
|
|
62
|
+
type: string;
|
|
63
|
+
}> = [
|
|
64
|
+
{ file: "icon-192.png", sizes: "192x192", type: "image/png" },
|
|
65
|
+
{ file: "icon-512.png", sizes: "512x512", type: "image/png" },
|
|
66
|
+
{ file: "icon.svg", sizes: "any", type: "image/svg+xml" },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
for (const candidate of candidates) {
|
|
70
|
+
if (await this.fs.exists(this.fs.join(publicDir, candidate.file))) {
|
|
71
|
+
icons.push({
|
|
72
|
+
src: `/${candidate.file}`,
|
|
73
|
+
sizes: candidate.sizes,
|
|
74
|
+
type: candidate.type,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return icons;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -24,23 +24,8 @@ export const webAppRouterTs = (options: {
|
|
|
24
24
|
imports.push(
|
|
25
25
|
'import { AdminSessionRouter } from "@alepha/ui/admin-sessions";',
|
|
26
26
|
);
|
|
27
|
-
imports.push('import { AdminAuditRouter } from "@alepha/ui/admin-audits";');
|
|
28
|
-
imports.push('import { AdminFileRouter } from "@alepha/ui/admin-files";');
|
|
29
|
-
imports.push(
|
|
30
|
-
'import { AdminParameterRouter } from "@alepha/ui/admin-parameters";',
|
|
31
|
-
);
|
|
32
|
-
imports.push('import { AdminJobRouter } from "@alepha/ui/admin-jobs";');
|
|
33
|
-
imports.push('import { AdminApiKeyRouter } from "@alepha/ui/admin-keys";');
|
|
34
|
-
imports.push(
|
|
35
|
-
'import { AdminNotificationRouter } from "@alepha/ui/admin-notifications";',
|
|
36
|
-
);
|
|
37
|
-
imports.push(
|
|
38
|
-
'import { AdminBillingRouter } from "@alepha/ui/admin-billing";',
|
|
39
|
-
);
|
|
40
27
|
imports.push('import { $inject } from "alepha";');
|
|
41
|
-
imports.push(
|
|
42
|
-
'import { IconLayoutDashboard, IconLockPassword, IconCreditCard } from "@tabler/icons-react";',
|
|
43
|
-
);
|
|
28
|
+
imports.push('import { IconLayoutDashboard } from "@tabler/icons-react";');
|
|
44
29
|
}
|
|
45
30
|
|
|
46
31
|
// Page import
|
|
@@ -67,61 +52,23 @@ export const webAppRouterTs = (options: {
|
|
|
67
52
|
classMembers.push(` // ── Admin Domain Routers ──────────────────────────
|
|
68
53
|
protected users = $inject(AdminUserRouter);
|
|
69
54
|
protected sessions = $inject(AdminSessionRouter);
|
|
70
|
-
protected audits = $inject(AdminAuditRouter);
|
|
71
|
-
protected files = $inject(AdminFileRouter);
|
|
72
|
-
protected parameters = $inject(AdminParameterRouter);
|
|
73
|
-
protected jobs = $inject(AdminJobRouter);
|
|
74
|
-
protected apiKeys = $inject(AdminApiKeyRouter);
|
|
75
|
-
protected notifications = $inject(AdminNotificationRouter);
|
|
76
|
-
protected billing = $inject(AdminBillingRouter);
|
|
77
55
|
|
|
78
56
|
// ── Admin Panel ─────────────────────────────────
|
|
79
57
|
admin = $uiAdmin({
|
|
80
58
|
pages: [
|
|
81
59
|
this.users.adminUsers,
|
|
60
|
+
this.users.adminUserLayout,
|
|
82
61
|
this.sessions.adminSessions,
|
|
83
|
-
this.audits.adminAudits,
|
|
84
|
-
this.files.adminFiles,
|
|
85
|
-
this.parameters.adminParameters,
|
|
86
|
-
this.jobs.adminJobs,
|
|
87
|
-
this.apiKeys.adminApiKeys,
|
|
88
|
-
this.notifications.adminNotifications,
|
|
89
|
-
this.billing.adminBilling,
|
|
90
62
|
],
|
|
91
63
|
sidebarItems: [
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
children: [
|
|
95
|
-
{ label: "Identity", icon: IconLockPassword, children: [
|
|
96
|
-
this.users.adminUsers,
|
|
97
|
-
this.sessions.adminSessions,
|
|
98
|
-
this.apiKeys.adminApiKeys,
|
|
99
|
-
]},
|
|
100
|
-
this.audits.adminAudits,
|
|
101
|
-
],
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
label: "System",
|
|
105
|
-
children: [
|
|
106
|
-
this.files.adminFiles,
|
|
107
|
-
this.jobs.adminJobs,
|
|
108
|
-
this.notifications.adminNotifications,
|
|
109
|
-
this.parameters.adminParameters,
|
|
110
|
-
],
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
label: "Commerce",
|
|
114
|
-
icon: IconCreditCard,
|
|
115
|
-
children: [
|
|
116
|
-
this.billing.adminBilling,
|
|
117
|
-
],
|
|
118
|
-
},
|
|
64
|
+
this.users.adminUsers,
|
|
65
|
+
this.sessions.adminSessions,
|
|
119
66
|
],
|
|
120
67
|
});
|
|
121
68
|
|
|
122
69
|
// ── Admin Dashboard ─────────────────────────────
|
|
123
70
|
adminDashboard = $page({
|
|
124
|
-
parent: this.admin
|
|
71
|
+
parent: this.admin,
|
|
125
72
|
path: "/",
|
|
126
73
|
label: "Dashboard",
|
|
127
74
|
icon: IconLayoutDashboard,
|
|
@@ -48,6 +48,17 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
48
48
|
return !!dbUrl?.startsWith("postgres:");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Propagate the environment's data-jurisdiction setting to the API client.
|
|
53
|
+
*
|
|
54
|
+
* Must be invoked at the top of every entry point (authenticate, build,
|
|
55
|
+
* deploy, secrets, provision, migrate, inspect, teardown) because
|
|
56
|
+
* CloudflareApi is a singleton reused across env invocations.
|
|
57
|
+
*/
|
|
58
|
+
protected configureApi(ctx: PlatformContext): void {
|
|
59
|
+
this.api.setJurisdiction(ctx.envConfig.jurisdiction);
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
protected async runShell(
|
|
52
63
|
command: string,
|
|
53
64
|
options: Parameters<ShellProvider["run"]>[1] = {},
|
|
@@ -70,6 +81,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
70
81
|
// -------------------------------------------------------------------------
|
|
71
82
|
|
|
72
83
|
async authenticate(ctx: PlatformContext, run: RunnerMethod): Promise<void> {
|
|
84
|
+
this.configureApi(ctx);
|
|
73
85
|
await run({
|
|
74
86
|
name: "authenticate",
|
|
75
87
|
handler: async () => {
|
|
@@ -112,6 +124,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
112
124
|
// -------------------------------------------------------------------------
|
|
113
125
|
|
|
114
126
|
async build(ctx: AppContext, run: RunnerMethod): Promise<void> {
|
|
127
|
+
this.configureApi(ctx);
|
|
115
128
|
const appDir = ctx.app.path
|
|
116
129
|
? this.fs.join(ctx.root, ctx.app.path)
|
|
117
130
|
: ctx.root;
|
|
@@ -159,6 +172,10 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
159
172
|
env.CLOUDFLARE_DOMAIN = ctx.envConfig.domain;
|
|
160
173
|
}
|
|
161
174
|
|
|
175
|
+
if (ctx.envConfig.jurisdiction) {
|
|
176
|
+
env.CLOUDFLARE_JURISDICTION = ctx.envConfig.jurisdiction;
|
|
177
|
+
}
|
|
178
|
+
|
|
162
179
|
await run({
|
|
163
180
|
name: "alepha build -t cloudflare",
|
|
164
181
|
handler: async () => {
|
|
@@ -178,6 +195,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
178
195
|
ctx: AppContext,
|
|
179
196
|
run: RunnerMethod,
|
|
180
197
|
): Promise<string | undefined> {
|
|
198
|
+
this.configureApi(ctx);
|
|
181
199
|
const workerName = ctx.naming.worker(
|
|
182
200
|
ctx.apps.length > 1 ? ctx.app.name : undefined,
|
|
183
201
|
);
|
|
@@ -212,6 +230,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
212
230
|
"DATABASE_URL",
|
|
213
231
|
"R2_BUCKET_NAME",
|
|
214
232
|
"CLOUDFLARE_DOMAIN",
|
|
233
|
+
"CLOUDFLARE_JURISDICTION",
|
|
215
234
|
"HYPERDRIVE_ID",
|
|
216
235
|
"POSTGRES_SCHEMA",
|
|
217
236
|
"NODE_ENV",
|
|
@@ -221,6 +240,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
221
240
|
ctx: PlatformContext,
|
|
222
241
|
run: RunnerMethod,
|
|
223
242
|
): Promise<void> {
|
|
243
|
+
this.configureApi(ctx);
|
|
224
244
|
const envVars = await this.envUtils.parseEnv(ctx.root, [`.env.${ctx.env}`]);
|
|
225
245
|
|
|
226
246
|
// Filter out binding/build vars, VITE_* vars, and empty values
|
|
@@ -265,6 +285,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
265
285
|
ctx: PlatformContext,
|
|
266
286
|
run: RunnerMethod,
|
|
267
287
|
): Promise<void> {
|
|
288
|
+
this.configureApi(ctx);
|
|
268
289
|
const needsDB = ctx.apps.some((a) => a.resources.hasDatabase);
|
|
269
290
|
const needsBucket = ctx.apps.some((a) => a.resources.hasBucket);
|
|
270
291
|
const postgres = needsDB && (await this.isPostgres(ctx));
|
|
@@ -345,6 +366,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
345
366
|
ctx: PlatformContext,
|
|
346
367
|
run: RunnerMethod,
|
|
347
368
|
): Promise<void> {
|
|
369
|
+
this.configureApi(ctx);
|
|
348
370
|
const needsDB = ctx.apps.some((a) => a.resources.hasDatabase);
|
|
349
371
|
if (!needsDB) {
|
|
350
372
|
return;
|
|
@@ -431,6 +453,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
431
453
|
ctx: PlatformContext,
|
|
432
454
|
run: RunnerMethod,
|
|
433
455
|
): Promise<PlatformState> {
|
|
456
|
+
this.configureApi(ctx);
|
|
434
457
|
const state: PlatformState = {
|
|
435
458
|
workers: [],
|
|
436
459
|
databases: [],
|
|
@@ -610,6 +633,7 @@ export class CloudflareAdapter extends PlatformAdapter {
|
|
|
610
633
|
// -------------------------------------------------------------------------
|
|
611
634
|
|
|
612
635
|
async teardown(ctx: PlatformContext, run: RunnerMethod): Promise<void> {
|
|
636
|
+
this.configureApi(ctx);
|
|
613
637
|
// 1. Remove queue consumers (must happen before worker or queue deletion)
|
|
614
638
|
for (const app of ctx.apps) {
|
|
615
639
|
if (app.resources.hasQueue) {
|
|
@@ -53,11 +53,27 @@ export const platformOptions = $atom({
|
|
|
53
53
|
* Named environments with their adapter and configuration.
|
|
54
54
|
*/
|
|
55
55
|
environments: t.record(
|
|
56
|
-
t.text(
|
|
56
|
+
t.text({
|
|
57
|
+
description:
|
|
58
|
+
"Environment name (e.g. 'production', 'staging', 'preview'). Used in resource naming and selected via --env.",
|
|
59
|
+
}),
|
|
57
60
|
t.object({
|
|
58
61
|
adapter: t.enum(["cloudflare", "vercel"]),
|
|
62
|
+
/**
|
|
63
|
+
* Custom domain for the deployed worker (e.g. "api.example.com").
|
|
64
|
+
*
|
|
65
|
+
* On Cloudflare this is attached as a custom-domain route.
|
|
66
|
+
* Omit to use the adapter's default `*.workers.dev` / preview URL.
|
|
67
|
+
*/
|
|
59
68
|
domain: t.optional(t.text()),
|
|
60
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Cloudflare data jurisdiction for R2 buckets and D1 databases.
|
|
71
|
+
* - "eu": data stays within the EU
|
|
72
|
+
* - "fedramp": FedRAMP-authorized regions
|
|
73
|
+
*
|
|
74
|
+
* Omit for the default (global) jurisdiction.
|
|
75
|
+
*/
|
|
76
|
+
jurisdiction: t.optional(t.enum(["eu", "fedramp"])),
|
|
61
77
|
}),
|
|
62
78
|
),
|
|
63
79
|
}),
|
|
@@ -75,6 +91,6 @@ export type PlatformOptions = Static<typeof platformOptions.schema>;
|
|
|
75
91
|
export interface EnvironmentConfig {
|
|
76
92
|
adapter: "cloudflare" | "vercel";
|
|
77
93
|
domain?: string;
|
|
78
|
-
domains?: Record<string, string>;
|
|
79
94
|
vars?: Record<string, string>;
|
|
95
|
+
jurisdiction?: "eu" | "fedramp";
|
|
80
96
|
}
|