123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- package web
- import (
- "context"
- "encoding/json"
- "fmt"
- "log"
- "mime/multipart"
- "net/http"
- "strings"
- h "github.com/theplant/htmlgo"
- )
- var Default = New()
- func Page(pf PageFunc, efs ...interface{}) (p *PageBuilder) {
- p = &PageBuilder{
- b: Default,
- }
- p.pageRenderFunc = pf
- p.RegisterEventFunc("__reload__", reload)
- p.EventFuncs(efs...)
- return
- }
- type PageBuilder struct {
- EventsHub
- b *Builder
- pageRenderFunc PageFunc
- maxFormSize int64
- }
- func (b *Builder) Page(pf PageFunc) (p *PageBuilder) {
- p = Page(pf).Builder(b)
- return
- }
- func (p *PageBuilder) Builder(v *Builder) (r *PageBuilder) {
- p.b = v
- r = p
- return
- }
- func (p *PageBuilder) Wrap(middlewares ...func(in PageFunc) PageFunc) (r *PageBuilder) {
- pf := p.pageRenderFunc
- for _, m := range middlewares {
- pf = m(pf)
- }
- p.pageRenderFunc = pf
- r = p
- return
- }
- func (p *PageBuilder) MaxFormSize(v int64) (r *PageBuilder) {
- p.maxFormSize = v
- r = p
- return
- }
- func (p *PageBuilder) EventFuncs(vs ...interface{}) (r *PageBuilder) {
- p.addMultipleEventFuncs(vs...)
- return p
- }
- func (p *PageBuilder) EventFunc(name string, ef EventFunc) (r *PageBuilder) {
- p.RegisterEventFunc(name, ef)
- return p
- }
- func (p *PageBuilder) MergeHub(hub *EventsHub) (r *PageBuilder) {
- p.EventsHub.eventFuncs = append(hub.eventFuncs, p.EventsHub.eventFuncs...)
- return p
- }
- func (p *PageBuilder) render(
- w http.ResponseWriter,
- r *http.Request,
- c context.Context,
- head *PageInjector,
- ) (pager *PageResponse, body string) {
- if p.pageRenderFunc == nil {
- return
- }
- ctx := MustGetEventContext(c)
- ctx.R = r
- ctx.W = w
- ctx.Injector = head
- pr, err := p.pageRenderFunc(ctx)
- if err != nil {
- panic(err)
- }
- pager = &pr
- if pager.Body == nil {
- panic("page's RenderFunc returns nil body, use pr.Body = root to set it")
- }
- // fmt.Println("eventFuncs count: ", len(p.eventFuncs))
- b, err := pager.Body.MarshalHTML(c)
- if err != nil {
- panic(err)
- }
- body = string(b)
- return
- }
- func (p *PageBuilder) index(w http.ResponseWriter, r *http.Request) {
- var err error
- var head = &PageInjector{}
- ctx := new(EventContext)
- c := WrapEventContext(r.Context(), ctx)
- pr, body := p.render(w, r, c, head)
- if len(pr.PageTitle) > 0 {
- head.Title(pr.PageTitle)
- }
- var resp string
- resp, err = p.b.layoutFunc(r, head, body)
- if err != nil {
- panic(err)
- }
- _, err = fmt.Fprintln(w, resp)
- if err != nil {
- panic(err)
- }
- }
- func (p *PageBuilder) parseForm(r *http.Request) *multipart.Form {
- maxSize := p.maxFormSize
- if maxSize == 0 {
- maxSize = 128 << 20 // 128MB
- }
- err := r.ParseMultipartForm(maxSize)
- if err != nil {
- panic(err)
- }
- return r.MultipartForm
- }
- const EventFuncIDName = "__execute_event__"
- func (p *PageBuilder) executeEvent(w http.ResponseWriter, r *http.Request) {
- ctx := new(EventContext)
- ctx.R = r
- ctx.W = w
- ctx.Injector = &PageInjector{}
- c := WrapEventContext(r.Context(), ctx)
- eventFuncID := r.FormValue(EventFuncIDName)
- // for server side restart and lost all the eventFuncs,
- // but user keep clicking page without refresh page to call p.render to fill up eventFuncs
- // because default added reload
- if len(p.eventFuncs) <= 1 &&
- p.eventFuncById(eventFuncID) == nil &&
- p.b.eventFuncById(eventFuncID) == nil {
- log.Println("Re-render because event funcs gone, might server restarted")
- head := &PageInjector{}
- p.render(w, r, c, head)
- }
- ef := p.eventFuncById(eventFuncID)
- if ef == nil {
- ef = p.b.eventFuncById(eventFuncID)
- }
- if ef == nil {
- log.Printf("event %s not found\n", eventFuncID)
- http.NotFound(w, r)
- return
- }
- er, err := ef(ctx)
- if err != nil {
- panic(err)
- }
- if er.Reload {
- head := &PageInjector{}
- pr, body := p.render(w, r, c, head)
- er.Body = h.RawHTML(body)
- if len(er.PageTitle) == 0 {
- er.PageTitle = pr.PageTitle
- }
- }
- er.Body = h.RawHTML(h.MustString(er.Body, c))
- for _, up := range er.UpdatePortals {
- up.Body = h.RawHTML(h.MustString(up.Body, c))
- }
- err = json.NewEncoder(w).Encode(er)
- if err != nil {
- panic(err)
- }
- }
- func reload(ctx *EventContext) (r EventResponse, err error) {
- r.Reload = true
- return
- }
- func (p *PageBuilder) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if strings.Index(r.URL.String(), EventFuncIDName) >= 0 {
- p.executeEvent(w, r)
- return
- }
- p.index(w, r)
- }
|