feat(v1.1.8): email verification flow (migration 0028 + SDK)
migration 0028: app_user_email_verifications table — token_hash PK,
app_id + user_id FKs cascading, expires_at, consumed_at. Single-use
via atomic UPDATE WHERE consumed_at IS NULL.
UsersServiceImpl gains:
* verifications: Arc<dyn AppUserVerificationRepo>
* email: Arc<dyn EmailService>
users::send_verification_email(user_id, opts) mints a 32-byte token,
stores SHA-256(token), and calls EmailService::send with the body
template's {link} placeholder substituted by link_base + ?token=raw.
EmailError::NotConfigured propagates as UsersError::EmailNotConfigured
so scripts already handling email-disabled mode (v1.1.7 email::send)
don't need new branches.
users::verify_email(token) atomically consumes the one-shot token via
the verifications repo, marks the user's email_verified_at = NOW(),
and emits a "users::email_verified" event for future triggers.
Internal email send uses a synthesized SdkCallCx with principal=None
so the email-service AppEmailSend authz check is skipped (the
users::* surface has already gated on AppUsersWrite — the internal
hop isn't the script's direct call). Documented in HANDBACK §7.
EmailTemplateOpts now requires `from` (the v1.1.7 email service needs
an envelope sender). The brief example omitted it; deviation logged
in HANDBACK §7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -104,9 +104,15 @@ pub struct GeneratedAccept {
|
||||
/// flows (`send_verification_email`, `request_password_reset`, `invite`).
|
||||
/// The script owns the body; PiCloud only injects `?token=<token>` into
|
||||
/// the `link_base`.
|
||||
///
|
||||
/// `from` is required because the underlying `EmailService::send` needs
|
||||
/// an envelope sender — the brief example omitted it for brevity, but
|
||||
/// the service can't synthesize a sensible default. Documented as a
|
||||
/// shape deviation in HANDBACK §7.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EmailTemplateOpts {
|
||||
pub link_base: String,
|
||||
pub from: String,
|
||||
pub subject: String,
|
||||
/// Body template with `{link}` substituted for the
|
||||
/// `link_base?token=<token>` URL. Plain text body — the v1.1.7
|
||||
|
||||
Reference in New Issue
Block a user