Flow split begin
This commit is contained in:
parent
ae00c1534d
commit
f3e17e149f
10 changed files with 286 additions and 157 deletions
|
@ -15,6 +15,13 @@ type cmdOptions interface {
|
|||
Execute() error
|
||||
}
|
||||
|
||||
func AppNamePtr() *string {
|
||||
s := AppName
|
||||
return &s
|
||||
}
|
||||
|
||||
func IntPtr(i int) *int { return &i }
|
||||
|
||||
// RunE is a convenience function for use with cobra.
|
||||
func RunE(c cmdOptions) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
type (
|
||||
// PyTimeStamp is a timestamp that marshals to python-style timestamp strings (long nano).
|
||||
PyTimestamp time.Time
|
||||
ClientID string
|
||||
)
|
||||
|
||||
const PytTimeFormat = "2006-01-02T15:04:05.999999-07:00"
|
||||
|
|
|
@ -24,7 +24,7 @@ var (
|
|||
|
||||
type Authenticator struct {
|
||||
store AuthStore
|
||||
flows FlowStore
|
||||
flows *AuthFlowManager
|
||||
sessions AccessSessionStore
|
||||
providers map[string]provider.AuthProvider
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func (a *Authenticator) InitAuth(s storage.Store) error {
|
|||
a.providers[nProv.ProviderType()] = nProv
|
||||
}
|
||||
|
||||
a.flows = make(FlowStore)
|
||||
a.flows = NewAuthFlowManager()
|
||||
|
||||
a.sessions.init()
|
||||
|
||||
|
@ -91,29 +91,23 @@ func (a *Authenticator) ProvidersHandler(c echo.Context) error {
|
|||
return c.JSON(http.StatusOK, providers)
|
||||
}
|
||||
|
||||
func (a *Authenticator) Check(f *Flow, req *http.Request, rm map[string]interface{}) (provider.ProviderUser, error) {
|
||||
func (a *Authenticator) Check(f *LoginFlow, req *http.Request, rm map[string]interface{}) (provider.ProviderUser, error) {
|
||||
cID, hasCID := rm["client_id"]
|
||||
cIDStr, cidIsStr := cID.(string)
|
||||
if !hasCID || !cidIsStr || cIDStr == "" || cIDStr != string(f.request.ClientID) {
|
||||
if !hasCID || !cidIsStr || cIDStr == "" || cIDStr != string(f.ClientID) {
|
||||
return nil, ErrInvalidAuth
|
||||
}
|
||||
|
||||
for _, h := range f.Handler {
|
||||
if h == nil {
|
||||
return nil, ErrInvalidHandler
|
||||
}
|
||||
p := a.Provider(f.Handler.String())
|
||||
if p == nil {
|
||||
return nil, ErrInvalidAuth
|
||||
}
|
||||
|
||||
p := a.Provider(*h)
|
||||
if p == nil {
|
||||
return nil, ErrInvalidAuth
|
||||
}
|
||||
user, success := p.ValidateCreds(req, rm)
|
||||
|
||||
user, success := p.ValidateCreds(req, rm)
|
||||
|
||||
if success {
|
||||
log.Info().Interface("user", user.UserData()).Msg("Login success")
|
||||
return user, nil
|
||||
}
|
||||
if success {
|
||||
log.Info().Interface("user", user.UserData()).Msg("Login success")
|
||||
return user, nil
|
||||
}
|
||||
|
||||
return nil, ErrInvalidAuth
|
||||
|
|
165
pkg/auth/flow.go
165
pkg/auth/flow.go
|
@ -3,132 +3,67 @@ package auth
|
|||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"dynatron.me/x/blasphem/internal/common"
|
||||
"dynatron.me/x/blasphem/internal/generate"
|
||||
"dynatron.me/x/blasphem/pkg/auth/provider"
|
||||
"dynatron.me/x/blasphem/pkg/flow"
|
||||
)
|
||||
|
||||
type FlowStore map[FlowID]*Flow
|
||||
|
||||
type FlowRequest struct {
|
||||
ClientID ClientID `json:"client_id"`
|
||||
Handler []*string `json:"handler"`
|
||||
RedirectURI string `json:"redirect_uri"`
|
||||
type AuthFlowManager struct {
|
||||
*flow.FlowManager
|
||||
}
|
||||
|
||||
type FlowType string
|
||||
|
||||
const (
|
||||
TypeForm FlowType = "form"
|
||||
TypeCreateEntry FlowType = "create_entry"
|
||||
)
|
||||
|
||||
type FlowID string
|
||||
type Step string
|
||||
|
||||
const (
|
||||
StepInit Step = "init"
|
||||
)
|
||||
|
||||
type Flow struct {
|
||||
Type FlowType `json:"type"`
|
||||
ID FlowID `json:"flow_id"`
|
||||
Handler []*string `json:"handler"`
|
||||
StepID *Step `json:"step_id,omitempty"`
|
||||
Schema []provider.FlowSchemaItem `json:"data_schema"`
|
||||
Errors interface{} `json:"errors"`
|
||||
DescPlace *string `json:"description_placeholders"`
|
||||
LastStep *string `json:"last_step"`
|
||||
|
||||
request *FlowRequest
|
||||
ctime time.Time
|
||||
type LoginFlow struct {
|
||||
flow.FlowHandlerBase
|
||||
}
|
||||
|
||||
func (f *Flow) touch() {
|
||||
f.ctime = time.Now()
|
||||
func NewAuthFlowManager() *AuthFlowManager {
|
||||
return &AuthFlowManager{FlowManager: flow.NewFlowManager()}
|
||||
}
|
||||
|
||||
func (fs FlowStore) register(f *Flow) {
|
||||
fs.cull()
|
||||
fs[f.ID] = f
|
||||
}
|
||||
|
||||
func (fs FlowStore) Remove(f *Flow) {
|
||||
delete(fs, f.ID)
|
||||
}
|
||||
|
||||
const cullAge = time.Minute * 30
|
||||
|
||||
func (fs FlowStore) cull() {
|
||||
for k, v := range fs {
|
||||
if time.Now().Sub(v.ctime) > cullAge {
|
||||
delete(fs, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fs FlowStore) Get(id FlowID) *Flow {
|
||||
f, ok := fs[id]
|
||||
if ok {
|
||||
return f
|
||||
func (afm *AuthFlowManager) NewLoginFlow(f *flow.FlowRequest, prov provider.AuthProvider) *LoginFlow {
|
||||
lf := &LoginFlow{
|
||||
FlowHandlerBase: flow.NewFlowHandlerBase(f, prov, prov.ProviderType()),
|
||||
}
|
||||
|
||||
return nil
|
||||
afm.Register(lf)
|
||||
|
||||
return lf
|
||||
}
|
||||
|
||||
func (a *Authenticator) NewFlow(r *FlowRequest) *Flow {
|
||||
var sch []provider.FlowSchemaItem
|
||||
func (a *Authenticator) NewFlow(r *flow.FlowRequest) *flow.FlowResult {
|
||||
var prov provider.AuthProvider
|
||||
|
||||
for _, h := range r.Handler {
|
||||
if h == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if hand := a.Provider(*h); hand != nil {
|
||||
sch = hand.FlowSchema()
|
||||
prov = a.Provider(h.String())
|
||||
if prov != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sch == nil {
|
||||
if prov == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
flow := &Flow{
|
||||
Type: TypeForm,
|
||||
ID: FlowID(generate.UUID()),
|
||||
StepID: stepPtr(StepInit),
|
||||
Schema: sch,
|
||||
Handler: r.Handler,
|
||||
Errors: []string{},
|
||||
request: r,
|
||||
}
|
||||
flow.touch()
|
||||
flow := a.flows.NewLoginFlow(r, prov)
|
||||
|
||||
a.flows.register(flow)
|
||||
|
||||
return flow
|
||||
return flow.ShowForm(nil)
|
||||
}
|
||||
|
||||
func stepPtr(s Step) *Step { return &s }
|
||||
|
||||
func (f *Flow) redirect(c echo.Context) {
|
||||
c.Request().Header.Set("Location", f.request.RedirectURI)
|
||||
func (f *LoginFlow) redirect(c echo.Context) {
|
||||
c.Request().Header.Set("Location", f.RedirectURI)
|
||||
}
|
||||
|
||||
func (f *Flow) progress(a *Authenticator, c echo.Context) error {
|
||||
if f.StepID == nil {
|
||||
c.Logger().Error("stepID is nil")
|
||||
return c.String(http.StatusInternalServerError, "No Step ID")
|
||||
}
|
||||
|
||||
switch *f.StepID {
|
||||
case StepInit:
|
||||
func (f *LoginFlow) progress(a *Authenticator, c echo.Context) error {
|
||||
switch f.Step() {
|
||||
case flow.StepInit:
|
||||
rm := make(map[string]interface{})
|
||||
|
||||
err := c.Bind(&rm)
|
||||
|
@ -136,31 +71,21 @@ func (f *Flow) progress(a *Authenticator, c echo.Context) error {
|
|||
return c.String(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
for _, si := range f.Schema {
|
||||
if si.Required {
|
||||
if _, ok := rm[si.Name]; !ok {
|
||||
return c.String(http.StatusBadRequest, "missing required param "+si.Name)
|
||||
}
|
||||
}
|
||||
err = f.Schema.CheckRequired(rm)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, f.ShowForm([]string{err.Error()}))
|
||||
}
|
||||
|
||||
user, err := a.Check(f, c.Request(), rm)
|
||||
switch err {
|
||||
case nil:
|
||||
var finishedFlow struct {
|
||||
ID FlowID `json:"flow_id"`
|
||||
Handler []*string `json:"handler"`
|
||||
Result AccessTokenID `json:"result"`
|
||||
Title string `json:"title"`
|
||||
Type FlowType `json:"type"`
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
finishedFlow := flow.FlowResult{}
|
||||
a.flows.Remove(f)
|
||||
copier.Copy(&finishedFlow, f)
|
||||
finishedFlow.Type = TypeCreateEntry
|
||||
finishedFlow.Title = common.AppName
|
||||
finishedFlow.Version = 1
|
||||
finishedFlow.Result = a.NewAccessToken(c.Request(), user, f)
|
||||
finishedFlow.Type = flow.TypeCreateEntry
|
||||
finishedFlow.Title = common.AppNamePtr()
|
||||
finishedFlow.Version = common.IntPtr(1)
|
||||
finishedFlow.Result = a.NewAccessToken(c.Request(), user)
|
||||
|
||||
f.redirect(c)
|
||||
|
||||
|
@ -170,10 +95,10 @@ func (f *Flow) progress(a *Authenticator, c echo.Context) error {
|
|||
case ErrInvalidAuth:
|
||||
fallthrough
|
||||
default:
|
||||
f.Errors = map[string]interface{}{
|
||||
fr := f.ShowForm(map[string]interface{}{
|
||||
"base": "invalid_auth",
|
||||
}
|
||||
return c.JSON(http.StatusOK, f)
|
||||
})
|
||||
return c.JSON(http.StatusOK, fr)
|
||||
}
|
||||
default:
|
||||
return c.String(http.StatusBadRequest, "unknown flow step")
|
||||
|
@ -181,13 +106,13 @@ func (f *Flow) progress(a *Authenticator, c echo.Context) error {
|
|||
}
|
||||
|
||||
func (a *Authenticator) LoginFlowDeleteHandler(c echo.Context) error {
|
||||
flowID := c.Param("flow_id")
|
||||
flowID := flow.FlowID(c.Param("flow_id"))
|
||||
|
||||
if flowID == "" {
|
||||
return c.String(http.StatusBadRequest, "empty flow ID")
|
||||
}
|
||||
|
||||
delete(a.flows, FlowID(flowID))
|
||||
a.flows.Delete(flowID)
|
||||
|
||||
return c.String(http.StatusOK, "deleted")
|
||||
}
|
||||
|
@ -202,7 +127,7 @@ func setJSON(c echo.Context) {
|
|||
func (a *Authenticator) BeginLoginFlowHandler(c echo.Context) error {
|
||||
setJSON(c)
|
||||
|
||||
var flowReq FlowRequest
|
||||
var flowReq flow.FlowRequest
|
||||
err := c.Bind(&flowReq)
|
||||
if err != nil {
|
||||
return c.String(http.StatusBadRequest, err.Error())
|
||||
|
@ -222,16 +147,10 @@ func (a *Authenticator) LoginFlowHandler(c echo.Context) error {
|
|||
|
||||
flowID := c.Param("flow_id")
|
||||
|
||||
flow := a.flows.Get(FlowID(flowID))
|
||||
flow := a.flows.Get(flow.FlowID(flowID))
|
||||
if flow == nil {
|
||||
return c.String(http.StatusNotFound, "no such flow")
|
||||
}
|
||||
|
||||
if time.Now().Sub(flow.ctime) > cullAge {
|
||||
a.flows.Remove(flow)
|
||||
|
||||
return c.String(http.StatusGone, "flow timed out")
|
||||
}
|
||||
|
||||
return flow.progress(a, c)
|
||||
return flow.(*LoginFlow).progress(a, c)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"dynatron.me/x/blasphem/pkg/auth/provider"
|
||||
"dynatron.me/x/blasphem/pkg/flow"
|
||||
"dynatron.me/x/blasphem/pkg/storage"
|
||||
)
|
||||
|
||||
|
@ -44,7 +45,7 @@ func (h *HAUser) ProviderUserData() interface{} { return h.UserData() }
|
|||
type HomeAssistantProvider struct {
|
||||
provider.AuthProviderBase `json:"-"`
|
||||
Users []HAUser `json:"users"`
|
||||
userMap map[string]*HAUser
|
||||
userMap map[string]*HAUser
|
||||
}
|
||||
|
||||
func NewHAProvider(s storage.Store) (provider.AuthProvider, error) {
|
||||
|
@ -126,8 +127,8 @@ func (hap *HomeAssistantProvider) NewCredData() interface{} {
|
|||
return &HAUser{}
|
||||
}
|
||||
|
||||
func (hap *HomeAssistantProvider) FlowSchema() []provider.FlowSchemaItem {
|
||||
return []provider.FlowSchemaItem{
|
||||
func (hap *HomeAssistantProvider) FlowSchema() flow.FlowSchema {
|
||||
return []flow.FlowSchemaItem{
|
||||
{
|
||||
Type: "string",
|
||||
Name: "username",
|
||||
|
|
|
@ -3,6 +3,7 @@ package provider
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"dynatron.me/x/blasphem/pkg/flow"
|
||||
"dynatron.me/x/blasphem/pkg/storage"
|
||||
)
|
||||
|
||||
|
@ -13,7 +14,7 @@ var Providers = make(map[string]Constructor)
|
|||
type AuthProvider interface { // TODO: this should include stepping
|
||||
AuthProviderMetadata
|
||||
ProviderBase() AuthProviderBase
|
||||
FlowSchema() []FlowSchemaItem
|
||||
FlowSchema() flow.FlowSchema
|
||||
NewCredData() interface{}
|
||||
ValidateCreds(r *http.Request, reqMap map[string]interface{}) (user ProviderUser, success bool)
|
||||
Lookup(ProviderUser) ProviderUser
|
||||
|
@ -24,7 +25,7 @@ func Register(providerName string, f func(storage.Store) (AuthProvider, error))
|
|||
}
|
||||
|
||||
type ProviderUser interface {
|
||||
// TODO: make sure this is sane with all the ProviderUser and UserData type stuf
|
||||
// TODO: make sure this is sane with all the ProviderUser and UserData type stuff
|
||||
UserData() ProviderUser
|
||||
}
|
||||
|
||||
|
@ -44,9 +45,3 @@ func (bp *AuthProviderBase) ProviderName() string { return bp.Name }
|
|||
func (bp *AuthProviderBase) ProviderID() *string { return bp.ID }
|
||||
func (bp *AuthProviderBase) ProviderType() string { return bp.Type }
|
||||
func (bp *AuthProviderBase) ProviderBase() AuthProviderBase { return *bp }
|
||||
|
||||
type FlowSchemaItem struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Required bool `json:"required"`
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"dynatron.me/x/blasphem/pkg/auth/provider"
|
||||
"dynatron.me/x/blasphem/pkg/flow"
|
||||
"dynatron.me/x/blasphem/pkg/storage"
|
||||
)
|
||||
|
||||
|
@ -61,8 +62,8 @@ func (hap *TrustedNetworksProvider) NewCredData() interface{} {
|
|||
return &UserData{}
|
||||
}
|
||||
|
||||
func (hap *TrustedNetworksProvider) FlowSchema() []provider.FlowSchemaItem {
|
||||
return []provider.FlowSchemaItem{
|
||||
func (hap *TrustedNetworksProvider) FlowSchema() flow.FlowSchema {
|
||||
return []flow.FlowSchemaItem{
|
||||
{
|
||||
Type: "string",
|
||||
Name: "username",
|
||||
|
|
|
@ -131,7 +131,7 @@ func (a *Authenticator) verifyAndGetCredential(tr *TokenRequest, r *http.Request
|
|||
|
||||
const defaultExpiration = 15 * time.Minute
|
||||
|
||||
func (a *Authenticator) NewAccessToken(r *http.Request, user provider.ProviderUser, f *Flow) AccessTokenID {
|
||||
func (a *Authenticator) NewAccessToken(r *http.Request, user provider.ProviderUser) AccessTokenID {
|
||||
id := AccessTokenID(generate.UUID())
|
||||
now := time.Now()
|
||||
|
||||
|
@ -156,7 +156,7 @@ const (
|
|||
GTRefreshToken GrantType = "refresh_token"
|
||||
)
|
||||
|
||||
type ClientID string
|
||||
type ClientID common.ClientID
|
||||
|
||||
func (c *ClientID) IsValid() bool {
|
||||
// TODO: || !indieauth.VerifyClientID(rq.ClientID)?
|
||||
|
|
|
@ -23,7 +23,7 @@ type authStore struct {
|
|||
Credentials []Credential `json:"credentials"`
|
||||
Refresh []RefreshToken `json:"refresh_tokens"`
|
||||
|
||||
userMap map[UserID]*User
|
||||
userMap map[UserID]*User
|
||||
providerUsers map[provider.ProviderUser]*Credential
|
||||
}
|
||||
|
||||
|
|
211
pkg/flow/flow.go
Normal file
211
pkg/flow/flow.go
Normal file
|
@ -0,0 +1,211 @@
|
|||
// flow is the data entry flow.
|
||||
package flow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"dynatron.me/x/blasphem/internal/common"
|
||||
"dynatron.me/x/blasphem/internal/generate"
|
||||
)
|
||||
|
||||
type (
|
||||
FlowResultType string
|
||||
FlowID string
|
||||
Step string
|
||||
HandlerKey string
|
||||
Errors interface{}
|
||||
|
||||
Context interface{}
|
||||
|
||||
FlowStore map[FlowID]Handler
|
||||
|
||||
FlowManager struct {
|
||||
flows FlowStore
|
||||
}
|
||||
|
||||
FlowResult struct {
|
||||
Type FlowResultType `json:"type"`
|
||||
ID FlowID `json:"flow_id"`
|
||||
Handler []*HandlerKey `json:"handler"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
StepID *Step `json:"step_id,omitempty"`
|
||||
Schema []FlowSchemaItem `json:"data_schema"`
|
||||
Extra *string `json:"extra,omitempty"`
|
||||
Required *bool `json:"required,omitempty"`
|
||||
Errors interface{} `json:"errors"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
DescPlace *string `json:"description_placeholders"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
Reason *string `json:"reason,omitempty"`
|
||||
Context *string `json:"context,omitempty"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
LastStep *string `json:"last_step"`
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
Version *int `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
FlowSchemaItem struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Required bool `json:"required"`
|
||||
}
|
||||
|
||||
FlowSchema []FlowSchemaItem
|
||||
|
||||
FlowRequest struct {
|
||||
ClientID common.ClientID `json:"client_id"`
|
||||
Handler []*HandlerKey `json:"handler"`
|
||||
RedirectURI string `json:"redirect_uri"`
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
Schemer interface {
|
||||
FlowSchema() FlowSchema
|
||||
}
|
||||
|
||||
Handler interface {
|
||||
Base() FlowHandlerBase
|
||||
FlowID() FlowID
|
||||
flowCtime() time.Time
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
StepInit Step = "init"
|
||||
)
|
||||
|
||||
func (fs *FlowSchema) CheckRequired(rm map[string]interface{}) error {
|
||||
for _, si := range *fs {
|
||||
if si.Required {
|
||||
if _, ok := rm[si.Name]; !ok {
|
||||
return fmt.Errorf("missing required param %s", si.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFlowManager() *FlowManager {
|
||||
return &FlowManager{
|
||||
flows: make(FlowStore),
|
||||
}
|
||||
}
|
||||
|
||||
func stepPtr(s Step) *Step { return &s }
|
||||
|
||||
type FlowHandlerBase struct {
|
||||
ID FlowID // ID is the FlowID
|
||||
Handler HandlerKey // Handler key
|
||||
Context Context // flow Context
|
||||
ClientID common.ClientID
|
||||
RedirectURI string
|
||||
Schema FlowSchema
|
||||
|
||||
// curStep is the current step set by the flow manager
|
||||
curStep Step
|
||||
|
||||
ctime time.Time
|
||||
}
|
||||
|
||||
func (f *FlowHandlerBase) Step() Step { return f.curStep }
|
||||
|
||||
func (f *FlowHandlerBase) Base() FlowHandlerBase { return *f }
|
||||
|
||||
func (f *FlowHandlerBase) FlowID() FlowID {
|
||||
return f.ID
|
||||
}
|
||||
|
||||
func (f *FlowHandlerBase) flowCtime() time.Time { return f.ctime }
|
||||
|
||||
func NewFlowHandlerBase(f *FlowRequest, sch Schemer, hand string) FlowHandlerBase {
|
||||
return FlowHandlerBase{
|
||||
ID: FlowID(generate.UUID()),
|
||||
Handler: HandlerKey(hand),
|
||||
ClientID: f.ClientID,
|
||||
RedirectURI: f.RedirectURI,
|
||||
Schema: sch.FlowSchema(),
|
||||
|
||||
curStep: StepInit,
|
||||
ctime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (hk *HandlerKey) String() string {
|
||||
return string(*hk)
|
||||
}
|
||||
|
||||
func (fm *FlowHandlerBase) Handlers() []*HandlerKey {
|
||||
return []*HandlerKey{&fm.Handler, nil}
|
||||
}
|
||||
|
||||
func resultErrs(e Errors) Errors {
|
||||
if e == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (fm *FlowHandlerBase) ShowForm(errs Errors) *FlowResult {
|
||||
res := &FlowResult{
|
||||
Type: TypeForm,
|
||||
ID: fm.ID,
|
||||
StepID: stepPtr(fm.curStep),
|
||||
Schema: fm.Schema,
|
||||
Handler: fm.Handlers(),
|
||||
Errors: resultErrs(errs),
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (fm *FlowManager) Delete(id FlowID) {
|
||||
delete(fm.flows, id)
|
||||
}
|
||||
|
||||
const (
|
||||
TypeForm FlowResultType = "form"
|
||||
TypeCreateEntry FlowResultType = "create_entry"
|
||||
TypeAbort FlowResultType = "abort"
|
||||
TypeExternalStep FlowResultType = "external"
|
||||
TypeExternalStepDone FlowResultType = "external_done"
|
||||
TypeShowProgress FlowResultType = "progress"
|
||||
TypeShowProgressDone FlowResultType = "progress_done"
|
||||
TypeMenu FlowResultType = "menu"
|
||||
)
|
||||
|
||||
func (f *FlowHandlerBase) touch() {
|
||||
f.ctime = time.Now()
|
||||
}
|
||||
|
||||
func (fm *FlowManager) Register(f Handler) {
|
||||
fm.flows.cull()
|
||||
fm.flows[f.FlowID()] = f
|
||||
}
|
||||
|
||||
func (fs *FlowManager) Remove(f Handler) {
|
||||
delete(fs.flows, f.FlowID())
|
||||
}
|
||||
|
||||
const cullAge = time.Minute * 30
|
||||
|
||||
func (fs FlowStore) cull() {
|
||||
for k, v := range fs {
|
||||
if time.Now().Sub(v.flowCtime()) > cullAge {
|
||||
delete(fs, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FlowManager) Get(id FlowID) Handler {
|
||||
f, ok := fs.flows[id]
|
||||
if ok {
|
||||
return f
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue