mvp

package module
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 23, 2024 License: MIT Imports: 62 Imported by: 1

README

mvp

Pragmatic Golang web stack enabling quick development of “modern”, but sane web apps and APIs

Documentation

Index

Constants

View Source
const (
	MoodNeutral = Mood(iota)
	MoodSuccess
	MoodFailure
	MoodSubtle
)
View Source
const (
	RateLimitPresetNone            = RateLimitPreset("none")
	RateLimitPresetDefaultWebRead  = RateLimitPreset("web.r")
	RateLimitPresetDefaultWebWrite = RateLimitPreset("web.w")
	RateLimitPresetDefaultAPIRead  = RateLimitPreset("api.r")
	RateLimitPresetDefaultAPIWrite = RateLimitPreset("api.w")
	RateLimitPresetSpam            = RateLimitPreset("spam")
	RateLimitPresetAuthentication  = RateLimitPreset("auth")
	RateLimitPresetDangerous       = RateLimitPreset("danger")
)
View Source
const (
	RateLimitGranularityApp  = RateLimitGranularity("app")
	RateLimitGranularityIP   = RateLimitGranularity("ip")
	RateLimitGranularityUser = RateLimitGranularity("user")
	RateLimitGranularityKey  = RateLimitGranularity("key")
)
View Source
const (
	MwSlotRateLimitAdjustment = "ratelimitconf"
	MwSlotRateLimiter         = "ratelimit"
)
View Source
const (
	CacheControlHeader          = "Cache-Control"
	CacheControlUncachable      = "no-cache, no-store, no-transform, must-revalidate, private, max-age=0"
	CacheControlPublicImmutable = "public, max-age=31536000, immutable"
	CacheControlPublicMutable   = "public, no-cache, max-age=0"
	CacheControlPrivateMutable  = "private, no-cache, max-age=0"
)

Variables

View Source
var (
	// Now is the start time of a request. For short requests, it should be the Now
	// value for all processing that happens inside. Can be mocked.
	Now = Value[time.Time]("now")

	// WallStart is an unmocked start time of the request for
	RealStartTime = Value[time.Time]("real_start_time")
)
View Source
var (
	ErrTooManyRequests = httperrors.Define(http.StatusTooManyRequests, "too_many_requests")
	ErrInvalidToken    = httperrors.Define(http.StatusUnauthorized, "invalid_token")
	ErrForbidden       = httperrors.Define(http.StatusForbidden, "forbidden")

	ErrAPIInvalidMethod          = httperrors.Define(http.StatusMethodNotAllowed, "invalid_http_method")
	ErrAPIUnsupportedContentType = httperrors.Define(http.StatusUnsupportedMediaType, "invalid_content_type")
	ErrAPIInvalidJSON            = httperrors.Define(http.StatusBadRequest, "invalid_json")
	ErrAPIUnknownMethod          = httperrors.Define(http.StatusNotFound, "unknown_method")

	ErrPanic = httperrors.Define(http.StatusInternalServerError, "internal_server_error")
)
View Source
var (
	BaseSettings = expandable.NewBase[Settings](baseSchema, "settings")
	BaseApp      = expandable.NewBase[App](baseSchema, "app")
	BaseRC       = expandable.NewBase[RC](baseSchema, "rc")
)
View Source
var (
	NoSite      = NewSite("none")
	DefaultSite = NewSite("default")
)
View Source
var (
	Absolute = mvpm.NewURLOption("absolute")
)
View Source
var (
	GoCtx = Value[context.Context]("goctx")
)

Functions

func AddClassList

func AddClassList(classes []string, items []string) []string

func AddClasses

func AddClasses(classes []string, items string) []string

func AddSingleClass

func AddSingleClass(classes []string, item string) []string

func Attr

func Attr(name string, value any) template.HTMLAttr

func Attrs

func Attrs(attrs map[string]string) template.HTMLAttr

func AttrsAny

func AttrsAny(attrs map[string]any) template.HTMLAttr

func AttrsSwitch added in v0.2.0

func AttrsSwitch(attrs any) template.HTMLAttr

func BuildAPIErrorResponse

func BuildAPIErrorResponse(err error) httperrors.Interface

func CanonicalEmail

func CanonicalEmail(email string) string

CanonicalEmail returns an email suitable for unique checks.

func CanonicalPhone

func CanonicalPhone(number string) string

CanonicalPhone returns an phone number suitable for unique checks.

func ClientNetworkingError

func ClientNetworkingError(err error) error

func DecodeFlashIntoRC added in v0.2.2

func DecodeFlashIntoRC(rc *RC)

func DetermineMIMEType

func DetermineMIMEType(r *http.Request) string

func DisableCaching

func DisableCaching(w http.ResponseWriter)

func EmailRateLimitingKey

func EmailRateLimitingKey(email string) string

EmailRateLimitingKey is a slightly paranoid function that maps emails into string keys to use for rate limiting.

func FacebookMessengerSendLinkURL added in v0.2.2

func FacebookMessengerSendLinkURL(appID, link, redirectURL string, ua string) string

func FacebookShareLinkURL added in v0.2.2

func FacebookShareLinkURL(appID, link, redirectURL string, ua string) string

func FlakeIDType

func FlakeIDType() reflect.Type

func GracefulShutdown

func GracefulShutdown(gracePeriod time.Duration, graceful func(ctx context.Context) error, forceful func())

