-
Notifications
You must be signed in to change notification settings - Fork 4
/
act.go
91 lines (83 loc) · 2.99 KB
/
act.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Content managed by Project Forge, see [projectforge.md] for details.
package controller
import (
"fmt"
"time"
"github.com/pkg/errors"
"github.com/valyala/fasthttp"
"projectforge.dev/projectforge/app"
"projectforge.dev/projectforge/app/controller/cutil"
"projectforge.dev/projectforge/app/lib/telemetry"
"projectforge.dev/projectforge/app/lib/user"
"projectforge.dev/projectforge/app/site"
)
func Act(key string, rc *fasthttp.RequestCtx, f func(as *app.State, ps *cutil.PageState) (string, error)) {
as := _currentAppState
ps := cutil.LoadPageState(as, rc, key, _currentAppRootLogger)
if allowed, reason := user.Check(string(ps.URI.Path()), ps.Accounts); !allowed {
f = Unauthorized(rc, reason, ps.Accounts)
}
if err := initAppRequest(as, ps); err != nil {
ps.Logger.Warnf("%+v", err)
}
actComplete(key, as, ps, rc, f)
}
func ActSite(key string, rc *fasthttp.RequestCtx, f func(as *app.State, ps *cutil.PageState) (string, error)) {
as := _currentSiteState
ps := cutil.LoadPageState(as, rc, key, _currentSiteRootLogger)
ps.Menu = site.Menu(ps.Context, as, ps.Profile, ps.Accounts)
if allowed, reason := user.Check(string(ps.URI.Path()), ps.Accounts); !allowed {
f = Unauthorized(rc, reason, ps.Accounts)
}
if err := initSiteRequest(as, ps); err != nil {
ps.Logger.Warnf("%+v", err)
}
actComplete(key, as, ps, rc, f)
}
func actComplete(key string, as *app.State, ps *cutil.PageState, rc *fasthttp.RequestCtx, f func(as *app.State, ps *cutil.PageState) (string, error)) {
err := ps.Clean(rc, as)
if err != nil {
ps.Logger.Warnf("error while cleaning request, somehow: %+v", err)
}
status := fasthttp.StatusOK
cutil.WriteCORS(rc)
startNanos := time.Now().UnixNano()
var redir string
ctx, span, logger := telemetry.StartSpan(ps.Context, "controller."+key, ps.Logger)
defer span.Complete()
logger = logger.With("path", string(rc.URI().Path()), "method", ps.Method, "status", status)
ps.Context = ctx
if ps.ForceRedirect == "" || ps.ForceRedirect == string(rc.URI().Path()) {
redir, err = safeRun(f, as, ps)
if err != nil {
redir, err = handleError(key, as, ps, rc, err)
if err != nil {
ps.Logger.Warnf("unable to handle error: %+v", err)
}
}
} else {
redir = ps.ForceRedirect
}
if redir != "" {
rc.Response.Header.Set("Location", redir)
status = fasthttp.StatusFound
rc.SetStatusCode(status)
}
elapsedMillis := float64((time.Now().UnixNano()-startNanos)/int64(time.Microsecond)) / float64(1000)
defer ps.Close()
logger = logger.With("elapsed", elapsedMillis)
logger.Debugf("processed request in [%.3fms] (render: %.3fms)", elapsedMillis, ps.RenderElapsed)
}
func safeRun(f func(as *app.State, ps *cutil.PageState) (string, error), as *app.State, ps *cutil.PageState) (s string, e error) {
defer func() {
if rec := recover(); rec != nil {
if recoverErr, ok := rec.(error); ok {
e = errors.Wrap(recoverErr, "panic")
} else {
e = errors.Errorf("controller encountered panic recovery of type [%T]: %s", rec, fmt.Sprint(rec))
}
}
}()
s, e = f(as, ps)
return
}