Documentation
¶
Overview ¶
Package adminrpc implements the moth.admin.v1 connect services.
Index ¶
- Constants
- func AdminFromContext(ctx context.Context) (store.Admin, bool)
- func ClearCookie(secure bool) *http.Cookie
- func IssueSession(ctx context.Context, st store.SessionStore, adminID string, secure bool) (*http.Cookie, error)
- func NewAuthInterceptor(st Store) connect.UnaryInterceptorFunc
- func NewID() string
- func PATProto(t store.PersonalAccessToken) *adminv1.PersonalAccessToken
- func SessionHashFromContext(ctx context.Context) (string, bool)
- func Slugify(name string) string
- type AccountHandler
- func (h *AccountHandler) AcceptAdminInvite(ctx context.Context, req *connect.Request[adminv1.AcceptAdminInviteRequest]) (*connect.Response[adminv1.AcceptAdminInviteResponse], error)
- func (h *AccountHandler) ChangePassword(ctx context.Context, req *connect.Request[adminv1.ChangePasswordRequest]) (*connect.Response[adminv1.ChangePasswordResponse], error)
- func (h *AccountHandler) CreatePersonalAccessToken(ctx context.Context, ...) (*connect.Response[adminv1.CreatePersonalAccessTokenResponse], error)
- func (h *AccountHandler) InviteAdmin(ctx context.Context, req *connect.Request[adminv1.InviteAdminRequest]) (*connect.Response[adminv1.InviteAdminResponse], error)
- func (h *AccountHandler) ListAdminInvites(ctx context.Context, _ *connect.Request[adminv1.ListAdminInvitesRequest]) (*connect.Response[adminv1.ListAdminInvitesResponse], error)
- func (h *AccountHandler) ListAdmins(ctx context.Context, _ *connect.Request[adminv1.ListAdminsRequest]) (*connect.Response[adminv1.ListAdminsResponse], error)
- func (h *AccountHandler) ListPersonalAccessTokens(ctx context.Context, ...) (*connect.Response[adminv1.ListPersonalAccessTokensResponse], error)
- func (h *AccountHandler) RevokeAdminInvite(ctx context.Context, req *connect.Request[adminv1.RevokeAdminInviteRequest]) (*connect.Response[adminv1.RevokeAdminInviteResponse], error)
- func (h *AccountHandler) RevokePersonalAccessToken(ctx context.Context, ...) (*connect.Response[adminv1.RevokePersonalAccessTokenResponse], error)
- type AnalyticsHandler
- func (h *AnalyticsHandler) GetStats(ctx context.Context, req *connect.Request[adminv1.GetStatsRequest]) (*connect.Response[adminv1.GetStatsResponse], error)
- func (h *AnalyticsHandler) GetSubscriptionStats(ctx context.Context, req *connect.Request[adminv1.GetSubscriptionStatsRequest]) (*connect.Response[adminv1.GetSubscriptionStatsResponse], error)
- func (h *AnalyticsHandler) ListRecentEvents(ctx context.Context, req *connect.Request[adminv1.ListRecentEventsRequest]) (*connect.Response[adminv1.ListRecentEventsResponse], error)
- func (h *AnalyticsHandler) RunRollup(ctx context.Context, req *connect.Request[adminv1.RunRollupRequest]) (*connect.Response[adminv1.RunRollupResponse], error)
- type AuditHandler
- type Auditor
- type BillingHandler
- func (h *BillingHandler) CreateEntitlement(ctx context.Context, req *connect.Request[adminv1.CreateEntitlementRequest]) (*connect.Response[adminv1.CreateEntitlementResponse], error)
- func (h *BillingHandler) CreateProduct(ctx context.Context, req *connect.Request[adminv1.CreateProductRequest]) (*connect.Response[adminv1.CreateProductResponse], error)
- func (h *BillingHandler) DeleteEntitlement(ctx context.Context, req *connect.Request[adminv1.DeleteEntitlementRequest]) (*connect.Response[adminv1.DeleteEntitlementResponse], error)
- func (h *BillingHandler) DeleteProduct(ctx context.Context, req *connect.Request[adminv1.DeleteProductRequest]) (*connect.Response[adminv1.DeleteProductResponse], error)
- func (h *BillingHandler) GetBillingCredentials(ctx context.Context, ...) (*connect.Response[adminv1.GetBillingCredentialsResponse], error)
- func (h *BillingHandler) GetProduct(ctx context.Context, req *connect.Request[adminv1.GetProductRequest]) (*connect.Response[adminv1.GetProductResponse], error)
- func (h *BillingHandler) GetUserSubscription(ctx context.Context, req *connect.Request[adminv1.GetUserSubscriptionRequest]) (*connect.Response[adminv1.GetUserSubscriptionResponse], error)
- func (h *BillingHandler) GrantEntitlement(ctx context.Context, req *connect.Request[adminv1.GrantEntitlementRequest]) (*connect.Response[adminv1.GrantEntitlementResponse], error)
- func (h *BillingHandler) ListEntitlements(ctx context.Context, req *connect.Request[adminv1.ListEntitlementsRequest]) (*connect.Response[adminv1.ListEntitlementsResponse], error)
- func (h *BillingHandler) ListProducts(ctx context.Context, req *connect.Request[adminv1.ListProductsRequest]) (*connect.Response[adminv1.ListProductsResponse], error)
- func (h *BillingHandler) ListUserSubscriptions(ctx context.Context, ...) (*connect.Response[adminv1.ListUserSubscriptionsResponse], error)
- func (h *BillingHandler) RevokeGrant(ctx context.Context, req *connect.Request[adminv1.RevokeGrantRequest]) (*connect.Response[adminv1.RevokeGrantResponse], error)
- func (h *BillingHandler) UpdateBillingCredentials(ctx context.Context, ...) (*connect.Response[adminv1.UpdateBillingCredentialsResponse], error)
- func (h *BillingHandler) UpdateEntitlement(ctx context.Context, req *connect.Request[adminv1.UpdateEntitlementRequest]) (*connect.Response[adminv1.UpdateEntitlementResponse], error)
- func (h *BillingHandler) UpdateProduct(ctx context.Context, req *connect.Request[adminv1.UpdateProductRequest]) (*connect.Response[adminv1.UpdateProductResponse], error)
- type CopyHandler
- func (h *CopyHandler) GetProjectCopy(ctx context.Context, req *connect.Request[adminv1.GetProjectCopyRequest]) (*connect.Response[adminv1.GetProjectCopyResponse], error)
- func (h *CopyHandler) ListCopyRevisions(ctx context.Context, req *connect.Request[adminv1.ListCopyRevisionsRequest]) (*connect.Response[adminv1.ListCopyRevisionsResponse], error)
- func (h *CopyHandler) ListLocales(ctx context.Context, req *connect.Request[adminv1.ListLocalesRequest]) (*connect.Response[adminv1.ListLocalesResponse], error)
- func (h *CopyHandler) ResetCopy(ctx context.Context, req *connect.Request[adminv1.ResetCopyRequest]) (*connect.Response[adminv1.ResetCopyResponse], error)
- func (h *CopyHandler) RestoreCopyRevision(ctx context.Context, req *connect.Request[adminv1.RestoreCopyRevisionRequest]) (*connect.Response[adminv1.RestoreCopyRevisionResponse], error)
- func (h *CopyHandler) UpdateProjectCopy(ctx context.Context, req *connect.Request[adminv1.UpdateProjectCopyRequest]) (*connect.Response[adminv1.UpdateProjectCopyResponse], error)
- type Credential
- type CredentialType
- type LiveStoreSyncer
- type MonetizationHandler
- func (h *MonetizationHandler) GetOffering(ctx context.Context, req *connect.Request[adminv1.GetOfferingRequest]) (*connect.Response[adminv1.GetOfferingResponse], error)
- func (h *MonetizationHandler) GetStoreCatalogStatus(ctx context.Context, ...) (*connect.Response[adminv1.GetStoreCatalogStatusResponse], error)
- func (h *MonetizationHandler) ReorderOffering(ctx context.Context, req *connect.Request[adminv1.ReorderOfferingRequest]) (*connect.Response[adminv1.ReorderOfferingResponse], error)
- func (h *MonetizationHandler) SyncStoreCatalog(ctx context.Context, req *connect.Request[adminv1.SyncStoreCatalogRequest]) (*connect.Response[adminv1.SyncStoreCatalogResponse], error)
- type PaywallHandler
- func (h *PaywallHandler) GetPaywallConfig(ctx context.Context, req *connect.Request[adminv1.GetPaywallConfigRequest]) (*connect.Response[adminv1.GetPaywallConfigResponse], error)
- func (h *PaywallHandler) ListPaywallRevisions(ctx context.Context, req *connect.Request[adminv1.ListPaywallRevisionsRequest]) (*connect.Response[adminv1.ListPaywallRevisionsResponse], error)
- func (h *PaywallHandler) ResetPaywall(ctx context.Context, req *connect.Request[adminv1.ResetPaywallRequest]) (*connect.Response[adminv1.ResetPaywallResponse], error)
- func (h *PaywallHandler) RestorePaywallRevision(ctx context.Context, ...) (*connect.Response[adminv1.RestorePaywallRevisionResponse], error)
- func (h *PaywallHandler) UpdatePaywallConfig(ctx context.Context, req *connect.Request[adminv1.UpdatePaywallConfigRequest]) (*connect.Response[adminv1.UpdatePaywallConfigResponse], error)
- type ProfileHandler
- func (h *ProfileHandler) GetProfile(ctx context.Context, req *connect.Request[adminv1.GetProfileRequest]) (*connect.Response[adminv1.GetProfileResponse], error)
- func (h *ProfileHandler) GetProjectSetupStatus(ctx context.Context, ...) (*connect.Response[adminv1.GetProjectSetupStatusResponse], error)
- func (h *ProfileHandler) UpdateProfile(ctx context.Context, req *connect.Request[adminv1.UpdateProfileRequest]) (*connect.Response[adminv1.UpdateProfileResponse], error)
- type ProjectHandler
- func (h *ProjectHandler) CreateProject(ctx context.Context, req *connect.Request[adminv1.CreateProjectRequest]) (*connect.Response[adminv1.CreateProjectResponse], error)
- func (h *ProjectHandler) DeleteProject(ctx context.Context, req *connect.Request[adminv1.DeleteProjectRequest]) (*connect.Response[adminv1.DeleteProjectResponse], error)
- func (h *ProjectHandler) ExportProject(ctx context.Context, req *connect.Request[adminv1.ExportProjectRequest]) (*connect.Response[adminv1.ExportProjectResponse], error)
- func (h *ProjectHandler) GetProject(ctx context.Context, req *connect.Request[adminv1.GetProjectRequest]) (*connect.Response[adminv1.GetProjectResponse], error)
- func (h *ProjectHandler) GetSigningKey(ctx context.Context, req *connect.Request[adminv1.GetSigningKeyRequest]) (*connect.Response[adminv1.GetSigningKeyResponse], error)
- func (h *ProjectHandler) ImportProject(ctx context.Context, req *connect.Request[adminv1.ImportProjectRequest]) (*connect.Response[adminv1.ImportProjectResponse], error)
- func (h *ProjectHandler) ListProjects(ctx context.Context, _ *connect.Request[adminv1.ListProjectsRequest]) (*connect.Response[adminv1.ListProjectsResponse], error)
- func (h *ProjectHandler) RegenerateSecretKey(ctx context.Context, req *connect.Request[adminv1.RegenerateSecretKeyRequest]) (*connect.Response[adminv1.RegenerateSecretKeyResponse], error)
- func (h *ProjectHandler) ResetSigningKey(ctx context.Context, req *connect.Request[adminv1.ResetSigningKeyRequest]) (*connect.Response[adminv1.ResetSigningKeyResponse], error)
- func (h *ProjectHandler) RotateSigningKey(ctx context.Context, req *connect.Request[adminv1.RotateSigningKeyRequest]) (*connect.Response[adminv1.RotateSigningKeyResponse], error)
- func (h *ProjectHandler) UpdateProject(ctx context.Context, req *connect.Request[adminv1.UpdateProjectRequest]) (*connect.Response[adminv1.UpdateProjectResponse], error)
- type PushHandler
- func (h *PushHandler) GetPushSettings(ctx context.Context, req *connect.Request[adminv1.GetPushSettingsRequest]) (*connect.Response[adminv1.GetPushSettingsResponse], error)
- func (h *PushHandler) ListPushDevices(ctx context.Context, req *connect.Request[adminv1.ListPushDevicesRequest]) (*connect.Response[adminv1.ListPushDevicesResponse], error)
- func (h *PushHandler) ListUserPushDevices(ctx context.Context, req *connect.Request[adminv1.ListUserPushDevicesRequest]) (*connect.Response[adminv1.ListUserPushDevicesResponse], error)
- func (h *PushHandler) RevokePushDevice(ctx context.Context, req *connect.Request[adminv1.RevokePushDeviceRequest]) (*connect.Response[adminv1.RevokePushDeviceResponse], error)
- func (h *PushHandler) UpdatePushSettings(ctx context.Context, req *connect.Request[adminv1.UpdatePushSettingsRequest]) (*connect.Response[adminv1.UpdatePushSettingsResponse], error)
- type SessionHandler
- func (h *SessionHandler) GetCurrentAdmin(ctx context.Context, _ *connect.Request[adminv1.GetCurrentAdminRequest]) (*connect.Response[adminv1.GetCurrentAdminResponse], error)
- func (h *SessionHandler) Login(ctx context.Context, req *connect.Request[adminv1.LoginRequest]) (*connect.Response[adminv1.LoginResponse], error)
- func (h *SessionHandler) Logout(ctx context.Context, req *connect.Request[adminv1.LogoutRequest]) (*connect.Response[adminv1.LogoutResponse], error)
- type SettingsHandler
- func (h *SettingsHandler) GetInstanceSettings(ctx context.Context, _ *connect.Request[adminv1.GetInstanceSettingsRequest]) (*connect.Response[adminv1.GetInstanceSettingsResponse], error)
- func (h *SettingsHandler) SMTPConfigured() bool
- func (h *SettingsHandler) SendTestEmail(ctx context.Context, req *connect.Request[adminv1.SendTestEmailRequest]) (*connect.Response[adminv1.SendTestEmailResponse], error)
- func (h *SettingsHandler) UpdateSmtpSettings(ctx context.Context, req *connect.Request[adminv1.UpdateSmtpSettingsRequest]) (*connect.Response[adminv1.UpdateSmtpSettingsResponse], error)
- type Store
- type ThemeHandler
- func (h *ThemeHandler) DeleteLogo(ctx context.Context, req *connect.Request[adminv1.DeleteLogoRequest]) (*connect.Response[adminv1.DeleteLogoResponse], error)
- func (h *ThemeHandler) GetTheme(ctx context.Context, req *connect.Request[adminv1.GetThemeRequest]) (*connect.Response[adminv1.GetThemeResponse], error)
- func (h *ThemeHandler) ListThemeRevisions(ctx context.Context, req *connect.Request[adminv1.ListThemeRevisionsRequest]) (*connect.Response[adminv1.ListThemeRevisionsResponse], error)
- func (h *ThemeHandler) ResetTheme(ctx context.Context, req *connect.Request[adminv1.ResetThemeRequest]) (*connect.Response[adminv1.ResetThemeResponse], error)
- func (h *ThemeHandler) RestoreThemeRevision(ctx context.Context, req *connect.Request[adminv1.RestoreThemeRevisionRequest]) (*connect.Response[adminv1.RestoreThemeRevisionResponse], error)
- func (h *ThemeHandler) UpdateTheme(ctx context.Context, req *connect.Request[adminv1.UpdateThemeRequest]) (*connect.Response[adminv1.UpdateThemeResponse], error)
- func (h *ThemeHandler) UploadLogo(ctx context.Context, req *connect.Request[adminv1.UploadLogoRequest]) (*connect.Response[adminv1.UploadLogoResponse], error)
- type UserHandler
- func (h *UserHandler) CreateUser(ctx context.Context, req *connect.Request[adminv1.CreateUserRequest]) (*connect.Response[adminv1.CreateUserResponse], error)
- func (h *UserHandler) DeleteUser(ctx context.Context, req *connect.Request[adminv1.DeleteUserRequest]) (*connect.Response[adminv1.DeleteUserResponse], error)
- func (h *UserHandler) DisableUser(ctx context.Context, req *connect.Request[adminv1.DisableUserRequest]) (*connect.Response[adminv1.DisableUserResponse], error)
- func (h *UserHandler) EnableUser(ctx context.Context, req *connect.Request[adminv1.EnableUserRequest]) (*connect.Response[adminv1.EnableUserResponse], error)
- func (h *UserHandler) GetUser(ctx context.Context, req *connect.Request[adminv1.GetUserRequest]) (*connect.Response[adminv1.GetUserResponse], error)
- func (h *UserHandler) ListUsers(ctx context.Context, req *connect.Request[adminv1.ListUsersRequest]) (*connect.Response[adminv1.ListUsersResponse], error)
- func (h *UserHandler) RevokeUserSessions(ctx context.Context, req *connect.Request[adminv1.RevokeUserSessionsRequest]) (*connect.Response[adminv1.RevokeUserSessionsResponse], error)
- func (h *UserHandler) SendPasswordReset(ctx context.Context, req *connect.Request[adminv1.SendPasswordResetRequest]) (*connect.Response[adminv1.SendPasswordResetResponse], error)
- func (h *UserHandler) UpdateUser(ctx context.Context, req *connect.Request[adminv1.UpdateUserRequest]) (*connect.Response[adminv1.UpdateUserResponse], error)
Constants ¶
const ( ActionProjectCreate = "project.create" ActionProjectUpdate = "project.update" ActionProjectDelete = "project.delete" ActionSecretKeyRegen = "project.secret_key.regenerate" ActionSigningKeyReset = "signing_key.reset" ActionSigningKeyRotate = "signing_key.rotate" ActionProjectExport = "project.export" ActionProjectImport = "project.import" ActionProviderUpdate = "provider.update" ActionUserCreate = "user.create" ActionUserUpdate = "user.update" ActionUserDisable = "user.disable" ActionUserEnable = "user.enable" ActionUserDelete = "user.delete" ActionUserSessionsRvk = "user.sessions.revoke" ActionUserPwReset = "user.password_reset.send" ActionPATCreate = "pat.create" ActionPATRevoke = "pat.revoke" ActionAdminInvite = "admin.invite" ActionAdminAccept = "admin.invite.accept" ActionAdminPassword = "admin.password.change" ActionSMTPUpdate = "smtp.update" ActionThemeUpdate = "theme.update" ActionThemeRestore = "theme.restore" ActionThemeReset = "theme.reset" ActionThemeLogoUpload = "theme.logo.upload" ActionThemeLogoDelete = "theme.logo.delete" // Milestone 11 — subscriptions & entitlements. ActionEntitlementCreate = "entitlement.create" ActionEntitlementUpdate = "entitlement.update" ActionEntitlementDelete = "entitlement.delete" ActionProductCreate = "product.create" ActionProductUpdate = "product.update" ActionProductDelete = "product.delete" ActionGrantCreate = "subscription.grant" ActionGrantRevoke = "subscription.grant.revoke" ActionBillingCredsUpdate = "billing.credentials.update" // Milestone 12 — store catalog provisioning. ActionStoreCatalogSync = "billing.store_catalog.sync" ActionOfferingReorder = "billing.offering.reorder" // Milestone 13 — themed paywall. ActionPaywallUpdate = "paywall.update" ActionPaywallRestore = "paywall.restore" ActionCopyUpdate = "copy.update" ActionCopyReset = "copy.reset" ActionCopyRestore = "copy.restore" ActionPaywallReset = "paywall.reset" // Milestone 20 — push device registry. ActionPushSettingsUpdate = "push.settings.update" ActionPushDeviceRevoke = "push.device.revoke" // Milestone 22 — setup profile (the creation wizard's answers). ActionProfileUpdate = "profile.update" )
Audit action names. Machine-readable, dotted <target>.<verb>; the human summary carries the specifics.
const CookieName = "moth_admin_session"
CookieName is the admin session cookie.
const SessionTTL = 7 * 24 * time.Hour
SessionTTL is how long an admin stays logged in.
Variables ¶
This section is empty.
Functions ¶
func AdminFromContext ¶
AdminFromContext returns the admin authenticated by the auth interceptor.
func ClearCookie ¶
ClearCookie returns an expired session cookie.
func IssueSession ¶
func IssueSession(ctx context.Context, st store.SessionStore, adminID string, secure bool) (*http.Cookie, error)
IssueSession creates a server-side session for adminID and returns the cookie to set. Shared with the first-run setup HTTP handler.
func NewAuthInterceptor ¶
func NewAuthInterceptor(st Store) connect.UnaryInterceptorFunc
NewAuthInterceptor authenticates every admin RPC (except the public procedures): either the admin session cookie (the SPA) or a personal access token presented as `authorization: Bearer moth_pat_...` (the CLI). Both paths inject the same admin identity into the context, so every admin handler works unchanged over either credential.
func PATProto ¶
func PATProto(t store.PersonalAccessToken) *adminv1.PersonalAccessToken
PATProto converts a stored personal access token to its proto metadata (exported: the local `moth admin token` commands reuse it for --json).
func SessionHashFromContext ¶
SessionHashFromContext returns the hash of the session token that authenticated this request, so handlers can spare the current session when ending the others. Absent on PAT-authenticated requests.
Types ¶
type AccountHandler ¶
type AccountHandler struct {
// contains filtered or unexported fields
}
AccountHandler implements moth.admin.v1.AdminAccountService.
func NewAccountHandler ¶
func NewAccountHandler(st Store, mailer mailpkg.Mailer, baseURL string, secure bool, smtpOn func() bool, auditor *Auditor) *AccountHandler
NewAccountHandler builds the admin account service.
func (*AccountHandler) AcceptAdminInvite ¶
func (h *AccountHandler) AcceptAdminInvite(ctx context.Context, req *connect.Request[adminv1.AcceptAdminInviteRequest]) (*connect.Response[adminv1.AcceptAdminInviteResponse], error)
func (*AccountHandler) ChangePassword ¶
func (h *AccountHandler) ChangePassword(ctx context.Context, req *connect.Request[adminv1.ChangePasswordRequest]) (*connect.Response[adminv1.ChangePasswordResponse], error)
func (*AccountHandler) CreatePersonalAccessToken ¶
func (h *AccountHandler) CreatePersonalAccessToken(ctx context.Context, req *connect.Request[adminv1.CreatePersonalAccessTokenRequest]) (*connect.Response[adminv1.CreatePersonalAccessTokenResponse], error)
func (*AccountHandler) InviteAdmin ¶
func (h *AccountHandler) InviteAdmin(ctx context.Context, req *connect.Request[adminv1.InviteAdminRequest]) (*connect.Response[adminv1.InviteAdminResponse], error)
func (*AccountHandler) ListAdminInvites ¶
func (h *AccountHandler) ListAdminInvites(ctx context.Context, _ *connect.Request[adminv1.ListAdminInvitesRequest]) (*connect.Response[adminv1.ListAdminInvitesResponse], error)
func (*AccountHandler) ListAdmins ¶
func (h *AccountHandler) ListAdmins(ctx context.Context, _ *connect.Request[adminv1.ListAdminsRequest]) (*connect.Response[adminv1.ListAdminsResponse], error)
func (*AccountHandler) ListPersonalAccessTokens ¶
func (h *AccountHandler) ListPersonalAccessTokens(ctx context.Context, _ *connect.Request[adminv1.ListPersonalAccessTokensRequest]) (*connect.Response[adminv1.ListPersonalAccessTokensResponse], error)
func (*AccountHandler) RevokeAdminInvite ¶
func (h *AccountHandler) RevokeAdminInvite(ctx context.Context, req *connect.Request[adminv1.RevokeAdminInviteRequest]) (*connect.Response[adminv1.RevokeAdminInviteResponse], error)
func (*AccountHandler) RevokePersonalAccessToken ¶
func (h *AccountHandler) RevokePersonalAccessToken(ctx context.Context, req *connect.Request[adminv1.RevokePersonalAccessTokenRequest]) (*connect.Response[adminv1.RevokePersonalAccessTokenResponse], error)
type AnalyticsHandler ¶
type AnalyticsHandler struct {
// contains filtered or unexported fields
}
func NewAnalyticsHandler ¶
func NewAnalyticsHandler(st Store, rollup *analytics.Rollup, now func() time.Time) *AnalyticsHandler
NewAnalyticsHandler builds the analytics service. now is injectable for tests; nil means time.Now.
func (*AnalyticsHandler) GetStats ¶
func (h *AnalyticsHandler) GetStats(ctx context.Context, req *connect.Request[adminv1.GetStatsRequest]) (*connect.Response[adminv1.GetStatsResponse], error)
func (*AnalyticsHandler) GetSubscriptionStats ¶
func (h *AnalyticsHandler) GetSubscriptionStats(ctx context.Context, req *connect.Request[adminv1.GetSubscriptionStatsRequest]) (*connect.Response[adminv1.GetSubscriptionStatsResponse], error)
GetSubscriptionStats serves the subscription revenue dashboards from the pre-aggregated monthly rollup (subscription_monthly_stats / subscription_tier_stats) — it never scans raw subscription_events. Money is per currency, never blended.
func (*AnalyticsHandler) ListRecentEvents ¶
func (h *AnalyticsHandler) ListRecentEvents(ctx context.Context, req *connect.Request[adminv1.ListRecentEventsRequest]) (*connect.Response[adminv1.ListRecentEventsResponse], error)
func (*AnalyticsHandler) RunRollup ¶
func (h *AnalyticsHandler) RunRollup(ctx context.Context, req *connect.Request[adminv1.RunRollupRequest]) (*connect.Response[adminv1.RunRollupResponse], error)
type AuditHandler ¶
type AuditHandler struct {
// contains filtered or unexported fields
}
AuditHandler implements moth.admin.v1.AuditService.
func NewAuditHandler ¶
func NewAuditHandler(st store.AuditStore) *AuditHandler
NewAuditHandler builds the audit viewer service.
func (*AuditHandler) ListAuditLog ¶
func (h *AuditHandler) ListAuditLog(ctx context.Context, req *connect.Request[adminv1.ListAuditLogRequest]) (*connect.Response[adminv1.ListAuditLogResponse], error)
ListAuditLog returns audit entries newest-first, narrowed by the filters and paged with page_size / page_token. The token is the id of the last row of the previous page (audit ids are UUIDv7, so id order is time order).
type Auditor ¶
type Auditor struct {
// contains filtered or unexported fields
}
Auditor records admin actions, attributing each to the credential that authenticated the request (browser session vs personal access token) and tagging it with the coarse client IP. Writes never fail the request.
func NewAuditor ¶
NewAuditor builds an Auditor over the audit sink.
type BillingHandler ¶
type BillingHandler struct {
// contains filtered or unexported fields
}
BillingHandler implements the four moth.admin.v1 billing services: EntitlementService, ProductService, SubscriptionService and BillingCredentialsService. One struct backs all four so they share the store, master key and audit sink; server.New registers it under each service path.
func NewBillingHandler ¶
func NewBillingHandler(st Store, master keys.MasterKey, auditor *Auditor, now func() time.Time) *BillingHandler
NewBillingHandler builds the admin billing services. now is injectable for tests; nil means time.Now.
func (*BillingHandler) CreateEntitlement ¶
func (h *BillingHandler) CreateEntitlement(ctx context.Context, req *connect.Request[adminv1.CreateEntitlementRequest]) (*connect.Response[adminv1.CreateEntitlementResponse], error)
func (*BillingHandler) CreateProduct ¶
func (h *BillingHandler) CreateProduct(ctx context.Context, req *connect.Request[adminv1.CreateProductRequest]) (*connect.Response[adminv1.CreateProductResponse], error)
func (*BillingHandler) DeleteEntitlement ¶
func (h *BillingHandler) DeleteEntitlement(ctx context.Context, req *connect.Request[adminv1.DeleteEntitlementRequest]) (*connect.Response[adminv1.DeleteEntitlementResponse], error)
func (*BillingHandler) DeleteProduct ¶
func (h *BillingHandler) DeleteProduct(ctx context.Context, req *connect.Request[adminv1.DeleteProductRequest]) (*connect.Response[adminv1.DeleteProductResponse], error)
func (*BillingHandler) GetBillingCredentials ¶
func (h *BillingHandler) GetBillingCredentials(ctx context.Context, req *connect.Request[adminv1.GetBillingCredentialsRequest]) (*connect.Response[adminv1.GetBillingCredentialsResponse], error)
func (*BillingHandler) GetProduct ¶
func (h *BillingHandler) GetProduct(ctx context.Context, req *connect.Request[adminv1.GetProductRequest]) (*connect.Response[adminv1.GetProductResponse], error)
func (*BillingHandler) GetUserSubscription ¶
func (h *BillingHandler) GetUserSubscription(ctx context.Context, req *connect.Request[adminv1.GetUserSubscriptionRequest]) (*connect.Response[adminv1.GetUserSubscriptionResponse], error)
func (*BillingHandler) GrantEntitlement ¶
func (h *BillingHandler) GrantEntitlement(ctx context.Context, req *connect.Request[adminv1.GrantEntitlementRequest]) (*connect.Response[adminv1.GrantEntitlementResponse], error)
func (*BillingHandler) ListEntitlements ¶
func (h *BillingHandler) ListEntitlements(ctx context.Context, req *connect.Request[adminv1.ListEntitlementsRequest]) (*connect.Response[adminv1.ListEntitlementsResponse], error)
func (*BillingHandler) ListProducts ¶
func (h *BillingHandler) ListProducts(ctx context.Context, req *connect.Request[adminv1.ListProductsRequest]) (*connect.Response[adminv1.ListProductsResponse], error)
func (*BillingHandler) ListUserSubscriptions ¶
func (h *BillingHandler) ListUserSubscriptions(ctx context.Context, req *connect.Request[adminv1.ListUserSubscriptionsRequest]) (*connect.Response[adminv1.ListUserSubscriptionsResponse], error)
func (*BillingHandler) RevokeGrant ¶
func (h *BillingHandler) RevokeGrant(ctx context.Context, req *connect.Request[adminv1.RevokeGrantRequest]) (*connect.Response[adminv1.RevokeGrantResponse], error)
func (*BillingHandler) UpdateBillingCredentials ¶
func (h *BillingHandler) UpdateBillingCredentials(ctx context.Context, req *connect.Request[adminv1.UpdateBillingCredentialsRequest]) (*connect.Response[adminv1.UpdateBillingCredentialsResponse], error)
func (*BillingHandler) UpdateEntitlement ¶
func (h *BillingHandler) UpdateEntitlement(ctx context.Context, req *connect.Request[adminv1.UpdateEntitlementRequest]) (*connect.Response[adminv1.UpdateEntitlementResponse], error)
func (*BillingHandler) UpdateProduct ¶
func (h *BillingHandler) UpdateProduct(ctx context.Context, req *connect.Request[adminv1.UpdateProductRequest]) (*connect.Response[adminv1.UpdateProductResponse], error)
type CopyHandler ¶
type CopyHandler struct {
// contains filtered or unexported fields
}
CopyHandler implements moth.admin.v1.CopyService: the per-project localization overrides on top of moth's bundled catalog (internal/i18n). It mirrors ThemeService — a curated, closed key set with bundled defaults, per-project overrides, and versioned revisions — keyed by screen × locale instead of by color. Every override save is validated against the catalog (known key, required placeholders, length) and recorded as a new revision; the store keeps the last store.CopyRevisionKeep for undo.
func NewCopyHandler ¶
func NewCopyHandler(st Store, auditor *Auditor) *CopyHandler
NewCopyHandler builds the copy service.
func (*CopyHandler) GetProjectCopy ¶
func (h *CopyHandler) GetProjectCopy(ctx context.Context, req *connect.Request[adminv1.GetProjectCopyRequest]) (*connect.Response[adminv1.GetProjectCopyResponse], error)
func (*CopyHandler) ListCopyRevisions ¶
func (h *CopyHandler) ListCopyRevisions(ctx context.Context, req *connect.Request[adminv1.ListCopyRevisionsRequest]) (*connect.Response[adminv1.ListCopyRevisionsResponse], error)
func (*CopyHandler) ListLocales ¶
func (h *CopyHandler) ListLocales(ctx context.Context, req *connect.Request[adminv1.ListLocalesRequest]) (*connect.Response[adminv1.ListLocalesResponse], error)
func (*CopyHandler) ResetCopy ¶
func (h *CopyHandler) ResetCopy(ctx context.Context, req *connect.Request[adminv1.ResetCopyRequest]) (*connect.Response[adminv1.ResetCopyResponse], error)
func (*CopyHandler) RestoreCopyRevision ¶
func (h *CopyHandler) RestoreCopyRevision(ctx context.Context, req *connect.Request[adminv1.RestoreCopyRevisionRequest]) (*connect.Response[adminv1.RestoreCopyRevisionResponse], error)
func (*CopyHandler) UpdateProjectCopy ¶
func (h *CopyHandler) UpdateProjectCopy(ctx context.Context, req *connect.Request[adminv1.UpdateProjectCopyRequest]) (*connect.Response[adminv1.UpdateProjectCopyResponse], error)
type Credential ¶
type Credential struct {
Type CredentialType
// PATID is the personal access token id when Type is CredentialPAT.
PATID string
// PATExpiresAt is that token's expiry (nil: never expires), so
// CreatePersonalAccessToken can cap PAT-minted tokens at the creating
// token's remaining lifetime.
PATExpiresAt *time.Time
}
Credential records how a request was authenticated, so actions can be attributed to the exact credential (the milestone-10 audit log).
func CredentialFromContext ¶
func CredentialFromContext(ctx context.Context) (Credential, bool)
CredentialFromContext returns which credential authenticated this request (cookie session or personal access token).
type CredentialType ¶
type CredentialType string
CredentialType is the kind of credential that authenticated a request.
const ( // CredentialSession is the browser session cookie (the admin SPA). CredentialSession CredentialType = "session" // CredentialPAT is a personal access token (the CLI, scripts). CredentialPAT CredentialType = "pat" )
type LiveStoreSyncer ¶
type LiveStoreSyncer struct {
// contains filtered or unexported fields
}
LiveStoreSyncer is the production storeSyncer wired by server.New. It drives the real store-catalog clients from moth's encrypted billing credentials.
Apple: the App Store Connect *catalog* API key is never persisted by moth, so this cannot create Apple subscriptions — it returns a guided ManualStep pointing at `moth setup billing` (which supplies the ASC key in-process). This is the honest boundary from the capability spike, not a silent skip.
Google: the service account is stored, so the Android Publisher subscription push runs here. The RTDN Pub/Sub topic needs a separate pubsub-scoped credential moth does not hold, so topic/subscription wiring degrades to a guided notification result.
Stripe: the secret key is stored and the Stripe API can do everything, so the catalog push (Products + recurring Prices) is fully automated here. The webhook endpoint is NOT wired from this path — Stripe reveals the signing secret exactly once at creation and `moth setup billing` persists it in the same run; the handler surfaces a guided step until the secret is stored.
func NewLiveStoreSyncer ¶
func NewLiveStoreSyncer(master keys.MasterKey) LiveStoreSyncer
NewLiveStoreSyncer builds the production storeSyncer for server.New. The returned value drives the real Android Publisher client for Google and emits a guided CLI step for Apple (the ASC catalog key is never persisted).
func (LiveStoreSyncer) Sync ¶
func (s LiveStoreSyncer) Sync(ctx context.Context, storeName, slug, baseURL string, creds store.BillingCredentials, cat setup.DesiredCatalog) (*setup.SyncResult, error)
type MonetizationHandler ¶
type MonetizationHandler struct {
// contains filtered or unexported fields
}
MonetizationHandler implements moth.admin.v1.MonetizationService: it reads a project's catalog + offering, reports store-connection/drift status, and reconciles the catalog into App Store Connect / Google Play.
Honest automation (plan/12): the App Store Connect *catalog* API key is write-only and never persisted server-side (moth stores only the App Store Server API IAP key, for the milestone-11 engine), so the admin cannot itself create Apple subscriptions — SyncStoreCatalog computes the plan/diff and emits the exact guided steps (including the `moth setup billing` command, which holds the ASC key in-process). Google's service account IS stored, so its catalog push can run server-side through the injected syncer. Every live store call sits behind the storeSyncer interface, nil for local/guided-only mode, so the handler is testable with no network.
func NewMonetizationHandler ¶
func NewMonetizationHandler(st Store, master keys.MasterKey, baseURL string, auditor *Auditor, syncer storeSyncer, now func() time.Time) *MonetizationHandler
NewMonetizationHandler builds the monetization service. syncer may be nil (local/guided-only reconciliation); now defaults to time.Now.
func (*MonetizationHandler) GetOffering ¶
func (h *MonetizationHandler) GetOffering(ctx context.Context, req *connect.Request[adminv1.GetOfferingRequest]) (*connect.Response[adminv1.GetOfferingResponse], error)
func (*MonetizationHandler) GetStoreCatalogStatus ¶
func (h *MonetizationHandler) GetStoreCatalogStatus(ctx context.Context, req *connect.Request[adminv1.GetStoreCatalogStatusRequest]) (*connect.Response[adminv1.GetStoreCatalogStatusResponse], error)
GetStoreCatalogStatus reports each store's credential/notification wiring and a per-product sync state. It computes a *live moth-side diff*: a product recorded in_sync whose price/period/listing has since changed in moth is reported DRIFT (recomputed from productRevision), not stale parity — so an edit that has not been re-pushed is visible immediately. Drift introduced outside moth (a price edited directly in Play Console / App Store Connect) is not observable from a persisted row; it surfaces on the next live push — for Google via the admin "Sync store catalog" action (which re-reads the store), for Apple via `moth setup billing` / `moth doctor` (moth never holds the ASC catalog key server-side, the honest boundary from the capability spike).
func (*MonetizationHandler) ReorderOffering ¶
func (h *MonetizationHandler) ReorderOffering(ctx context.Context, req *connect.Request[adminv1.ReorderOfferingRequest]) (*connect.Response[adminv1.ReorderOfferingResponse], error)
func (*MonetizationHandler) SyncStoreCatalog ¶
func (h *MonetizationHandler) SyncStoreCatalog(ctx context.Context, req *connect.Request[adminv1.SyncStoreCatalogRequest]) (*connect.Response[adminv1.SyncStoreCatalogResponse], error)
type PaywallHandler ¶
type PaywallHandler struct {
// contains filtered or unexported fields
}
PaywallHandler implements moth.admin.v1.PaywallService: the per-project paywall configuration (plan/13). Configs are validated by internal/paywall (bounded copy, known layout, http(s) legal links) before every save, and every save is a new revision (the store keeps the last store.PaywallRevisionKeep for undo). It mirrors ThemeHandler exactly, minus the logo asset plumbing — the paywall inherits colors/typography/logo from the theme and owns no assets of its own.
func NewPaywallHandler ¶
func NewPaywallHandler(st Store, auditor *Auditor) *PaywallHandler
NewPaywallHandler builds the paywall service.
func (*PaywallHandler) GetPaywallConfig ¶
func (h *PaywallHandler) GetPaywallConfig(ctx context.Context, req *connect.Request[adminv1.GetPaywallConfigRequest]) (*connect.Response[adminv1.GetPaywallConfigResponse], error)
func (*PaywallHandler) ListPaywallRevisions ¶
func (h *PaywallHandler) ListPaywallRevisions(ctx context.Context, req *connect.Request[adminv1.ListPaywallRevisionsRequest]) (*connect.Response[adminv1.ListPaywallRevisionsResponse], error)
func (*PaywallHandler) ResetPaywall ¶
func (h *PaywallHandler) ResetPaywall(ctx context.Context, req *connect.Request[adminv1.ResetPaywallRequest]) (*connect.Response[adminv1.ResetPaywallResponse], error)
func (*PaywallHandler) RestorePaywallRevision ¶
func (h *PaywallHandler) RestorePaywallRevision(ctx context.Context, req *connect.Request[adminv1.RestorePaywallRevisionRequest]) (*connect.Response[adminv1.RestorePaywallRevisionResponse], error)
func (*PaywallHandler) UpdatePaywallConfig ¶
func (h *PaywallHandler) UpdatePaywallConfig(ctx context.Context, req *connect.Request[adminv1.UpdatePaywallConfigRequest]) (*connect.Response[adminv1.UpdatePaywallConfigResponse], error)
type ProfileHandler ¶
type ProfileHandler struct {
// contains filtered or unexported fields
}
ProfileHandler implements moth.admin.v1.ProfileService: the project setup profile (the milestone-22 wizard's answers — plain config, full replacement, no revisions, like the push settings) and the derived setup checklist. The checklist is recomputed from live configuration on every call using the same predicates `moth doctor` runs (internal/setup); nothing about completeness is ever stored, so it cannot go stale.
func NewProfileHandler ¶
func NewProfileHandler(st Store, auditor *Auditor, smtpConfigured func() bool, now func() time.Time) *ProfileHandler
NewProfileHandler builds the profile admin service. smtpConfigured is the instance settings handler's SMTPConfigured (nil: SMTP treated as not configured); now is injectable for tests (nil: time.Now).
func (*ProfileHandler) GetProfile ¶
func (h *ProfileHandler) GetProfile(ctx context.Context, req *connect.Request[adminv1.GetProfileRequest]) (*connect.Response[adminv1.GetProfileResponse], error)
GetProfile returns the project's setup profile; a project created before the wizard has none (has_profile false, no profile message).
func (*ProfileHandler) GetProjectSetupStatus ¶
func (h *ProfileHandler) GetProjectSetupStatus(ctx context.Context, req *connect.Request[adminv1.GetProjectSetupStatusRequest]) (*connect.Response[adminv1.GetProjectSetupStatusResponse], error)
GetProjectSetupStatus derives the outstanding setup checklist from live configuration plus the profile's intent. Features the profile did not choose produce no item; each item disappears the moment the underlying configuration exists, however it got there (tabs, CLI, a teammate). Projects without a profile get an empty list and has_profile false.
func (*ProfileHandler) UpdateProfile ¶
func (h *ProfileHandler) UpdateProfile(ctx context.Context, req *connect.Request[adminv1.UpdateProfileRequest]) (*connect.Response[adminv1.UpdateProfileResponse], error)
UpdateProfile validates and installs a full replacement of the setup profile.
type ProjectHandler ¶
type ProjectHandler struct {
// contains filtered or unexported fields
}
ProjectHandler implements moth.admin.v1.ProjectService.
func NewProjectHandler ¶
func NewProjectHandler(st Store, master keys.MasterKey, baseURL string, auditor *Auditor) *ProjectHandler
NewProjectHandler builds the project service. The master key encrypts each new project's signing key at rest.
func (*ProjectHandler) CreateProject ¶
func (h *ProjectHandler) CreateProject(ctx context.Context, req *connect.Request[adminv1.CreateProjectRequest]) (*connect.Response[adminv1.CreateProjectResponse], error)
func (*ProjectHandler) DeleteProject ¶
func (h *ProjectHandler) DeleteProject(ctx context.Context, req *connect.Request[adminv1.DeleteProjectRequest]) (*connect.Response[adminv1.DeleteProjectResponse], error)
func (*ProjectHandler) ExportProject ¶
func (h *ProjectHandler) ExportProject(ctx context.Context, req *connect.Request[adminv1.ExportProjectRequest]) (*connect.Response[adminv1.ExportProjectResponse], error)
ExportProject returns every user of the project with password hashes and provider identities, for migration off moth.
func (*ProjectHandler) GetProject ¶
func (h *ProjectHandler) GetProject(ctx context.Context, req *connect.Request[adminv1.GetProjectRequest]) (*connect.Response[adminv1.GetProjectResponse], error)
func (*ProjectHandler) GetSigningKey ¶
func (h *ProjectHandler) GetSigningKey(ctx context.Context, req *connect.Request[adminv1.GetSigningKeyRequest]) (*connect.Response[adminv1.GetSigningKeyResponse], error)
func (*ProjectHandler) ImportProject ¶
func (h *ProjectHandler) ImportProject(ctx context.Context, req *connect.Request[adminv1.ImportProjectRequest]) (*connect.Response[adminv1.ImportProjectResponse], error)
ImportProject bulk-creates users from a migration document, carrying their (possibly foreign) password hashes so the first sign-in verifies with the original algorithm and rehashes to argon2id. Users whose email already exists in the project are skipped.
func (*ProjectHandler) ListProjects ¶
func (h *ProjectHandler) ListProjects(ctx context.Context, _ *connect.Request[adminv1.ListProjectsRequest]) (*connect.Response[adminv1.ListProjectsResponse], error)
func (*ProjectHandler) RegenerateSecretKey ¶
func (h *ProjectHandler) RegenerateSecretKey(ctx context.Context, req *connect.Request[adminv1.RegenerateSecretKeyRequest]) (*connect.Response[adminv1.RegenerateSecretKeyResponse], error)
func (*ProjectHandler) ResetSigningKey ¶
func (h *ProjectHandler) ResetSigningKey(ctx context.Context, req *connect.Request[adminv1.ResetSigningKeyRequest]) (*connect.Response[adminv1.ResetSigningKeyResponse], error)
func (*ProjectHandler) RotateSigningKey ¶
func (h *ProjectHandler) RotateSigningKey(ctx context.Context, req *connect.Request[adminv1.RotateSigningKeyRequest]) (*connect.Response[adminv1.RotateSigningKeyResponse], error)
RotateSigningKey mints a fresh active signing key and moves the current key to grace status: it stays in the JWKS until grace_expire_time so in-flight access tokens keep validating and no user is signed out. Unlike ResetSigningKey it never revokes refresh tokens.
func (*ProjectHandler) UpdateProject ¶
func (h *ProjectHandler) UpdateProject(ctx context.Context, req *connect.Request[adminv1.UpdateProjectRequest]) (*connect.Response[adminv1.UpdateProjectResponse], error)
type PushHandler ¶
type PushHandler struct {
// contains filtered or unexported fields
}
PushHandler implements moth.admin.v1.PushService: the project push settings (enable switch + Web Push VAPID public key — plain config, full replacement, no revisions, unlike the paywall/theme documents) and the user detail Devices panel. Registrations are metadata-only here: push tokens are credentials and appear exclusively on the secret-key surface (moth.server.v1), never in admin responses.
func NewPushHandler ¶
func NewPushHandler(st Store, auditor *Auditor, now func() time.Time) *PushHandler
NewPushHandler builds the push admin service. now is injectable for tests; nil means time.Now.
func (*PushHandler) GetPushSettings ¶
func (h *PushHandler) GetPushSettings(ctx context.Context, req *connect.Request[adminv1.GetPushSettingsRequest]) (*connect.Response[adminv1.GetPushSettingsResponse], error)
GetPushSettings returns the project's push settings; a project that never configured push gets the defaults (disabled, no VAPID key).
func (*PushHandler) ListPushDevices ¶
func (h *PushHandler) ListPushDevices(ctx context.Context, req *connect.Request[adminv1.ListPushDevicesRequest]) (*connect.Response[adminv1.ListPushDevicesResponse], error)
ListPushDevices returns the project's active registrations for the Push tab, newest first with keyset pagination and an optional target filter. Each row carries the owning user's id and email so the operator can tell whose device it is; tokens never appear (adminv1.PushDevice has no token field). Project-wide per-target totals ride along on every page.
func (*PushHandler) ListUserPushDevices ¶
func (h *PushHandler) ListUserPushDevices(ctx context.Context, req *connect.Request[adminv1.ListUserPushDevicesRequest]) (*connect.Response[adminv1.ListUserPushDevicesResponse], error)
ListUserPushDevices returns one user's registrations for the user detail Devices panel, most recently seen first — active and revoked (revocation is auditable, not a delete), never the tokens.
func (*PushHandler) RevokePushDevice ¶
func (h *PushHandler) RevokePushDevice(ctx context.Context, req *connect.Request[adminv1.RevokePushDeviceRequest]) (*connect.Response[adminv1.RevokePushDeviceResponse], error)
RevokePushDevice revokes one registration by its row id (`admin` reason, audit-logged). Idempotent: revoking an already-revoked registration succeeds, keeps the original reason and is not re-audited.
func (*PushHandler) UpdatePushSettings ¶
func (h *PushHandler) UpdatePushSettings(ctx context.Context, req *connect.Request[adminv1.UpdatePushSettingsRequest]) (*connect.Response[adminv1.UpdatePushSettingsResponse], error)
UpdatePushSettings validates and installs a full replacement of the push settings.
type SessionHandler ¶
type SessionHandler struct {
// contains filtered or unexported fields
}
SessionHandler implements moth.admin.v1.SessionService.
func NewSessionHandler ¶
func NewSessionHandler(st Store, secure bool) *SessionHandler
NewSessionHandler builds the session service. secure controls the Secure attribute on cookies (true when the instance is served over https).
func (*SessionHandler) GetCurrentAdmin ¶
func (h *SessionHandler) GetCurrentAdmin(ctx context.Context, _ *connect.Request[adminv1.GetCurrentAdminRequest]) (*connect.Response[adminv1.GetCurrentAdminResponse], error)
func (*SessionHandler) Login ¶
func (h *SessionHandler) Login(ctx context.Context, req *connect.Request[adminv1.LoginRequest]) (*connect.Response[adminv1.LoginResponse], error)
func (*SessionHandler) Logout ¶
func (h *SessionHandler) Logout(ctx context.Context, req *connect.Request[adminv1.LogoutRequest]) (*connect.Response[adminv1.LogoutResponse], error)
type SettingsHandler ¶
type SettingsHandler struct {
// contains filtered or unexported fields
}
SettingsHandler implements moth.admin.v1.InstanceSettingsService.
The effective SMTP configuration is resolved with this precedence: database (set through this service) > config file / environment > none (console transport). Updates swap the shared dynamic mailer in place, so no restart is needed.
func NewSettingsHandler ¶
func NewSettingsHandler(ctx context.Context, st Store, cfg config.Config, dyn *mailpkg.Dynamic, fallback mailpkg.Mailer, master keys.MasterKey, auditor *Auditor) (*SettingsHandler, error)
NewSettingsHandler builds the instance settings service and points dyn at the effective SMTP transport.
func (*SettingsHandler) GetInstanceSettings ¶
func (h *SettingsHandler) GetInstanceSettings(ctx context.Context, _ *connect.Request[adminv1.GetInstanceSettingsRequest]) (*connect.Response[adminv1.GetInstanceSettingsResponse], error)
func (*SettingsHandler) SMTPConfigured ¶
func (h *SettingsHandler) SMTPConfigured() bool
SMTPConfigured reports whether a real SMTP transport is currently effective (used to decide whether invite emails actually go out).
func (*SettingsHandler) SendTestEmail ¶
func (h *SettingsHandler) SendTestEmail(ctx context.Context, req *connect.Request[adminv1.SendTestEmailRequest]) (*connect.Response[adminv1.SendTestEmailResponse], error)
func (*SettingsHandler) UpdateSmtpSettings ¶
func (h *SettingsHandler) UpdateSmtpSettings(ctx context.Context, req *connect.Request[adminv1.UpdateSmtpSettingsRequest]) (*connect.Response[adminv1.UpdateSmtpSettingsResponse], error)
type Store ¶
type Store interface {
store.AdminStore
store.AdminInviteStore
store.SessionStore
store.PersonalAccessTokenStore
store.InstanceSettingStore
store.InstanceSecretStore
store.AuditStore
store.ProjectStore
store.UserStore
store.UserMigrationStore
store.RefreshTokenStore
store.ProviderSecretStore
store.ThemeStore
store.PaywallStore
store.CopyStore
store.EventStore
store.StatsStore
store.SubscriptionStatsStore
store.EntitlementStore
store.ProductStore
store.SubscriptionStore
store.SubscriptionGrantStore
store.BillingCredentialStore
store.SubscriptionEventStore
store.ProductStoreSyncStore
store.PushDeviceStore
}
Store is everything the admin services need from persistence.
type ThemeHandler ¶
type ThemeHandler struct {
// contains filtered or unexported fields
}
ThemeHandler implements moth.admin.v1.ThemeService: the per-project design system (plan/06). Token sets are validated by internal/theme — WCAG AA contrast included — before every save, and every save is a new revision (the store keeps the last store.ThemeRevisionKeep for undo).
func NewThemeHandler ¶
func NewThemeHandler(st Store, uploadsDir string, auditor *Auditor) *ThemeHandler
NewThemeHandler builds the theme service. uploadsDir is created lazily on the first logo upload.
func (*ThemeHandler) DeleteLogo ¶
func (h *ThemeHandler) DeleteLogo(ctx context.Context, req *connect.Request[adminv1.DeleteLogoRequest]) (*connect.Response[adminv1.DeleteLogoResponse], error)
func (*ThemeHandler) GetTheme ¶
func (h *ThemeHandler) GetTheme(ctx context.Context, req *connect.Request[adminv1.GetThemeRequest]) (*connect.Response[adminv1.GetThemeResponse], error)
func (*ThemeHandler) ListThemeRevisions ¶
func (h *ThemeHandler) ListThemeRevisions(ctx context.Context, req *connect.Request[adminv1.ListThemeRevisionsRequest]) (*connect.Response[adminv1.ListThemeRevisionsResponse], error)
func (*ThemeHandler) ResetTheme ¶
func (h *ThemeHandler) ResetTheme(ctx context.Context, req *connect.Request[adminv1.ResetThemeRequest]) (*connect.Response[adminv1.ResetThemeResponse], error)
func (*ThemeHandler) RestoreThemeRevision ¶
func (h *ThemeHandler) RestoreThemeRevision(ctx context.Context, req *connect.Request[adminv1.RestoreThemeRevisionRequest]) (*connect.Response[adminv1.RestoreThemeRevisionResponse], error)
func (*ThemeHandler) UpdateTheme ¶
func (h *ThemeHandler) UpdateTheme(ctx context.Context, req *connect.Request[adminv1.UpdateThemeRequest]) (*connect.Response[adminv1.UpdateThemeResponse], error)
func (*ThemeHandler) UploadLogo ¶
func (h *ThemeHandler) UploadLogo(ctx context.Context, req *connect.Request[adminv1.UploadLogoRequest]) (*connect.Response[adminv1.UploadLogoResponse], error)
type UserHandler ¶
type UserHandler struct {
// contains filtered or unexported fields
}
UserHandler implements moth.admin.v1.UserService — the operator's user management, a cookie-authed façade over the same domain layer as moth.server.v1.
func NewUserHandler ¶
func NewUserHandler(st Store, auth *authrpc.Handler, mailer mailpkg.Mailer, ev *events.Writer, auditor *Auditor) *UserHandler
NewUserHandler builds the admin user service. events may be nil (tests).
func (*UserHandler) CreateUser ¶
func (h *UserHandler) CreateUser(ctx context.Context, req *connect.Request[adminv1.CreateUserRequest]) (*connect.Response[adminv1.CreateUserResponse], error)
func (*UserHandler) DeleteUser ¶
func (h *UserHandler) DeleteUser(ctx context.Context, req *connect.Request[adminv1.DeleteUserRequest]) (*connect.Response[adminv1.DeleteUserResponse], error)
func (*UserHandler) DisableUser ¶
func (h *UserHandler) DisableUser(ctx context.Context, req *connect.Request[adminv1.DisableUserRequest]) (*connect.Response[adminv1.DisableUserResponse], error)
func (*UserHandler) EnableUser ¶
func (h *UserHandler) EnableUser(ctx context.Context, req *connect.Request[adminv1.EnableUserRequest]) (*connect.Response[adminv1.EnableUserResponse], error)
func (*UserHandler) GetUser ¶
func (h *UserHandler) GetUser(ctx context.Context, req *connect.Request[adminv1.GetUserRequest]) (*connect.Response[adminv1.GetUserResponse], error)
func (*UserHandler) ListUsers ¶
func (h *UserHandler) ListUsers(ctx context.Context, req *connect.Request[adminv1.ListUsersRequest]) (*connect.Response[adminv1.ListUsersResponse], error)
func (*UserHandler) RevokeUserSessions ¶
func (h *UserHandler) RevokeUserSessions(ctx context.Context, req *connect.Request[adminv1.RevokeUserSessionsRequest]) (*connect.Response[adminv1.RevokeUserSessionsResponse], error)
func (*UserHandler) SendPasswordReset ¶
func (h *UserHandler) SendPasswordReset(ctx context.Context, req *connect.Request[adminv1.SendPasswordResetRequest]) (*connect.Response[adminv1.SendPasswordResetResponse], error)
func (*UserHandler) UpdateUser ¶
func (h *UserHandler) UpdateUser(ctx context.Context, req *connect.Request[adminv1.UpdateUserRequest]) (*connect.Response[adminv1.UpdateUserResponse], error)