gracefulShutdown tries to do a graceful shutdown, but abandons the attempt and falls back to forceful shutdown after a timeout.

func HTMLify

func HTMLify(v any) template.HTML

func HTMLifyMultiparString added in v0.2.2

func HTMLifyMultiparString(text string) template.HTML

func HTMLifyMultiparValue added in v0.2.2

func HTMLifyMultiparValue(v any) template.HTML

func HTMLifyString added in v0.2.0

func HTMLifyString(text string) template.HTML

func HTTPStatusCode

func HTTPStatusCode(err error) int

func InterceptShutdownSignals

func InterceptShutdownSignals(shutdown func())

func IsAndroidRequest added in v0.2.1

func IsAndroidRequest(r *http.Request) bool

func IsAndroidUA added in v0.2.1

func IsAndroidUA(ua string) bool

func IsClientNetworkingError

func IsClientNetworkingError(err error) bool

func IsFacebookUA added in v0.2.2

func IsFacebookUA(ua string) bool

func IsGetOrHead added in v0.2.2

func IsGetOrHead(r *http.Request) bool

func IsMobileRequest added in v0.2.1

func IsMobileRequest(r *http.Request) bool

func IsMobileUA added in v0.2.1

func IsMobileUA(ua string) bool

func IsUndefined

func IsUndefined(v any) bool

func JoinClassList

func JoinClassList(classes []string) string

func JoinClassStrings added in v0.2.1

func JoinClassStrings(items ...string) string

func JoinClasses

func JoinClasses(items ...any) string

func JoinInlineCSS added in v0.2.1

func JoinInlineCSS(a, b string) string

func Main

func Main(ge *Configuration)

func MarkPrivateMutable

func MarkPrivateMutable(w http.ResponseWriter)

func MarkPublicImmutable

func MarkPublicImmutable(w http.ResponseWriter)

func MarkPublicMutable

func MarkPublicMutable(w http.ResponseWriter)

func NewPanic added in v0.2.2

func NewPanic(errValue any) error

func PartialRenderingError added in v0.2.2

func PartialRenderingError(templ string, cause error) error

func PlusToPercent20 added in v0.2.1

func PlusToPercent20(s string) string

func PushPartial added in v0.2.1

func PushPartial(rc RCish, vd *ViewData, elementID string, ch mvplive.Channel, ep mvplive.Envelope)

func RandomAlpha

func RandomAlpha(n int) string

func RandomBytes

func RandomBytes(len int) []byte

func RandomDigits

func RandomDigits(n int) string

func RandomHex

func RandomHex(len int) string

func RandomPassword added in v0.2.2

func RandomPassword(n int) string

func ReadAPIRequest

func ReadAPIRequest(r *http.Request, in any) error

func RecoveredError

func RecoveredError(e any) error

func RegisterBuiltinUtilityViewHelpers added in v0.2.0

func RegisterBuiltinUtilityViewHelpers(m template.FuncMap)

func RenderPartial added in v0.2.1

func RenderPartial(rc *RC, vd *ViewData) template.HTML

func RenderPartialTo added in v0.2.2

func RenderPartialTo(wr io.Writer, rc *RC, vd *ViewData)

func RequireGetOrHead added in v0.2.2

func RequireGetOrHead(w http.ResponseWriter, r *http.Request) bool

func SMSLinkURI added in v0.2.1

func SMSLinkURI(phone, body string, ua string) string

func SafeCall added in v0.2.2

func SafeCall(f func() error) (err error)

func SafeCall01 added in v0.2.2

func SafeCall01[R1 any](f func() (R1, error)) (r1 R1, err error)

func SafeCall11 added in v0.2.2

func SafeCall11[A1, R1 any](f func(a1 A1) (R1, error), a1 A1) (r1 R1, err error)

func SafeCall21 added in v0.2.2

func SafeCall21[A1, A2, R1 any](f func(a1 A1, a2 A2) (R1, error), a1 A1, a2 A2) (r1 R1, err error)

func SkipPanicStackIf added in v0.2.2

func SkipPanicStackIf(f func(error) bool)

func Stringify

func Stringify(v any) string

func TweetIntentURL added in v0.2.1

func TweetIntentURL(body string, linkURL string) string

func ValueDefault

func ValueDefault[T any](v T) func(val *Val[T])

ValueDefault is an option to pass to Value.

func ValueDep

func ValueDep[T any](deps ...AnyVal) func(val *Val[T])

ValueDep is an option to pass to Value. It signals that this value should be initialized after those other values.

func ValueInitializer

func ValueInitializer[T any](f func() T) func(val *Val[T])

ValueInitializer is an option to pass to Value.

func ValueZeroInit

func ValueZeroInit[T any](val *Val[T])

ValueZeroInit is an option to pass to Value.

func ViewRenderingError added in v0.2.2

func ViewRenderingError(templ string, cause error) error

func WhatsappSendURL added in v0.2.1

func WhatsappSendURL(body string, ua string) string

func WriteAPIResponse

func WriteAPIResponse(w http.ResponseWriter, out any, indented bool)

func WriteFileAtomic

func WriteFileAtomic(path string, data []byte, perm fs.FileMode) (err error)

func WriteMethodNotAllowed added in v0.2.2

func WriteMethodNotAllowed(w http.ResponseWriter)

func WritePlainError added in v0.2.2

func WritePlainError(w http.ResponseWriter, err error)

Types

type AnyVal

type AnyVal interface {
	Name() string
	// contains filtered or unexported methods
}

