mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
web + ui: Pass init cookie from server to client
This commit is contained in:
5
pkg/server/web/app_json.go
Normal file
5
pkg/server/web/app_json.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package web
|
||||
|
||||
type jsonAppConfig struct {
|
||||
MonitorVisible bool `json:"monitor-visible"`
|
||||
}
|
||||
@@ -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.
|
||||
func fileHandler(name string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
@@ -3,10 +3,12 @@ package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"expvar"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
@@ -75,7 +77,8 @@ func Initialize(
|
||||
fileHandler(filepath.Join(conf.Web.UIDir, "favicon.png")))
|
||||
|
||||
// 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("/monitor").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
|
||||
func serve(ctx context.Context) {
|
||||
// server.Serve blocks until we close the listener
|
||||
|
||||
15
ui/src/Data/AppConfig.elm
Normal file
15
ui/src/Data/AppConfig.elm
Normal 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
|
||||
@@ -4,11 +4,11 @@ module Data.Session exposing
|
||||
, Session
|
||||
, addRecent
|
||||
, clearFlash
|
||||
, decodeValueWithDefault
|
||||
, decoder
|
||||
, disableRouting
|
||||
, enableRouting
|
||||
, init
|
||||
, initError
|
||||
, 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 mailbox session =
|
||||
if List.head session.persistent.recentMailboxes == Just mailbox then
|
||||
@@ -99,11 +110,6 @@ decoder =
|
||||
|> optional "recentMailboxes" (D.list D.string) []
|
||||
|
||||
|
||||
decodeValueWithDefault : D.Value -> Persistent
|
||||
decodeValueWithDefault =
|
||||
D.decodeValue decoder >> Result.withDefault { recentMailboxes = [] }
|
||||
|
||||
|
||||
encode : Persistent -> E.Value
|
||||
encode persistent =
|
||||
E.object
|
||||
|
||||
@@ -2,7 +2,8 @@ module Main exposing (main)
|
||||
|
||||
import Browser exposing (Document, UrlRequest)
|
||||
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 Json.Decode as D exposing (Value)
|
||||
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 sessionValue location key =
|
||||
init configValue location key =
|
||||
let
|
||||
configDecoder =
|
||||
D.map2 InitConfig
|
||||
(D.field "app-config" AppConfig.decoder)
|
||||
(D.field "session" Session.decoder)
|
||||
|
||||
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, _ ) =
|
||||
Home.init session
|
||||
|
||||
@@ -4,10 +4,16 @@ import { Elm } from './Main.elm'
|
||||
import registerMonitorPorts from './registerMonitor'
|
||||
import './renderedHtml'
|
||||
|
||||
// Initial configuration from Inbucket server to Elm App.
|
||||
var flags = {
|
||||
"app-config": appConfig(),
|
||||
"session": sessionObject(),
|
||||
}
|
||||
|
||||
// App startup.
|
||||
var app = Elm.Main.init({
|
||||
node: document.getElementById('root'),
|
||||
flags: sessionObject()
|
||||
flags: flags,
|
||||
})
|
||||
|
||||
// Message monitor.
|
||||
@@ -24,9 +30,24 @@ window.addEventListener("storage", function (event) {
|
||||
}
|
||||
}, 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() {
|
||||
var s = localStorage.session
|
||||
try {
|
||||
var s = localStorage.session
|
||||
if (s) {
|
||||
return JSON.parse(s)
|
||||
}
|
||||
@@ -35,3 +56,20 @@ function sessionObject() {
|
||||
}
|
||||
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=/;"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user