Documentation
¶
Overview ¶
Package cloud is the self-serve cloud/SaaS PREPARATION skeleton (v0.7 "Kimlik" E3 — docs/CLOUD.md).
Everything here is master-gated by FILEX_CLOUD (config.CloudConfig.Enabled, default OFF). While the flag is off the package is never constructed: api.BuildRouter skips the /api/cloud route block entirely, capabilities carry no cloud field, and the migration-00021 provider columns (plan / limits_json / billing_ref) stay NULL. Turning the flag on only makes sense together with FILEX_MULTI_TENANT=1 — a signed-up "tenant" IS a provider row from the v0.1.61 native multi-tenancy foundation, provisioned through the exact same db.Store.CreateProvider primitive the /api/admin/providers lifecycle API uses (no parallel provisioning path exists).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalid — malformed signup input (→ 400). ErrInvalid = errors.New("cloud: invalid input") // ErrConflict — tenant slug already taken (→ 409). ErrConflict = errors.New("cloud: conflict") // ErrTokenUnknown — verification token unknown or expired (→ 404). ErrTokenUnknown = errors.New("cloud: unknown or expired verification token") )
Sentinel error kinds the handler maps to HTTP statuses.
var ErrStripeNotConfigured = errors.New("cloud: stripe not configured")
ErrStripeNotConfigured — no STRIPE_SECRET; the billing endpoints answer 503.
Functions ¶
func VerifyWebhookSignature ¶
VerifyWebhookSignature drafts the Stripe-Signature check. SKELETON — always rejects until implemented so an accidentally exposed webhook can never be spoofed into doing something.
TODO(cloud-launch): parse `t=…,v1=…`, HMAC-SHA256 over "<t>.<payload>" with the endpoint's signing secret (whsec_…), constant-time compare + 5-minute tolerance window.
Types ¶
type Plan ¶
type Plan struct {
// ID is the stable plan key stored in providers.plan (e.g. "free", "pro").
ID string `json:"id"`
// Name is the display name.
Name string `json:"name"`
// PriceMonthly is a display string (e.g. "9.90 EUR"); "" = free.
PriceMonthly string `json:"price_monthly,omitempty"`
// StripePriceID links the plan to a Stripe Price for checkout sessions.
// Empty on free plans / while billing is not wired.
StripePriceID string `json:"stripe_price_id,omitempty"`
// Limits are the plan's resource ceilings.
Limits PlanLimits `json:"limits"`
}
Plan is one entry of the config-driven catalog (FILEX_CLOUD_PLANS).
func DefaultPlans ¶
func DefaultPlans() []Plan
DefaultPlans is the built-in catalog used when FILEX_CLOUD_PLANS is empty: a single free skeleton plan so the signup flow is exercisable without any configuration.
func ParsePlans ¶
ParsePlans decodes the FILEX_CLOUD_PLANS JSON array. An empty input returns DefaultPlans(). Errors (bad JSON, missing/duplicate ids) are returned so the caller can surface them; callers that must keep booting fall back to DefaultPlans() and report the error via /api/cloud/status.
type PlanLimits ¶
type PlanLimits struct {
// StorageBytes caps the tenant's total storage. 0 = unlimited.
StorageBytes int64 `json:"storage_bytes,omitempty"`
// MaxUsers caps the tenant's user count. 0 = unlimited.
MaxUsers int `json:"max_users,omitempty"`
}
PlanLimits are the resource ceilings a plan grants a tenant. The zero value means "unlimited" for that dimension. Stamped as a JSON snapshot into providers.limits_json at signup so later catalog edits don't silently change an existing tenant's entitlement.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service implements the self-signup skeleton. Constructed ONLY when FILEX_CLOUD=1 (api.BuildRouter); nothing else references it.
func New ¶
New builds the Service from the runtime config pieces. An invalid FILEX_CLOUD_PLANS does not abort boot: the service falls back to DefaultPlans() and reports the parse error through Status().
func (*Service) PlansErr ¶
PlansErr returns the remembered FILEX_CLOUD_PLANS parse error ("" when the catalog loaded cleanly).
func (*Service) Signup ¶
func (s *Service) Signup(ctx context.Context, req SignupRequest) (*SignupResult, error)
Signup validates the request and provisions the tenant DISABLED, pending e-mail verification. Provisioning reuses the exact primitive the /api/admin/providers lifecycle API uses (db.Store.CreateProvider) — no parallel provisioning path — then stamps the plan snapshot into the migration-00021 columns. Storage linking is deliberately NOT part of the skeleton (see docs/CLOUD.md runbook).
type SignupRequest ¶
type SignupRequest struct {
// Email of the prospective tenant owner (receives the verification mail).
Email string `json:"email"`
// Slug is the requested tenant identifier (DNS-label rules).
Slug string `json:"slug"`
// Name is the display name; defaults to the slug.
Name string `json:"name,omitempty"`
// Plan is a plan id from the catalog; defaults to the first plan.
Plan string `json:"plan,omitempty"`
// Locale is the language the verification mail is written in — the
// signing-up visitor's (the handler fills it from the request when the
// body does not say). Any language the server speaks; English otherwise.
Locale string `json:"locale,omitempty"`
}
SignupRequest is the POST /api/cloud/signup payload.
type SignupResult ¶
type SignupResult struct {
ProviderID int64 `json:"tenant_id"`
Slug string `json:"slug"`
Host string `json:"host,omitempty"`
Plan string `json:"plan"`
// MailSent — whether the verification mail went out through SMTP.
MailSent bool `json:"mail_sent"`
// VerifyToken is returned ONLY when the mail could not be sent (no/unverified
// SMTP) so a local/dev flow can complete verification — the same on-screen
// fallback pattern the share/invite mailer uses. Empty when mailed.
VerifyToken string `json:"verify_token,omitempty"`
}
SignupResult reports a provisioned (but not yet verified) tenant.
type StripeClient ¶
type StripeClient struct {
// Secret is the API secret key (sk_test_… / sk_live_…). Empty = not
// configured; every call returns ErrStripeNotConfigured.
Secret string
// HTTP is the transport; nil → a 15s-timeout default client.
HTTP *http.Client
}
StripeClient is a minimal stdlib-http Stripe caller.
func NewStripe ¶
func NewStripe(secret string) *StripeClient
NewStripe constructs the client (secret may be empty = not configured).
func (*StripeClient) Configured ¶
func (c *StripeClient) Configured() bool
Configured reports whether a secret key is present.
func (*StripeClient) CreateCheckoutSession ¶
func (c *StripeClient) CreateCheckoutSession(ctx context.Context, priceID, successURL, cancelURL string) (string, error)
CreateCheckoutSession drafts POST /v1/checkout/sessions and returns the hosted checkout URL. DRAFT — the request wiring is real but the parameter set is the bare minimum:
TODO(cloud-launch): pass customer_email + client_reference_id (tenant slug) TODO(cloud-launch): set subscription mode metadata for the webhook to map TODO(cloud-launch): idempotency key header (Idempotency-Key) TODO(cloud-launch): typed error decoding (Stripe error JSON envelope)