type App

type App struct {
	ValueSet
	DBSchema      edb.Schema
	JobSchema     *mvpjobs.Schema
	Configuration *Configuration
	Settings      *Settings
	Hooks         Hooks
	BaseURL       *url.URL
	IPForwarding  *mvputil.IPForwarding
	// contains filtered or unexported fields
}

func (*App) AuthenticateRequestMiddleware

func (app *App) AuthenticateRequestMiddleware(rc *RC) (any, error)

func (*App) Close

func (app *App) Close()

func (*App) DB

func (app *App) DB() *edb.DB

func (*App) DecodeAuthToken

func (app *App) DecodeAuthToken(rc *RC, token string) error

func (*App) Enqueue

func (app *App) Enqueue(rc RCish, kind *mvpjobs.Kind, in mvpjobs.Params) *mvpjobs.Job

func (*App) EnqueueEphemeral

func (app *App) EnqueueEphemeral(kind *mvpjobs.Kind, name string, f func(rc *RC) error)

func (*App) EvalTemplate

func (app *App) EvalTemplate(templateName string, data any) (template.HTML, error)

func (*App) ExecTemplate

func (app *App) ExecTemplate(w io.Writer, templateName string, data any) error

func (*App) FileProvider

func (app *App) FileProvider(resourceURI string, rc *RC) (FileProvider, string)

func (*App) FileURL

func (app *App) FileURL(resourceURI string, rc *RC, opt mvpm.URLOption) string

func (*App) Initialize

func (app *App) Initialize(settings *Settings, opt AppOptions)

func (*App) Job

func (app *App) Job(txish edb.Txish, kind *mvpjobs.Kind, name string) *mvpjobs.Job

func (*App) JobImpl

func (app *App) JobImpl(kind *mvpjobs.Kind)

func (*App) MakeAuthToken

func (app *App) MakeAuthToken(sessID flake.ID, actorRef mvpm.Ref, validity time.Duration) string

func (*App) MethodImpl

func (app *App) MethodImpl(method *mvprpc.Method, impl any)

func (*App) NewHTTPRequestRC

func (app *App) NewHTTPRequestRC(w http.ResponseWriter, r bunrouter.Request) *RC

func (*App) NewID

func (app *App) NewID() flake.ID

func (*App) Now

func (app *App) Now() time.Time

func (*App) PublishMsg

func (app *App) PublishMsg(lc flogger.Context, ch mvplive.Channel, msg *mvplive.Msg)

func (*App) PublishTurbo

func (app *App) PublishTurbo(lc flogger.Context, ch mvplive.Channel, ep mvplive.Envelope, f func(stream *hotwired.Stream))

func (*App) RateLimiters

func (app *App) RateLimiters(preset RateLimitPreset) map[RateLimitGranularity]*RateLimiter

func (*App) ReadFile

func (app *App) ReadFile(resourceURI string, rc *RC) ([]byte, error)

func (*App) Redirect

func (app *App) Redirect(name string, extras ...any) *Redirect

Redirect returns a response object that redirects to a given route. Status code defaults to 303 See Other. Supply map[string]string or pairs of (key string, value string) arguments to supply path parameters for the route. Supply url.Values to add query parameters. Supply Absolute flag to use an absolute URL.

func (*App) Reenqueue

func (app *App) Reenqueue(rc RCish, kind *mvpjobs.Kind, j *mvpjobs.Job, in mvpjobs.Params, force bool)

func (*App) Render

func (app *App) Render(lc flogger.Context, data *ViewData) ([]byte, error)

func (*App) RenderForm

func (app *App) RenderForm(rc *RC, form *forms.Form) template.HTML

func (*App) RunJob

func (app *App) RunJob(ctx context.Context, kind *mvpjobs.Kind, params mvpjobs.Params) error

func (*App) RunPendingJobs

func (app *App) RunPendingJobs(ctx context.Context) int

func (*App) SendEmail

func (app *App) SendEmail(rc *RC, msg *Email)

func (*App) ServeHTTP

func (app *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*App) SetNewKeyOnRow

func (app *App) SetNewKeyOnRow(row any) bool

func (*App) StartEphemeralJobWorkers

func (app *App) StartEphemeralJobWorkers(ctx context.Context, count int, quitf func(err error))

func (*App) StartJobWorkers

func (app *App) StartJobWorkers(ctx context.Context, count int, quitf func(err error))

func (*App) StaticFS

func (app *App) StaticFS() fs.FS

func (*App) Subscribe

func (app *App) Subscribe(ctx context.Context, lc flogger.Context, w http.ResponseWriter, ch mvplive.Channel, afterID flake.ID)

func (*App) URL

func (app *App) URL(name string, extras ...URLOption) string

URL generates a relative (default) or absolute URL based on a named route. Supply map[string]string or pairs of (key string, value string) arguments to supply path parameters for the route. Supply url.Values to add query parameters. Supply Absolute flag to return an absolute URL.

func (*App) WriteAPIError

func (app *App) WriteAPIError(w http.ResponseWriter, err error)

type AppBehaviors

type AppBehaviors struct {
	IsTesting           bool
	ServeAssetsFromDisk bool
	CrashOnPanic        bool
	PrettyJSON          bool
	DisableRateLimits   bool
	AllowInsecureHttp   bool
}

type AppOptions

type AppOptions struct {
	Context context.Context
	Logf    func(format string, v ...interface{})
}

type Auth

