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

Merge branch 'feature/config-cookie' into develop

This commit is contained in:
James Hillyerd
2018-12-31 15:29:15 -08:00
9 changed files with 153 additions and 17 deletions

View File

@@ -12,6 +12,7 @@ export INBUCKET_SMTP_STOREDOMAINS="important.local"
export INBUCKET_WEB_TEMPLATECACHE="false"
export INBUCKET_WEB_COOKIEAUTHKEY="not-secret"
export INBUCKET_WEB_UIDIR="ui/dist"
#export INBUCKET_WEB_MONITORVISIBLE="false"
export INBUCKET_STORAGE_TYPE="file"
export INBUCKET_STORAGE_PARAMS="path:/tmp/inbucket"
export INBUCKET_STORAGE_RETENTIONPERIOD="3h"

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.
func fileHandler(name string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

View File

@@ -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

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

@@ -0,0 +1,20 @@
module Data.AppConfig exposing (AppConfig, decoder, default)
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
default : AppConfig
default =
AppConfig True

View File

@@ -4,15 +4,16 @@ module Data.Session exposing
, Session
, addRecent
, clearFlash
, decodeValueWithDefault
, decoder
, disableRouting
, enableRouting
, init
, initError
, showFlash
)
import Browser.Navigation as Nav
import Data.AppConfig as AppConfig exposing (AppConfig)
import Html exposing (Html)
import Json.Decode as D
import Json.Decode.Pipeline exposing (..)
@@ -28,6 +29,7 @@ type alias Session =
, flash : Maybe Flash
, routing : Bool
, zone : Time.Zone
, config : AppConfig
, persistent : Persistent
}
@@ -43,17 +45,30 @@ type alias Persistent =
}
init : Nav.Key -> Url -> Persistent -> Session
init key location persistent =
init : Nav.Key -> Url -> AppConfig -> Persistent -> Session
init key location config persistent =
{ key = key
, host = location.host
, flash = Nothing
, routing = True
, zone = Time.utc
, config = config
, persistent = 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
, config = AppConfig.default
, persistent = Persistent []
}
addRecent : String -> Session -> Session
addRecent mailbox session =
if List.head session.persistent.recentMailboxes == Just mailbox then
@@ -99,11 +114,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

View File

@@ -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.appConfig config.session
Err error ->
Session.initError key location (D.errorToString error)
( subModel, _ ) =
Home.init session
@@ -238,9 +255,21 @@ changeRouteTo route model =
|> updateWith Mailbox MailboxMsg model
Route.Monitor ->
if session.config.monitorVisible then
Monitor.init session
|> updateWith Monitor MonitorMsg model
else
let
flash =
{ title = "Unknown route requested"
, table = [ ( "Error", "Monitor disabled by configuration." ) ]
}
in
( applyToModelSession (Session.showFlash flash) model
, Cmd.none
)
Route.Status ->
Status.init session
|> updateWith Status StatusMsg model

View File

@@ -44,7 +44,11 @@ frame controls session page modal content =
[ ul [ class "navbar", attribute "role" "navigation" ]
[ li [ class "navbar-brand" ]
[ a [ Route.href Route.Home ] [ text "@ inbucket" ] ]
, navbarLink page Route.Monitor [ text "Monitor" ]
, if session.config.monitorVisible then
navbarLink page Route.Monitor [ text "Monitor" ]
else
text ""
, navbarLink page Route.Status [ text "Status" ]
, navbarRecent page controls
, li [ class "navbar-mailbox" ]

View File

@@ -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=/;"
}