1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +00:00

web + ui: Pass init cookie from server to client

This commit is contained in:
James Hillyerd
2018-12-31 11:46:29 -08:00
parent 91f3e08ce5
commit c57260349b
7 changed files with 122 additions and 12 deletions

View File

@@ -0,0 +1,5 @@
package web
type jsonAppConfig struct {
MonitorVisible bool `json:"monitor-visible"`
}

View File

@@ -31,6 +31,16 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
} }
// cookieHandler injects an HTTP cookie into the response.
func cookieHandler(cookie *http.Cookie, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Debug().Str("module", "web").Str("remote", req.RemoteAddr).Str("proto", req.Proto).
Str("method", req.Method).Str("path", req.RequestURI).Msg("Injecting cookie")
http.SetCookie(w, cookie)
next.ServeHTTP(w, req)
})
}
// fileHandler creates a handler that sends the named file regardless of the requested URL. // fileHandler creates a handler that sends the named file regardless of the requested URL.
func fileHandler(name string) http.Handler { func fileHandler(name string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

View File

@@ -3,10 +3,12 @@ package web
import ( import (
"context" "context"
"encoding/json"
"expvar" "expvar"
"net" "net"
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
"net/url"
"path/filepath" "path/filepath"
"time" "time"
@@ -75,7 +77,8 @@ func Initialize(
fileHandler(filepath.Join(conf.Web.UIDir, "favicon.png"))) fileHandler(filepath.Join(conf.Web.UIDir, "favicon.png")))
// SPA managed paths. // SPA managed paths.
spaHandler := fileHandler(filepath.Join(conf.Web.UIDir, "index.html")) spaHandler := cookieHandler(appConfigCookie(conf.Web),
fileHandler(filepath.Join(conf.Web.UIDir, "index.html")))
Router.Path("/").Handler(spaHandler) Router.Path("/").Handler(spaHandler)
Router.Path("/monitor").Handler(spaHandler) Router.Path("/monitor").Handler(spaHandler)
Router.Path("/status").Handler(spaHandler) Router.Path("/status").Handler(spaHandler)
@@ -126,6 +129,22 @@ func Start(ctx context.Context) {
} }
} }
func appConfigCookie(webConfig config.Web) *http.Cookie {
o := &jsonAppConfig{
MonitorVisible: webConfig.MonitorVisible,
}
b, err := json.Marshal(o)
if err != nil {
log.Error().Str("module", "web").Str("phase", "startup").Err(err).
Msg("Failed to convert app-config to JSON")
}
return &http.Cookie{
Name: "app-config",
Value: url.PathEscape(string(b)),
Path: "/",
}
}
// serve begins serving HTTP requests // serve begins serving HTTP requests
func serve(ctx context.Context) { func serve(ctx context.Context) {
// server.Serve blocks until we close the listener // server.Serve blocks until we close the listener

15
ui/src/Data/AppConfig.elm Normal file
View File

@@ -0,0 +1,15 @@
module Data.AppConfig exposing (AppConfig, decoder)
import Json.Decode as D
import Json.Decode.Pipeline as P
type alias AppConfig =
{ monitorVisible : Bool
}
decoder : D.Decoder AppConfig
decoder =
D.succeed AppConfig
|> P.required "monitor-visible" D.bool

View File

@@ -4,11 +4,11 @@ module Data.Session exposing
, Session , Session
, addRecent , addRecent
, clearFlash , clearFlash
, decodeValueWithDefault
, decoder , decoder
, disableRouting , disableRouting
, enableRouting , enableRouting
, init , init
, initError
, showFlash , showFlash
) )
@@ -54,6 +54,17 @@ init key location persistent =
} }
initError : Nav.Key -> Url -> String -> Session
initError key location error =
{ key = key
, host = location.host
, flash = Just (Flash "Initialization failed" [ ( "Error", error ) ])
, routing = True
, zone = Time.utc
, persistent = Persistent []
}
addRecent : String -> Session -> Session addRecent : String -> Session -> Session
addRecent mailbox session = addRecent mailbox session =
if List.head session.persistent.recentMailboxes == Just mailbox then if List.head session.persistent.recentMailboxes == Just mailbox then
@@ -99,11 +110,6 @@ decoder =
|> optional "recentMailboxes" (D.list D.string) [] |> optional "recentMailboxes" (D.list D.string) []
decodeValueWithDefault : D.Value -> Persistent
decodeValueWithDefault =
D.decodeValue decoder >> Result.withDefault { recentMailboxes = [] }
encode : Persistent -> E.Value encode : Persistent -> E.Value
encode persistent = encode persistent =
E.object E.object