type Auth struct {
	SessionID flake.ID
	ActorRef  mvpm.Ref
}

type CallRegistry

type CallRegistry interface {
	CallImpl(method *mvprpc.Method, impl any)
}

type Configuration

type Configuration struct {
	Envs map[string][]string

	Preinstall func(settings *Settings)
	Install    func(settings *Settings)

	BuildCommit string
	BuildVer    string

	EmbeddedConfig   []byte
	EmbeddedSecrets  string
	EmbeddedStaticFS embed.FS
	EmbeddedViewsFS  embed.FS

	ConfigFileName  string
	SecretsFileName string
	StaticSubdir    string
	ViewsSubdir     string
	LocalDevAppRoot string
	FallbackFormID  string

	Modules []*Module

	LoadSecrets func(*Settings, Secrets)

	Types map[mvpm.Type][]string

	AuthTokenCookieName string
	AuthTokenKeys       mvpm.NamedKeySet
}

func (*Configuration) ValidEnvs

func (ge *Configuration) ValidEnvs() []string

type DebugOutput

type DebugOutput string

DebugOutput can be returned by request handlers

type DomainRouter

type DomainRouter struct {
	// contains filtered or unexported fields
}

func (*DomainRouter) HandleFunc

func (router *DomainRouter) HandleFunc(domain string, h func(http.ResponseWriter, *http.Request))

func (*DomainRouter) Handler

func (router *DomainRouter) Handler(domain string, h http.Handler)

func (*DomainRouter) Site

func (router *DomainRouter) Site(domain string, site *Site)

func (*DomainRouter) ValidDomains

func (router *DomainRouter) ValidDomains() []string

type Email

type Email struct {
	From    string
	To      string
	ReplyTo string
	Cc      string
	Bcc     string
	Subject string

	View     string
	Layout   string
	Data     any
	TextBody string
	HtmlBody string

	ServerTemplateIntID int64
	ServerTemplateData  map[string]any

	MessageStream string
	Category      string
}

type EphemeralJob

type EphemeralJob struct {
	Kind *mvpjobs.Kind
	Key  string
	F    func(rc *RC) error
}

type EphemeralJobQueue

type EphemeralJobQueue struct {
	// contains filtered or unexported fields
}

type ErrHandler

type ErrHandler = func(rc *RC, err error) (any, error)

type ExpandableObject

type ExpandableObject interface {
	// ValueAt(idx int) any
	ValueSet() ValueSet
}

type ExpandableType

type ExpandableType struct {
	StoredValues []AnyVal
}

type FileProvider

type FileProvider interface {
	String() string
	FileURL(path string, rc *RC, opt mvpm.URLOption) string
	ReadFile(path string) ([]byte, error)
}

type Flash added in v0.2.2

type Flash struct {
	Msg          *Msg           `json:"m,omitempty"`
	Action       string         `json:"a,omitempty"`
	ScrollTarget string         `json:"s,omitempty"`
	Extras       map[string]any `json:"e,omitempty"`
}

func DecodeFlashFromQuery added in v0.2.2

func DecodeFlashFromQuery(query url.Values) (*Flash, error)

func FailureMsg

func FailureMsg(text string) *Flash

func NeutralMsg

func NeutralMsg(text string) *Flash

func NewFlash added in v0.2.2

func NewFlash() *Flash

func SubtleMsg

func SubtleMsg(text string) *Flash

func SuccessMsg

func SuccessMsg(text string) *Flash

func (*Flash) JSONBytes added in v0.2.2

func (flash *Flash) JSONBytes() []byte

func (*Flash) JSONString added in v0.2.2

func (flash *Flash) JSONString() string

func (*Flash) SafeJSON added in v0.2.2

func (flash *Flash) SafeJSON() template.JS

func (*Flash) ScrollTo added in v0.2.2

func (flash *Flash) ScrollTo(id string) *Flash

func (*Flash) WithAction added in v0.2.2

func (flash *Flash) WithAction(action string) *Flash

type Hooks

type Hooks struct {
	// contains filtered or unexported fields
}

func (*Hooks) CloseApp

func (h *Hooks) CloseApp(f func(app *App))

func (*Hooks) CloseRC

func (h *Hooks) CloseRC(f func(app *App, rc *RC))

func (*Hooks) DomainRoutes

func (h *Hooks) DomainRoutes(f func(app *App, b *DomainRouter))

func (*Hooks) Helpers

func (h *Hooks) Helpers(f func(m template.FuncMap))

func (*Hooks) InitApp

func (h *Hooks) InitApp(f func(app *App))

func (*Hooks) InitDB

func (h *Hooks) InitDB(f func(app *App, rc *RC))

func (*Hooks) InitRC

func (h *Hooks) InitRC(f func(app *App, rc *RC))

func (*Hooks) JWTTokenKey

func (h *Hooks) JWTTokenKey(f func(rc *RC, c *TokenDecoding) error)

func (*Hooks) MakeRowKey

func (h *Hooks) MakeRowKey(f func(app *App, tbl *edb.Table) any)

func (*Hooks) Middleware

func (h *Hooks) Middleware(f func(r Router))

func (*Hooks) Migrate added in v0.2.1

func (h *Hooks) Migrate(f func(app *App, b *MigrationBuilder))

func (*Hooks) PostAuth

func (h *Hooks) PostAuth(f func(app *App, rc *RC) error)

func (*Hooks) ResetAuth

