I could not get Kestrel to listen for requests on Linux, and Suave now works with OWIN (and listens/responds on Linux as expected)
156 lines
5.8 KiB
Forth
156 lines
5.8 KiB
Forth
module MyWebLog.App
|
|
|
|
open MyWebLog
|
|
open MyWebLog.Data
|
|
open MyWebLog.Data.RethinkDB
|
|
open MyWebLog.Entities
|
|
open MyWebLog.Logic.WebLog
|
|
open MyWebLog.Resources
|
|
open Nancy
|
|
open Nancy.Authentication.Forms
|
|
open Nancy.Bootstrapper
|
|
open Nancy.Conventions
|
|
open Nancy.Cryptography
|
|
open Nancy.Owin
|
|
open Nancy.Security
|
|
open Nancy.Session.Persistable
|
|
//open Nancy.Session.Relational
|
|
open Nancy.Session.RethinkDB
|
|
open Nancy.TinyIoc
|
|
open Nancy.ViewEngines.SuperSimpleViewEngine
|
|
open NodaTime
|
|
open RethinkDb.Driver.Net
|
|
open Suave
|
|
open Suave.Owin
|
|
open System
|
|
open System.IO
|
|
open System.Reflection
|
|
open System.Security.Claims
|
|
open System.Text.RegularExpressions
|
|
|
|
/// Establish the configuration for this instance
|
|
let cfg = try AppConfig.FromJson (System.IO.File.ReadAllText "config.json")
|
|
with ex -> raise <| Exception (Strings.get "ErrBadAppConfig", ex)
|
|
|
|
let data = lazy (RethinkMyWebLogData (cfg.DataConfig.Conn, cfg.DataConfig) :> IMyWebLogData)
|
|
|
|
/// Support RESX lookup via the @Translate SSVE alias
|
|
type TranslateTokenViewEngineMatcher() =
|
|
static let regex = Regex ("@Translate\.(?<TranslationKey>[a-zA-Z0-9-_]+);?", RegexOptions.Compiled)
|
|
interface ISuperSimpleViewEngineMatcher with
|
|
member this.Invoke (content, model, host) =
|
|
let translate (m : Match) = Strings.get m.Groups.["TranslationKey"].Value
|
|
regex.Replace(content, translate)
|
|
|
|
|
|
/// Handle forms authentication
|
|
type MyWebLogUser (claims : Claim seq) =
|
|
inherit ClaimsPrincipal (ClaimsIdentity (claims, "forms"))
|
|
|
|
new (user : User) =
|
|
// TODO: refactor the User.Claims property to produce this, and just pass it as the constructor
|
|
let claims =
|
|
seq {
|
|
yield Claim (ClaimTypes.Name, user.PreferredName)
|
|
for claim in user.Claims -> Claim (ClaimTypes.Role, claim)
|
|
}
|
|
MyWebLogUser claims
|
|
|
|
type MyWebLogUserMapper (container : TinyIoCContainer) =
|
|
|
|
interface IUserMapper with
|
|
member this.GetUserFromIdentifier (identifier, context) =
|
|
match context.Request.PersistableSession.GetOrDefault (Keys.User, User.Empty) with
|
|
| user when user.Id = string identifier -> upcast MyWebLogUser user
|
|
| _ -> null
|
|
|
|
|
|
/// Set up the application environment
|
|
type MyWebLogBootstrapper() =
|
|
inherit DefaultNancyBootstrapper()
|
|
|
|
override this.ConfigureRequestContainer (container, context) =
|
|
base.ConfigureRequestContainer (container, context)
|
|
/// User mapper for forms authentication
|
|
container.Register<IUserMapper, MyWebLogUserMapper>()
|
|
|> ignore
|
|
|
|
override this.ConfigureConventions (conventions) =
|
|
base.ConfigureConventions conventions
|
|
conventions.StaticContentsConventions.Add
|
|
(StaticContentConventionBuilder.AddDirectory ("admin/content", "views/admin/content"))
|
|
// Make theme content available at [theme-name]/
|
|
Directory.EnumerateDirectories (Path.Combine [| "views"; "themes" |])
|
|
|> Seq.map (fun themeDir -> themeDir, Path.Combine [| themeDir; "content" |])
|
|
|> Seq.filter (fun (_, contentDir) -> Directory.Exists contentDir)
|
|
|> Seq.iter (fun (themeDir, contentDir) ->
|
|
conventions.StaticContentsConventions.Add
|
|
(StaticContentConventionBuilder.AddDirectory ((Path.GetFileName themeDir), contentDir)))
|
|
|
|
override this.ConfigureApplicationContainer (container) =
|
|
base.ConfigureApplicationContainer container
|
|
container.Register<AppConfig> cfg
|
|
|> ignore
|
|
data.Force().SetUp ()
|
|
container.Register<IMyWebLogData> (data.Force ())
|
|
|> ignore
|
|
// NodaTime
|
|
container.Register<IClock> SystemClock.Instance
|
|
|> ignore
|
|
// I18N in SSVE
|
|
container.Register<ISuperSimpleViewEngineMatcher seq> (fun _ _ ->
|
|
Seq.singleton (TranslateTokenViewEngineMatcher () :> ISuperSimpleViewEngineMatcher))
|
|
|> ignore
|
|
|
|
override this.ApplicationStartup (container, pipelines) =
|
|
base.ApplicationStartup (container, pipelines)
|
|
// Forms authentication configuration
|
|
let auth =
|
|
FormsAuthenticationConfiguration (
|
|
CryptographyConfiguration =
|
|
CryptographyConfiguration (
|
|
AesEncryptionProvider (PassphraseKeyGenerator (cfg.AuthEncryptionPassphrase, cfg.AuthSalt)),
|
|
DefaultHmacProvider (PassphraseKeyGenerator (cfg.AuthHmacPassphrase, cfg.AuthSalt))),
|
|
RedirectUrl = "~/user/log-on",
|
|
UserMapper = container.Resolve<IUserMapper> ())
|
|
FormsAuthentication.Enable (pipelines, auth)
|
|
// CSRF
|
|
Csrf.Enable pipelines
|
|
// Sessions
|
|
let sessions = RethinkDBSessionConfiguration cfg.DataConfig.Conn
|
|
sessions.Database <- cfg.DataConfig.Database
|
|
//let sessions = RelationalSessionConfiguration(ConfigurationManager.ConnectionStrings.["SessionStore"].ConnectionString)
|
|
PersistableSessions.Enable (pipelines, sessions)
|
|
()
|
|
|
|
override this.Configure (environment) =
|
|
base.Configure environment
|
|
environment.Tracing (true, true)
|
|
|
|
|
|
let version =
|
|
let v = typeof<AppConfig>.GetTypeInfo().Assembly.GetName().Version
|
|
match v.Build with
|
|
| 0 -> match v.Minor with 0 -> string v.Major | _ -> sprintf "%d.%d" v.Major v.Minor
|
|
| _ -> sprintf "%d.%d.%d" v.Major v.Minor v.Build
|
|
|> sprintf "v%s"
|
|
|
|
/// Set up the request environment
|
|
type RequestEnvironment() =
|
|
interface IRequestStartup with
|
|
member this.Initialize (pipelines, context) =
|
|
let establishEnv (ctx : NancyContext) =
|
|
ctx.Items.[Keys.RequestStart] <- DateTime.Now.Ticks
|
|
match tryFindWebLogByUrlBase (data.Force ()) ctx.Request.Url.HostName with
|
|
| Some webLog -> ctx.Items.[Keys.WebLog] <- webLog
|
|
| None -> // TODO: redirect to domain set up page
|
|
Exception (sprintf "%s %s" ctx.Request.Url.HostName (Strings.get "ErrNotConfigured"))
|
|
|> raise
|
|
ctx.Items.[Keys.Version] <- version
|
|
null
|
|
pipelines.BeforeRequest.AddItemToStartOfPipeline establishEnv
|
|
|
|
let Run () =
|
|
OwinApp.ofMidFunc "/" (NancyMiddleware.UseNancy (NancyOptions (Bootstrapper = new MyWebLogBootstrapper ())))
|
|
|> startWebServer defaultConfig
|