View File

@@ -2,7 +2,8 @@ module Main exposing (main)
import Browser exposing (Document, UrlRequest) import Browser exposing (Document, UrlRequest)
import Browser.Navigation as Nav import Browser.Navigation as Nav
import Data.Session as Session exposing (Session, decoder) import Data.AppConfig as AppConfig exposing (AppConfig)
import Data.Session as Session exposing (Session)
import Html exposing (..) import Html exposing (..)
import Json.Decode as D exposing (Value) import Json.Decode as D exposing (Value)
import Page.Home as Home import Page.Home as Home
@@ -34,11 +35,27 @@ type alias Model =
} }
type alias InitConfig =
{ appConfig : AppConfig
, session : Session.Persistent
}
init : Value -> Url -> Nav.Key -> ( Model, Cmd Msg ) init : Value -> Url -> Nav.Key -> ( Model, Cmd Msg )
init sessionValue location key = init configValue location key =
let let
configDecoder =
D.map2 InitConfig
(D.field "app-config" AppConfig.decoder)
(D.field "session" Session.decoder)
session = session =
Session.init key location (Session.decodeValueWithDefault sessionValue) case D.decodeValue configDecoder configValue of
Ok config ->
Session.init key location config.session
Err error ->
Session.initError key location (D.errorToString error)
( subModel, _ ) = ( subModel, _ ) =
Home.init session Home.init session

View File

@@ -4,10 +4,16 @@ import { Elm } from './Main.elm'
import registerMonitorPorts from './registerMonitor' import registerMonitorPorts from './registerMonitor'
import './renderedHtml' import './renderedHtml'
// Initial configuration from Inbucket server to Elm App.
var flags = {
"app-config": appConfig(),
"session": sessionObject(),
}
// App startup. // App startup.
var app = Elm.Main.init({ var app = Elm.Main.init({
node: document.getElementById('root'), node: document.getElementById('root'),
flags: sessionObject() flags: flags,
}) })
// Message monitor. // Message monitor.
@@ -24,9 +30,24 @@ window.addEventListener("storage", function (event) {
} }
}, false) }, false)
// Decode the JSON value of the app-config cookie, then delete it.
function appConfig() {
var name = "app-config"
var c = getCookie(name)
if (c) {
deleteCookie(name)
return JSON.parse(decodeURIComponent(c))
}
console.warn("Inbucket " + name + " cookie not found, running with defaults.")
return {
"monitor-visible": true,
}
}
// Grab peristent session data out of local storage.
function sessionObject() { function sessionObject() {
var s = localStorage.session
try { try {
var s = localStorage.session
if (s) { if (s) {
return JSON.parse(s) return JSON.parse(s)
} }
@@ -35,3 +56,20 @@ function sessionObject() {
} }
return null return null
} }
function getCookie(cookieName) {
var name = cookieName + "="
var cookies = decodeURIComponent(document.cookie).split(';')
for (var i=0; i<cookies.length; i++) {
var cookie = cookies[i].trim()
if (cookie.indexOf(name) == 0) {
return cookie.substring(name.length, cookie.length)
}
}
return null
}
function deleteCookie(cookieName) {
document.cookie = cookieName +
"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
}