func (h *Hooks) ResetAuth(f func(app *App, rc *RC))

func (*Hooks) SiteRoutes

func (h *Hooks) SiteRoutes(site *Site, f func(b *RouteBuilder))

func (*Hooks) URLGen

func (h *Hooks) URLGen(f func(app *App, g *URLGen))

func (*Hooks) URLGenOption

func (h *Hooks) URLGenOption(f func(app *App, g *URLGen, option string) bool)

type JobImpl

type JobImpl struct {
	Method         *mvprpc.Method
	Enabled        bool
	Kind           *mvpjobs.Kind
	RepeatInterval time.Duration
}

type JobRegistry

type JobRegistry interface {
	JobImpl(kind *mvpjobs.Kind, impl any, opts ...any)
}

type MethodImpl

type MethodImpl struct {
	*mvprpc.Method
	// contains filtered or unexported fields
}

type MigrationBuilder added in v0.2.1

type MigrationBuilder struct {
	// contains filtered or unexported fields
}

func (*MigrationBuilder) Draft added in v0.2.1

func (b *MigrationBuilder) Draft(id mvpm.MigrationID, name string, handler func(rc *RC))

func (*MigrationBuilder) Run added in v0.2.1

func (b *MigrationBuilder) Run(id mvpm.MigrationID, name string, handler func(rc *RC))

func (*MigrationBuilder) Stale added in v0.2.1

func (b *MigrationBuilder) Stale(id mvpm.MigrationID, name string, handler func(rc *RC))

type Module

type Module struct {
	Name string

	SetupHooks  func(app *App)
	LoadSecrets func(*Settings, Secrets)

	DBSchema  *edb.Schema
	JobSchema *mvpjobs.Schema
	Types     map[mvpm.Type][]string
}

type Mood

type Mood int

func ParseMood

func ParseMood(s string) (Mood, error)

func (*Mood) DecodeMsgpack added in v0.2.2

func (v *Mood) DecodeMsgpack(dec *msgpack.Decoder) error

func (Mood) EncodeMsgpack added in v0.2.2

func (v Mood) EncodeMsgpack(enc *msgpack.Encoder) error

func (Mood) MarshalText added in v0.2.2

func (v Mood) MarshalText() ([]byte, error)

func (Mood) String

func (v Mood) String() string

func (*Mood) UnmarshalText added in v0.2.2

func (v *Mood) UnmarshalText(b []byte) error

type Msg

type Msg struct {
	Text     string `json:"t,omitempty"`
	Link     string `json:"l,omitempty"`
	LinkText string `json:"lt,omitempty"`
	Mood     Mood   `json:"m,omitempty"`
}

func DecodeMsg added in v0.2.2

func DecodeMsg(raw string) *Msg

func RawFailureMsg added in v0.2.2

func RawFailureMsg(text string) *Msg

func RawNeutralMsg added in v0.2.2

func RawNeutralMsg(text string) *Msg

func RawSubtleMsg added in v0.2.2

func RawSubtleMsg(text string) *Msg

func RawSuccessMsg added in v0.2.2

func RawSuccessMsg(text string) *Msg

func (*Msg) Encode added in v0.2.2

func (msg *Msg) Encode() string

func (*Msg) Failure

func (msg *Msg) Failure() bool

func (*Msg) Success

func (msg *Msg) Success() bool

type Panic added in v0.2.2

type Panic struct {
	// contains filtered or unexported fields
}

func (*Panic) Error added in v0.2.2

func (p *Panic) Error() string

func (*Panic) Unwrap added in v0.2.2

func (p *Panic) Unwrap() error

type PathParamsMapAny

type PathParamsMapAny map[string]any

type PathParamsMapStr added in v0.2.1

type PathParamsMapStr map[string]string

type RC

type RC struct {
	RequestID string
	Start     time.Time // ACTUAL time of request start

	RealIPStr string
	RealHost  string
	RealTLS   bool

	Route      *Route
	Request    bunrouter.Request
	RespWriter http.ResponseWriter
	Flash      *Flash

	SetCookies []*http.Cookie

	RateLimitPreset RateLimitPreset
	RateLimitKey    string
	// contains filtered or unexported fields
}

RC stands for Request Context, and holds all the things we want to associate with a request.

RC carries:

  1. Transaction
  2. Authentication
  3. context.Context
  4. Logging context
  5. Current HTTP request info
  6. HTTP response side-channel bits (cookies at the moment)
  7. Matched route or call info
  8. Timing information
  9. Anything else that middleware needs to read or write

Why this instead of stuffing things into context.Context? Mostly because it's more explicit, typed, doesn't require O(N) lookup to have N values, and doesn't require an allocation per value. If anything, you'd put *RC into context.Context, not individual values, so you'd need this type anyway.

RC doesn't just wrap HTTP requests; it is also used by jobs and one-off code. Similar to context.Context, it's a nice way to propagate transactions, authentication and other stuff throughout the routing/call handling machinery without every function knowing about and dealing with individual values.

This type also carries any data you want to communicate between middleware and HTTP handlers. At the very least, it carries concrete authentication data, but also things like tenant ref in multi-tenant applications, etc. As one example, we want to be able to implement Rails-like “flash” middleware, so RC needs to carry input/output cookies AND any resulting flash data.

As such, this type is meant to be customized for a particular application.

func NewHTTPRC

func NewHTTPRC(app *App, w http.ResponseWriter, r bunrouter.Request) *RC

func NewRC

