pub struct Waitlist { /* private fields */ }Expand description
A per-product waitlist.
Implementations§
Source§impl Waitlist
impl Waitlist
Sourcepub fn new() -> Self
pub fn new() -> Self
No products allowed until Waitlist::products or
Waitlist::any_product says otherwise; 7-day confirm TTL;
referrals on.
Sourcepub fn products(
self,
products: impl IntoIterator<Item = impl Into<String>>,
) -> Self
pub fn products( self, products: impl IntoIterator<Item = impl Into<String>>, ) -> Self
Exactly these product slugs may be joined.
Sourcepub fn any_product(self) -> Self
pub fn any_product(self) -> Self
Accept any product slug (single-product ventures that do not want an allowlist).
Sourcepub fn confirm_ttl_days(self, days: u32) -> Self
pub fn confirm_ttl_days(self, days: u32) -> Self
How long a confirmation link stays valid (default 7 days).
Sourcepub fn retention_days_pending(self, days: u32) -> Self
pub fn retention_days_pending(self, days: u32) -> Self
Purge pending entries untouched for this many days from the
scheduled handler (default 30; issue #13 retention rule).
Sourcepub fn status_redirect(self, url: impl Into<String>) -> Self
pub fn status_redirect(self, url: impl Into<String>) -> Self
Where confirmation redirects to, as
<url>?token=<status token> (default
<public_url>/waitlist/status).
Sourcepub fn referrals(self, enabled: bool) -> Self
pub fn referrals(self, enabled: bool) -> Self
Whether ref codes credit referrers (default true).
Sourcepub fn answers_schema(
self,
validate: impl Fn(&Value) -> Result<(), String> + Send + Sync + 'static,
) -> Self
pub fn answers_schema( self, validate: impl Fn(&Value) -> Result<(), String> + Send + Sync + 'static, ) -> Self
Validates the free-form answers JSON of a join (default: accept
anything). Only runs when the join carries answers; pair with
Self::require_answers to also reject joins without them.
Sourcepub fn require_answers(self, required: bool) -> Self
pub fn require_answers(self, required: bool) -> Self
Whether a join must carry answers (default false): with
true, a body without the field — absent or null — is
rejected as validation-failed.
Trait Implementations§
Source§impl Module for Waitlist
impl Module for Waitlist
Source§fn personal_data(&self) -> &'static [PersonalDataSet]
fn personal_data(&self) -> &'static [PersonalDataSet]
The entry is anonymised, not deleted; the other two tables have no subject column at all (issue #265).
Why Anonymise and not Erase. A confirmed entry carries two
numbers that belong to other people as much as to its owner:
position is a dense per-product join order that is deliberately
never recomputed, and referrals is a credit already granted. Delete
the row and the queue acquires a hole nobody can explain and every
referred_by pointing at its code orphans; keep it and null the
person out of it, and the request is answered without rewriting
anybody else’s place in the line. Migration 0005 exists for exactly
this: email and email_normalized were NOT NULL, so the SET … = NULL this declaration promises could not have run.
referral_code is deliberately kept. It is a random 8-character
string that names nobody once the address is gone, and it is what the
referrals other people were credited for still resolve against.
Why id and not the address. GET /v1/privacy/export?subject=…
puts the subject value in a URL, and URLs outlive their request in
access logs, proxies and browser history — which is why this module’s
own links carry the opaque row id and never the address (issue #135).
Declaring email_normalized as the subject would have made a subject
access request the one operation that writes an address into those
logs.
Why the other two have no subject column. waitlist_position_lock
is one row per product, genuinely names nobody, and is none.
waitlist_send_cooldown is the harder case, declared unreachable,
and its reason says so plainly: its primary key is the
address and product the mail throttle counts against, so the address
is inside a key rather than in a column of its own, and
… WHERE <column> = ? cannot reach it. That is the same shape issue
#266 describes for Outbox, and the table is core’s
SendCooldown shape rather than this
module’s, so giving it a column of its own is a change to every module
that has one. Saying so is the honest answer available here; inventing
a subject column that matched nothing would not be.
Source§fn requires(&self) -> &'static [Port]
fn requires(&self) -> &'static [Port]
Source§fn tables(&self) -> &'static [&'static str]
fn tables(&self) -> &'static [&'static str]
Source§fn emits(&self) -> &'static [&'static str]
fn emits(&self) -> &'static [&'static str]
"<module>.<event>"), listed by
/__health.Source§fn public_writes(&self) -> bool
fn public_writes(&self) -> bool
Source§fn migrations(&self) -> Migrations
fn migrations(&self) -> Migrations
Source§fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>
fn validate_config(&self, cfg: &dyn Config) -> Result<(), ConfigError>
Source§fn router(&self, ctx: ModuleContext) -> Router
fn router(&self, ctx: ModuleContext) -> Router
/v1/<name>.Source§fn surface(&self) -> Surface
fn surface(&self) -> Surface
Action::input::<Body>()), so the
declaration cannot drift from the route. Harness::build
validates it; GET /__surface serves the composition. Default:
nothing, and a module that declares nothing renders nothing.Source§fn scheduled<'a>(
&'a self,
ctx: &'a ModuleContext,
cron: &'a str,
) -> BoxFuture<'a, Result<(), AnyError>>
fn scheduled<'a>( &'a self, ctx: &'a ModuleContext, cron: &'a str, ) -> BoxFuture<'a, Result<(), AnyError>>
cron is the trigger expression). Default: none.Source§fn harness_api(&self) -> u32
fn harness_api(&self) -> u32
Harness::build.Source§fn depends_on(&self) -> &'static [&'static str]
fn depends_on(&self) -> &'static [&'static str]
Source§fn actor_kinds(&self) -> &'static [&'static str]
fn actor_kinds(&self) -> &'static [&'static str]
[a-z0-9_-]; duplicates across
modules are a build error, and so is a kind with no handler registered
through
HarnessBuilder::actor. Read moreSource§fn public_write_policy(&self) -> RoutePolicy
fn public_write_policy(&self) -> RoutePolicy
RoutePolicy on
(issue #143). Read moreSource§fn signature_verification(&self) -> SignatureVerification
fn signature_verification(&self) -> SignatureVerification
RoutePolicy::Signature
deliveries (issue #533): how a webhook handler checks a machine
signature before acting. Read moreSource§fn max_body_bytes(&self, _cfg: &dyn Config) -> usize
fn max_body_bytes(&self, _cfg: &dyn Config) -> usize
Source§fn streaming_routes(&self) -> &'static [StreamRoute]
fn streaming_routes(&self) -> &'static [StreamRoute]
StreamRoute::max_bytes ceiling. Opt-in: the default
is empty, and every existing route keeps the buffered behaviour it
always had. Read moreSource§fn max_blob_object_bytes(&self) -> u64
fn max_blob_object_bytes(&self) -> u64
ScopedBlob the harness hands the module on
put_stream, upload_part and complete_multipart. Read moreSource§fn self_check(&self) -> Vec<String>
fn self_check(&self) -> Vec<String>
Source§fn well_known(&self) -> Option<Router>
fn well_known(&self) -> Option<Router>
/.well-known, for
spec-mandated discovery documents (OIDC openid-configuration,
jwks.json) that must live outside /v1 (issue #46). Paths are
relative to the prefix: register /jwks.json, not
/.well-known/jwks.json. Read more