func NewRC(ctx context.Context, app *App, requestID string) *RC

func (*RC) ActorRef

func (rc *RC) ActorRef() mvpm.Ref

func (*RC) App

func (rc *RC) App() *App

func (*RC) AppendLogPrefix

func (rc *RC) AppendLogPrefix(buf *bytes.Buffer)

func (*RC) AppendLogSuffix

func (rc *RC) AppendLogSuffix(buf *bytes.Buffer)

func (*RC) Auth

func (rc *RC) Auth() Auth

func (*RC) BaseApp

func (rc *RC) BaseApp() *App

func (*RC) BaseRC

func (rc *RC) BaseRC() *RC

func (*RC) Close

func (rc *RC) Close()

func (*RC) DBTx

func (rc *RC) DBTx() *edb.Tx

func (*RC) Deadline

func (rc *RC) Deadline() (deadline time.Time, ok bool)

func (*RC) DefaultPathParams

func (rc *RC) DefaultPathParams() PathParamsMapStr

func (*RC) DeleteAuthCookie

func (rc *RC) DeleteAuthCookie()

func (*RC) Done

func (rc *RC) Done() <-chan struct{}

func (*RC) DoneReading added in v0.2.2

func (rc *RC) DoneReading()

func (*RC) Err

func (rc *RC) Err() error

func (*RC) Fail

func (rc *RC) Fail(err error)

func (*RC) FileURL

func (rc *RC) FileURL(resourceURI string, opt mvpm.URLOption) string

func (*RC) HandleForm

func (rc *RC) HandleForm(form *forms.Form) bool

func (*RC) InTx added in v0.2.2

func (rc *RC) InTx(affinity mvpm.StoreAffinity, f func() error) error

func (*RC) IsLoggedIn

func (rc *RC) IsLoggedIn() bool

func (*RC) LogTo

func (rc *RC) LogTo(buf *strings.Builder)

func (*RC) Logf

func (rc *RC) Logf(format string, args ...any)

func (*RC) MustRead added in v0.2.2

func (rc *RC) MustRead(f func())

func (*RC) MustWrite added in v0.2.2

func (rc *RC) MustWrite(f func())

func (*RC) NewID added in v0.2.2

func (rc *RC) NewID() flake.ID

func (*RC) Now

func (rc *RC) Now() time.Time

func (*RC) ReadFile

func (rc *RC) ReadFile(resourceURI string) ([]byte, error)

func (*RC) Redirect

func (rc *RC) Redirect(name string, extras ...any) *Redirect

func (*RC) RedirectBack added in v0.2.1

func (rc *RC) RedirectBack() *Redirect

func (*RC) RefreshNowTime added in v0.2.2

func (rc *RC) RefreshNowTime()

func (*RC) RouteName added in v0.2.2

func (rc *RC) RouteName() string

func (*RC) SendSSE

func (rc *RC) SendSSE(msg *sse.Msg)

func (*RC) SendSSETurboStream added in v0.2.2

func (rc *RC) SendSSETurboStream(id uint64, f func(stream *hotwired.Stream))

func (*RC) SessionID

func (rc *RC) SessionID() flake.ID

func (*RC) SetAuthCookie

func (rc *RC) SetAuthCookie(token string, validity time.Duration)

func (*RC) SetAuthUsingCookie

func (rc *RC) SetAuthUsingCookie(auth Auth)

func (*RC) SetCookie

func (rc *RC) SetCookie(cookie *http.Cookie)

func (*RC) TryRead added in v0.2.2

func (rc *RC) TryRead(f func() error) error

func (*RC) TryWrite added in v0.2.2

func (rc *RC) TryWrite(f func() error) error

func (*RC) Value

func (rc *RC) Value(key any) any

type RCish added in v0.2.1

type RCish interface {
	BaseRC() *RC
	BaseApp() *App
	DBTx() *edb.Tx
	Now() time.Time
	RefreshNowTime()

	IsLoggedIn() bool
	SessionID() flake.ID
	ActorRef() mvpm.Ref
	Auth() Auth

	InTx(affinity mvpm.StoreAffinity, f func() error) error
	TryRead(f func() error) error
	TryWrite(f func() error) error
	MustRead(f func())
	MustWrite(f func())
	DoneReading()

	flogger.Context
}

type RateLimitGranularity

type RateLimitGranularity string

type RateLimitPreset

type RateLimitPreset string

type RateLimitSettings

type RateLimitSettings struct {
	PerSec rate.Limit
	Burst  int
}

type RateLimiter

type RateLimiter struct {
	Preset   RateLimitPreset
	Settings RateLimitSettings
	// contains filtered or unexported fields
}

func (*RateLimiter) Limiter

func (limiter *RateLimiter) Limiter(key string) *rate.Limiter

type RawOutput

type RawOutput struct {
	Data        []byte
	ContentType string
	Header      http.Header
}

type Redirect

type Redirect struct {
	Path       string
	StatusCode int
	Values     url.Values
	RouteName  string
	Flash      *Flash
}

func (*Redirect) EffectivePath added in v0.2.2

func (redir *Redirect) EffectivePath() string

func (*Redirect) EffectiveStatusCode added in v0.2.2

func (redir *Redirect) EffectiveStatusCode() int

func (*Redirect) Permanent

func (redir *Redirect) Permanent() *Redirect

Permanent uses 308 Permanent Redirect for this redirect. Same HTTP method will be used for the redirected request.

Note that there is no permanent redirection code that is guaranteed to always use GET. 301 Moved Permanently may or may not do that and is not recommended.

func (*Redirect) SameMethod

func (redir *Redirect) SameMethod() *Redirect

SameMethod uses 307 Temporary Redirect for this redirect. Same HTTP method will be used for the redirected request, unlike the default 303 See Other response which always redirects with a GET.

func (*Redirect) WithFlash added in v0.2.2

func (redir *Redirect) WithFlash(flash *Flash) *Redirect

func (*Redirect) WithValue added in v0.2.2

func (redir *Redirect) WithValue(key, value string) *Redirect

func (*Redirect) WithValues added in v0.2.2

func (redir *Redirect) WithValues(values url.Values) *Redirect

type RenderData

type RenderData struct {
	Data any
	Args map[string]any
	*ViewData
}

func (*RenderData) ArgsByPrefix

func (d *RenderData) ArgsByPrefix(prefix string) map[string]any

func (*RenderData) Bind

func (d *RenderData) Bind(data any, args ...any) *RenderData

func (*RenderData) HTMLSafeString

func (d *RenderData) HTMLSafeString(name string) (template.HTML, bool)

func (*RenderData) MapSA

func (d *RenderData) MapSA(name string) (map[string]any, bool)

func (*RenderData) PopHTMLSafeString

func (d *RenderData) PopHTMLSafeString(name string) (template.HTML, bool)

func (*RenderData) PopMapSA

func (d *RenderData) PopMapSA(name string) (map[string]any, bool)

func (*RenderData) PopString

func (d *RenderData) PopString(name string) (string, bool)

func (*RenderData) PopValue added in v0.2.1

func (d *RenderData) PopValue(name string) (any, bool)

func (*RenderData) String

func (d *RenderData) String(name string) (string, bool)

func (*RenderData) Value

func (d *RenderData) Value(name string) (any, bool)

type RenderingError added in v0.2.2

type RenderingError struct {
	Kind     string
	Template string
	Cause    error
}

func (*RenderingError) Error added in v0.2.2

func (err *RenderingError) Error() string

func (*RenderingError) SkipStackOnPanic added in v0.2.2

func (err *RenderingError) SkipStackOnPanic() bool

type ResponseHandled

type ResponseHandled struct{}

type Route

type Route struct {
	// contains filtered or unexported fields
}

func (*Route) BodyParamNames added in v0.2.1

func (r *Route) BodyParamNames() []string

func (*Route) Description

func (r *Route) Description() string

Description returns "callName method path"

func (*Route) IsIdempotent

func (r *Route) IsIdempotent() bool

IsIdempotent whether the route is idempotent, which generally means the GET method, although some GET routes might be manually marked as non-idempotent via Route.Mutator().

func (*Route) Method

func (r *Route) Method() string

Method returns the HTTP method.

func (*Route) Mutator

func (route *Route) Mutator()

func (*Route) OnError

func (c *Route) OnError(f ErrHandler)

func (*Route) Path

func (r *Route) Path() string

RouteName returns the HTTP method.

func (*Route) PathParams

func (r *Route) PathParams() []string

func (*Route) RateLimit

func (c *Route) RateLimit(preset RateLimitPreset)

func (*Route) RouteName

func (r *Route) RouteName() string

RouteName returns the name passed to RouteBuilder.Route.

func (*Route) Use

func (c *Route) Use(middleware any)

Use adds the given middleware to all future routes.

func (*Route) UseIn

func (c *Route) UseIn(slot string, middleware any)

UseIn adds the given middleware under the given slot. If the slot is already occupied, overrides the middleware in the given slot (so that you, for example, can define a default authentication or CORS middleware, but override it for certain groups or routes). Use nil as middleware to override with nothing, or to reserve a slot position without assigning middlware yet.

type RouteBuilder

type RouteBuilder struct {
	// contains filtered or unexported fields
}

RouteBuilder helps to define named routes, and holds the path and middleware.

func (*RouteBuilder) Func added in v0.2.1

func (g *RouteBuilder) Func(f any, options ...RouteOption) *Route

func (*RouteBuilder) Group

func (g *RouteBuilder) Group(path string, f func(b *RouteBuilder))

group defines a group of routes with a common prefix and/or middleware.

func (*RouteBuilder) OnError

func (c *RouteBuilder) OnError(f ErrHandler)

func (*RouteBuilder) RateLimit

func (c *RouteBuilder) RateLimit(preset RateLimitPreset)

func (*RouteBuilder) Raw

func (g *RouteBuilder) Raw() *bunrouter.Group

func (*RouteBuilder) Route

func (g *RouteBuilder) Route(routeName string, methodAndPath string, f any, options ...RouteOption) *Route

Route defines a named route. methodAndPath are space-separated.

The handler function must have func(rc *RC, in *SomeStruct) (output, error) signature, where output can be anything, but must be something that app.writeResponse accepts.

func (*RouteBuilder) RouteForm added in v0.2.2

func (g *RouteBuilder) RouteForm(routeName string, path string, f any, options ...RouteOption) *Route

func (*RouteBuilder) Static

func (g *RouteBuilder) Static(path string)

func (*RouteBuilder) Use

func (c *RouteBuilder) Use(middleware any)

Use adds the given middleware to all future routes.

func (*RouteBuilder) UseIn

func (c *RouteBuilder) UseIn(slot string, middleware any)

UseIn adds the given middleware under the given slot. If the slot is already occupied, overrides the middleware in the given slot (so that you, for example, can define a default authentication or CORS middleware, but override it for certain groups or routes). Use nil as middleware to override with nothing, or to reserve a slot position without assigning middlware yet.

type RouteFlagOption added in v0.2.2

type RouteFlagOption int
const (
	ReadOnly RouteFlagOption = 1 + iota
	Mutator
	IdempotentMutator
)

type RouteOption

type RouteOption = any

type Router

type Router interface {
	Use(middleware any)
	UseIn(slot string, middleware any)
	OnError(f ErrHandler)
	RateLimit(preset RateLimitPreset)
}

type Secrets

type Secrets map[string]string

func (Secrets) Optional

func (secrets Secrets) Optional(name string, val interface{ Set(string) error }) bool

func (Secrets) OptionalNamedKeySet

func (secrets Secrets) OptionalNamedKeySet(name string, keys *mvpm.NamedKeySet, minKeyLen, maxKeyLen int) bool

func (Secrets) Required

func (secrets Secrets) Required(name string, val interface{ Set(string) error })

func (Secrets) RequiredNamedKeySet

func (secrets Secrets) RequiredNamedKeySet(name string, keys *mvpm.NamedKeySet, minKeyLen, maxKeyLen int)

type Settings

type Settings struct {
	Env           string
	Configuration *Configuration

	// Configuration options
	LocalOverridesFile string
	AutoEncryptSecrets bool
	KeyringFile        string

	// HTTP server options
	BindAddr             string
	BindPort             int
	ForwardingProxyCIDRs string

	// job options
	WorkerCount           int
	EphemeralWorkerCount  int
	EphemeralQueueMaxSize int

	// app options
	AppName                  string // user-visible app name
	AppID                    string // unchangeable internal name for various identification purposes
	BaseURL                  string
	RateLimits               map[RateLimitPreset]map[RateLimitGranularity]RateLimitSettings
	MaxRateLimitRequestDelay jsonext.Duration
	AppBehaviors

	DataDir string

	VerboseDB bool

	Postmark                     postmark.Credentials
	PostmarkDefaultMessageStream string
	EmailDefaultFrom             string
	EmailDefaultLayout           string

	JWTIssuers []string // Issuer and Audience for this app's tokens
}

func LoadConfig

func LoadConfig(ge *Configuration, env string, installHook func(*Settings)) *Settings

type Site

type Site struct {
	// contains filtered or unexported fields
}

func NewSite

func NewSite(id string) *Site

type SiteData

type SiteData struct {
	AppName string
}

type SkipStackOnPanic added in v0.2.2

type SkipStackOnPanic interface {
	SkipStackOnPanic() bool
}

type TokenDecoding

type TokenDecoding struct {
	Now    time.Time
	Token  jwt.Token
	Claims jwt.Claims
	KeyID  string
	Issuer string
	// contains filtered or unexported fields
}

func (*TokenDecoding) DecodeHS256

func (c *TokenDecoding) DecodeHS256(key []byte) error

func (*TokenDecoding) SetAuth

func (c *TokenDecoding) SetAuth(rc *RC, auth Auth)

type URLGen

type URLGen struct {
	url.URL
	Absolute    bool
	Options     []string
	PathKeys    map[string]string
	QueryParams url.Values
}

type URLOption added in v0.2.2

type URLOption = any

type Val

type Val[T any] struct {
	// contains filtered or unexported fields
}

func Value

func Value[T any](name string, opts ...func(val *Val[T])) *Val[T]

func (*Val[T]) Get

func (val *Val[T]) Get(o ExpandableObject) T

func (*Val[T]) Name

func (val *Val[T]) Name() string

func (*Val[T]) Set

func (val *Val[T]) Set(o ExpandableObject, v T)

type ValueSet

type ValueSet []any

type ViewData

type ViewData struct {
	View         string
	Title        string
	TitleVisible bool
	Layout       string
	Data         any
	SemanticPath string
	StatusCode   int
	Flash        *Flash

	ContentType string

	*SiteData
	Route *Route
	App   *App
	RC    any

	// Content is only populated in layouts and contains the rendered content of the page
	Content template.HTML
	// Head is extra HEAD content
	Head template.HTML
	// contains filtered or unexported fields
}

func (*ViewData) BaseRC

func (vd *ViewData) BaseRC() *RC

func (*ViewData) DefaultPathParams

func (vd *ViewData) DefaultPathParams() PathParamsMapStr

func (*ViewData) IsActive

func (vd *ViewData) IsActive(path string) bool

func (*ViewData) LC

func (vd *ViewData) LC() flogger.Context

Directories

Path Synopsis
Package director launches a bunch of components and gets them restarted on failure.
Package director launches a bunch of components and gets them restarted on failure.
Package flake manages IDs very similar to snowflake IDs used by Twitter and others.
Package flake manages IDs very similar to snowflake IDs used by Twitter and others.
Package flogger stands for Fire Logger; keep your naughty thoughts to yourself.
Package flogger stands for Fire Logger; keep your naughty thoughts to yourself.
Package httperrors defines an error wrapper that carries extra data for convenient rendering of errors in HTTP responses, esp.
Package httperrors defines an error wrapper that carries extra data for convenient rendering of errors in HTTP responses, esp.
See https://html.spec.whatwg.org/multipage/server-sent-events.html
See https://html.spec.whatwg.org/multipage/server-sent-events.html

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL