Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
f4be57b665 | |||
7f94e0beef | |||
f59566a3d3 | |||
f2f766fc05 | |||
75c4d4f991 | |||
b50d0d9884 | |||
7ae15b9e93 |
2
build.fs
2
build.fs
|
@ -33,7 +33,7 @@ let zipTheme (name : string) (_ : TargetParameter) =
|
||||||
|> Zip.zipSpec $"{releasePath}/{name}-theme.zip"
|
|> Zip.zipSpec $"{releasePath}/{name}-theme.zip"
|
||||||
|
|
||||||
/// Frameworks supported by this build
|
/// Frameworks supported by this build
|
||||||
let frameworks = [ "net6.0"; "net7.0"; "net8.0" ]
|
let frameworks = [ "net6.0"; "net8.0" ]
|
||||||
|
|
||||||
/// Publish the project for the given runtime ID
|
/// Publish the project for the given runtime ID
|
||||||
let publishFor rid (_ : TargetParameter) =
|
let publishFor rid (_ : TargetParameter) =
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||||
<DebugType>embedded</DebugType>
|
<DebugType>embedded</DebugType>
|
||||||
<AssemblyVersion>2.1.0.0</AssemblyVersion>
|
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
||||||
<FileVersion>2.1.0.0</FileVersion>
|
<FileVersion>2.2.0.0</FileVersion>
|
||||||
<Version>2.1.0</Version>
|
<Version>2.2.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -5,17 +5,17 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BitBadger.Documents.Postgres" Version="3.0.0-rc-2" />
|
<PackageReference Include="BitBadger.Documents.Postgres" Version="3.1.0" />
|
||||||
<PackageReference Include="BitBadger.Documents.Sqlite" Version="3.0.0-rc-2" />
|
<PackageReference Include="BitBadger.Documents.Sqlite" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.3" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />
|
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />
|
||||||
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.1.0" />
|
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.1.0" />
|
||||||
<PackageReference Include="Npgsql.NodaTime" Version="8.0.2" />
|
<PackageReference Include="Npgsql.NodaTime" Version="8.0.3" />
|
||||||
<PackageReference Include="RethinkDb.Driver" Version="2.3.150" />
|
<PackageReference Include="RethinkDb.Driver" Version="2.3.150" />
|
||||||
<PackageReference Include="RethinkDb.Driver.FSharp" Version="0.9.0-beta-07" />
|
<PackageReference Include="RethinkDb.Driver.FSharp" Version="0.9.0-beta-07" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.200" />
|
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -211,6 +211,16 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
||||||
return! setDbVersion "v2.1.1"
|
return! setDbVersion "v2.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Migrate from v2.1.1 to v2.2
|
||||||
|
let migrateV2point1point1ToV2point2 () = backgroundTask {
|
||||||
|
Utils.Migration.logStep log "v2.1.1 to v2.2" "Setting e-mail to lowercase"
|
||||||
|
do! Custom.nonQuery
|
||||||
|
$"""UPDATE {Table.WebLogUser} SET data = data || ('{{"Email":"' || lower(data->>'Email') || '"}}')::jsonb"""
|
||||||
|
[]
|
||||||
|
Utils.Migration.logStep log "v2.1.1 to v2.2" "Setting database version to v2.2"
|
||||||
|
return! setDbVersion "v2.2"
|
||||||
|
}
|
||||||
|
|
||||||
/// Do required data migration between versions
|
/// Do required data migration between versions
|
||||||
let migrate version = backgroundTask {
|
let migrate version = backgroundTask {
|
||||||
let mutable v = defaultArg version ""
|
let mutable v = defaultArg version ""
|
||||||
|
@ -226,6 +236,10 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
||||||
let! ver = migrateV2ToV2point1point1 ()
|
let! ver = migrateV2ToV2point1point1 ()
|
||||||
v <- ver
|
v <- ver
|
||||||
|
|
||||||
|
if v = "v2.1.1" then
|
||||||
|
let! ver = migrateV2point1point1ToV2point2 ()
|
||||||
|
v <- ver
|
||||||
|
|
||||||
if v <> Utils.Migration.currentDbVersion then
|
if v <> Utils.Migration.currentDbVersion then
|
||||||
log.LogWarning $"Unknown database version; assuming {Utils.Migration.currentDbVersion}"
|
log.LogWarning $"Unknown database version; assuming {Utils.Migration.currentDbVersion}"
|
||||||
let! _ = setDbVersion Utils.Migration.currentDbVersion
|
let! _ = setDbVersion Utils.Migration.currentDbVersion
|
||||||
|
|
|
@ -239,11 +239,23 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate from v2.1 to v2.1.1
|
/// Migrate from v2.1 to v2.1.1
|
||||||
let migrateV2ToV2point1point1 () = backgroundTask {
|
let migrateV2point1ToV2point1point1 () = backgroundTask {
|
||||||
Utils.Migration.logStep log "v2.1 to v2.1.1" "Setting database version; no migration required"
|
Utils.Migration.logStep log "v2.1 to v2.1.1" "Setting database version; no migration required"
|
||||||
do! setDbVersion "v2.1.1"
|
do! setDbVersion "v2.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Migrate from v2.1.1 to v2.2
|
||||||
|
let migrateV2point1point1ToV2point2 () = backgroundTask {
|
||||||
|
Utils.Migration.logStep log "v2.1.1 to v2.2" "Setting e-mail to lowercase"
|
||||||
|
do! rethink {
|
||||||
|
withTable Table.WebLogUser
|
||||||
|
update (fun row -> {| Email = row[nameof WebLogUser.Empty.Email].Downcase() |})
|
||||||
|
write; withRetryOnce; ignoreResult conn
|
||||||
|
}
|
||||||
|
Utils.Migration.logStep log "v2.1.1 to v2.2" "Setting database version to v2.2"
|
||||||
|
do! setDbVersion "v2.2"
|
||||||
|
}
|
||||||
|
|
||||||
/// Migrate data between versions
|
/// Migrate data between versions
|
||||||
let migrate version = backgroundTask {
|
let migrate version = backgroundTask {
|
||||||
let mutable v = defaultArg version ""
|
let mutable v = defaultArg version ""
|
||||||
|
@ -261,9 +273,13 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi
|
||||||
v <- "v2.1"
|
v <- "v2.1"
|
||||||
|
|
||||||
if v = "v2.1" then
|
if v = "v2.1" then
|
||||||
do! migrateV2ToV2point1point1 ()
|
do! migrateV2point1ToV2point1point1 ()
|
||||||
v <- "v2.1.1"
|
v <- "v2.1.1"
|
||||||
|
|
||||||
|
if v = "v2.1.1" then
|
||||||
|
do! migrateV2point1point1ToV2point2 ()
|
||||||
|
v <- "v2.2"
|
||||||
|
|
||||||
if v <> Utils.Migration.currentDbVersion then
|
if v <> Utils.Migration.currentDbVersion then
|
||||||
log.LogWarning $"Unknown database version; assuming {Utils.Migration.currentDbVersion}"
|
log.LogWarning $"Unknown database version; assuming {Utils.Migration.currentDbVersion}"
|
||||||
do! setDbVersion Utils.Migration.currentDbVersion
|
do! setDbVersion Utils.Migration.currentDbVersion
|
||||||
|
|
|
@ -431,10 +431,18 @@ type SQLiteData(conn: SqliteConnection, log: ILogger<SQLiteData>, ser: JsonSeria
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Migrate from v2.1 to v2.1.1
|
/// Migrate from v2.1 to v2.1.1
|
||||||
let migrateV2ToV2point1point1 () = backgroundTask {
|
let migrateV2point1ToV2point1point1 () = backgroundTask {
|
||||||
Utils.Migration.logStep log "v2.1 to v2.1.1" "Setting database version; no migration required"
|
Utils.Migration.logStep log "v2.1 to v2.1.1" "Setting database version; no migration required"
|
||||||
do! setDbVersion "v2.1.1"
|
do! setDbVersion "v2.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Migrate from v2.1.1 to v2.2
|
||||||
|
let migrateV2point1point1ToV2point2 () = backgroundTask {
|
||||||
|
Utils.Migration.logStep log "v2.1.1 to v2.2" "Setting e-mail to lowercase"
|
||||||
|
do! Custom.nonQuery $"UPDATE {Table.WebLogUser} SET data = json_set(data, '$.Email', lower(data->>'Email'))" []
|
||||||
|
Utils.Migration.logStep log "v2.1.1 to v2.2" "Setting database version to v2.2"
|
||||||
|
do! setDbVersion "v2.2"
|
||||||
|
}
|
||||||
|
|
||||||
/// Migrate data among versions (up only)
|
/// Migrate data among versions (up only)
|
||||||
let migrate version = backgroundTask {
|
let migrate version = backgroundTask {
|
||||||
|
@ -453,9 +461,13 @@ type SQLiteData(conn: SqliteConnection, log: ILogger<SQLiteData>, ser: JsonSeria
|
||||||
v <- "v2.1"
|
v <- "v2.1"
|
||||||
|
|
||||||
if v = "v2.1" then
|
if v = "v2.1" then
|
||||||
do! migrateV2ToV2point1point1 ()
|
do! migrateV2point1ToV2point1point1 ()
|
||||||
v <- "v2.1.1"
|
v <- "v2.1.1"
|
||||||
|
|
||||||
|
if v = "v2.1.1" then
|
||||||
|
do! migrateV2point1point1ToV2point2 ()
|
||||||
|
v <- "v2.2"
|
||||||
|
|
||||||
if v <> Utils.Migration.currentDbVersion then
|
if v <> Utils.Migration.currentDbVersion then
|
||||||
log.LogWarning $"Unknown database version; assuming {Utils.Migration.currentDbVersion}"
|
log.LogWarning $"Unknown database version; assuming {Utils.Migration.currentDbVersion}"
|
||||||
do! setDbVersion Utils.Migration.currentDbVersion
|
do! setDbVersion Utils.Migration.currentDbVersion
|
||||||
|
|
|
@ -54,7 +54,7 @@ module Migration =
|
||||||
open Microsoft.Extensions.Logging
|
open Microsoft.Extensions.Logging
|
||||||
|
|
||||||
/// The current database version
|
/// The current database version
|
||||||
let currentDbVersion = "v2.1.1"
|
let currentDbVersion = "v2.2"
|
||||||
|
|
||||||
/// Log a migration step
|
/// Log a migration step
|
||||||
let logStep<'T> (log: ILogger<'T>) migration message =
|
let logStep<'T> (log: ILogger<'T>) migration message =
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Markdig" Version="0.36.2" />
|
<PackageReference Include="Markdig" Version="0.37.0" />
|
||||||
<PackageReference Include="Markdown.ColorCode" Version="2.2.1" />
|
<PackageReference Include="Markdown.ColorCode" Version="2.2.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="NodaTime" Version="3.1.11" />
|
<PackageReference Include="NodaTime" Version="3.1.11" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.200" />
|
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -154,7 +154,9 @@ type DisplayTheme = {
|
||||||
|
|
||||||
/// Create a display theme from a theme
|
/// Create a display theme from a theme
|
||||||
static member FromTheme inUseFunc (theme: Theme) =
|
static member FromTheme inUseFunc (theme: Theme) =
|
||||||
let fileName = if string theme.Id = "default" then "default-theme.zip" else $"./themes/{theme.Id}-theme.zip"
|
let fileName =
|
||||||
|
if string theme.Id = "default" then "default-theme.zip"
|
||||||
|
else Path.Combine(".", "themes", $"{theme.Id}-theme.zip")
|
||||||
{ Id = string theme.Id
|
{ Id = string theme.Id
|
||||||
Name = theme.Name
|
Name = theme.Name
|
||||||
Version = theme.Version
|
Version = theme.Version
|
||||||
|
@ -937,7 +939,7 @@ type EditUserModel = {
|
||||||
member this.UpdateUser (user: WebLogUser) =
|
member this.UpdateUser (user: WebLogUser) =
|
||||||
{ user with
|
{ user with
|
||||||
AccessLevel = AccessLevel.Parse this.AccessLevel
|
AccessLevel = AccessLevel.Parse this.AccessLevel
|
||||||
Email = this.Email
|
Email = this.Email.ToLowerInvariant()
|
||||||
Url = noneIfBlank this.Url
|
Url = noneIfBlank this.Url
|
||||||
FirstName = this.FirstName
|
FirstName = this.FirstName
|
||||||
LastName = this.LastName
|
LastName = this.LastName
|
||||||
|
|
|
@ -118,16 +118,19 @@ let displayThemeTests = testList "DisplayTheme.FromTheme" [
|
||||||
Expect.isFalse model.IsOnDisk "IsOnDisk should not have been set"
|
Expect.isFalse model.IsOnDisk "IsOnDisk should not have been set"
|
||||||
}
|
}
|
||||||
test "succeeds when a non-default theme is not in use and is on disk" {
|
test "succeeds when a non-default theme is not in use and is on disk" {
|
||||||
let dir = Directory.CreateDirectory "themes"
|
let dir = Directory.CreateDirectory "themes"
|
||||||
let file = File.Create "./themes/another-theme.zip"
|
|
||||||
try
|
try
|
||||||
let model = DisplayTheme.FromTheme (fun _ -> false) { theme with Id = ThemeId "another" }
|
let testFile = Path.Combine(".", "themes", "another-theme.zip")
|
||||||
Expect.isFalse model.IsInUse "IsInUse should not have been set"
|
let file = File.Create testFile
|
||||||
Expect.isTrue model.IsOnDisk "IsOnDisk should have been set"
|
try
|
||||||
|
let model = DisplayTheme.FromTheme (fun _ -> false) { theme with Id = ThemeId "another" }
|
||||||
|
Expect.isFalse model.IsInUse "IsInUse should not have been set"
|
||||||
|
Expect.isTrue model.IsOnDisk "IsOnDisk should have been set"
|
||||||
|
finally
|
||||||
|
file.Close()
|
||||||
|
file.Dispose()
|
||||||
|
File.Delete testFile
|
||||||
finally
|
finally
|
||||||
file.Close()
|
|
||||||
file.Dispose()
|
|
||||||
File.Delete "./themes/another-theme.zip"
|
|
||||||
dir.Delete()
|
dir.Delete()
|
||||||
}
|
}
|
||||||
test "succeeds when the default theme is on disk" {
|
test "succeeds when the default theme is on disk" {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Expecto" Version="10.2.1" />
|
<PackageReference Include="Expecto" Version="10.2.1" />
|
||||||
<PackageReference Include="ThrowawayDb.Postgres" Version="1.4.0" />
|
<PackageReference Include="ThrowawayDb.Postgres" Version="1.4.0" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.200" />
|
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -399,8 +399,11 @@ module Theme =
|
||||||
let! _ = loadFromZip themeId stream data
|
let! _ = loadFromZip themeId stream data
|
||||||
do! ThemeAssetCache.refreshTheme themeId data
|
do! ThemeAssetCache.refreshTheme themeId data
|
||||||
TemplateCache.invalidateTheme themeId
|
TemplateCache.invalidateTheme themeId
|
||||||
|
// Ensure the themes directory exists
|
||||||
|
let themeDir = Path.Combine(".", "themes")
|
||||||
|
if not (Directory.Exists themeDir) then Directory.CreateDirectory themeDir |> ignore
|
||||||
// Save the .zip file
|
// Save the .zip file
|
||||||
use file = new FileStream($"./themes/{themeId}-theme.zip", FileMode.Create)
|
use file = new FileStream(Path.Combine(".", "themes", $"{themeId}-theme.zip"), FileMode.Create)
|
||||||
do! themeFile.CopyToAsync file
|
do! themeFile.CopyToAsync file
|
||||||
do! addMessage ctx
|
do! addMessage ctx
|
||||||
{ UserMessage.Success with
|
{ UserMessage.Success with
|
||||||
|
@ -435,7 +438,7 @@ module Theme =
|
||||||
| _ ->
|
| _ ->
|
||||||
match! data.Theme.Delete (ThemeId themeId) with
|
match! data.Theme.Delete (ThemeId themeId) with
|
||||||
| true ->
|
| true ->
|
||||||
let zippedTheme = $"./themes/{themeId}-theme.zip"
|
let zippedTheme = Path.Combine(".", "themes", $"{themeId}-theme.zip")
|
||||||
if File.Exists zippedTheme then File.Delete zippedTheme
|
if File.Exists zippedTheme then File.Delete zippedTheme
|
||||||
do! addMessage ctx { UserMessage.Success with Message = $"Theme ID {themeId} deleted successfully" }
|
do! addMessage ctx { UserMessage.Success with Message = $"Theme ID {themeId} deleted successfully" }
|
||||||
return! all next ctx
|
return! all next ctx
|
||||||
|
|
|
@ -46,7 +46,7 @@ open Microsoft.AspNetCore.Authentication.Cookies
|
||||||
let doLogOn : HttpHandler = fun next ctx -> task {
|
let doLogOn : HttpHandler = fun next ctx -> task {
|
||||||
let! model = ctx.BindFormAsync<LogOnModel>()
|
let! model = ctx.BindFormAsync<LogOnModel>()
|
||||||
let data = ctx.Data
|
let data = ctx.Data
|
||||||
let! tryUser = data.WebLogUser.FindByEmail model.EmailAddress ctx.WebLog.Id
|
let! tryUser = data.WebLogUser.FindByEmail (model.EmailAddress.ToLowerInvariant()) ctx.WebLog.Id
|
||||||
match! verifyPassword tryUser model.Password ctx with
|
match! verifyPassword tryUser model.Password ctx with
|
||||||
| Ok _ ->
|
| Ok _ ->
|
||||||
let user = tryUser.Value
|
let user = tryUser.Value
|
||||||
|
|
|
@ -20,33 +20,31 @@ let private doCreateWebLog (args: string[]) (sp: IServiceProvider) = task {
|
||||||
| true, ianaId -> ianaId
|
| true, ianaId -> ianaId
|
||||||
| false, _ -> raise <| TimeZoneNotFoundException $"Cannot find IANA timezone for {local}"
|
| false, _ -> raise <| TimeZoneNotFoundException $"Cannot find IANA timezone for {local}"
|
||||||
|
|
||||||
// Create the web log
|
|
||||||
let webLogId = WebLogId.Create()
|
|
||||||
let userId = WebLogUserId.Create()
|
|
||||||
let homePageId = PageId.Create()
|
|
||||||
let slug = Handlers.Upload.makeSlug args[2]
|
|
||||||
|
|
||||||
// If this is the first web log being created, the user will be an installation admin; otherwise, they will be an
|
// If this is the first web log being created, the user will be an installation admin; otherwise, they will be an
|
||||||
// admin just over their web log
|
// admin just over their web log
|
||||||
let! webLogs = data.WebLog.All()
|
let! webLogs = data.WebLog.All()
|
||||||
let accessLevel = if List.isEmpty webLogs then Administrator else WebLogAdmin
|
let accessLevel = if List.isEmpty webLogs then Administrator else WebLogAdmin
|
||||||
|
|
||||||
do! data.WebLog.Add
|
let homePageId = PageId.Create()
|
||||||
{ WebLog.Empty with
|
|
||||||
Id = webLogId
|
// Create the web log
|
||||||
Name = args[2]
|
let webLog =
|
||||||
Slug = slug
|
{ WebLog.Empty with
|
||||||
UrlBase = args[1]
|
Id = WebLogId.Create()
|
||||||
DefaultPage = string homePageId
|
Name = args[2]
|
||||||
TimeZone = timeZone }
|
Slug = Handlers.Upload.makeSlug args[2]
|
||||||
|
UrlBase = args[1].ToLowerInvariant()
|
||||||
|
DefaultPage = string homePageId
|
||||||
|
TimeZone = timeZone }
|
||||||
|
do! data.WebLog.Add webLog
|
||||||
|
|
||||||
// Create the admin user
|
// Create the admin user
|
||||||
let now = Noda.now ()
|
let now = Noda.now ()
|
||||||
let user =
|
let user =
|
||||||
{ WebLogUser.Empty with
|
{ WebLogUser.Empty with
|
||||||
Id = userId
|
Id = WebLogUserId.Create()
|
||||||
WebLogId = webLogId
|
WebLogId = webLog.Id
|
||||||
Email = args[3]
|
Email = args[3].ToLowerInvariant()
|
||||||
FirstName = "Admin"
|
FirstName = "Admin"
|
||||||
LastName = "User"
|
LastName = "User"
|
||||||
PreferredName = "Admin"
|
PreferredName = "Admin"
|
||||||
|
@ -58,8 +56,8 @@ let private doCreateWebLog (args: string[]) (sp: IServiceProvider) = task {
|
||||||
do! data.Page.Add
|
do! data.Page.Add
|
||||||
{ Page.Empty with
|
{ Page.Empty with
|
||||||
Id = homePageId
|
Id = homePageId
|
||||||
WebLogId = webLogId
|
WebLogId = webLog.Id
|
||||||
AuthorId = userId
|
AuthorId = user.Id
|
||||||
Title = "Welcome to myWebLog!"
|
Title = "Welcome to myWebLog!"
|
||||||
Permalink = Permalink "welcome-to-myweblog.html"
|
Permalink = Permalink "welcome-to-myweblog.html"
|
||||||
PublishedOn = now
|
PublishedOn = now
|
||||||
|
@ -70,11 +68,11 @@ let private doCreateWebLog (args: string[]) (sp: IServiceProvider) = task {
|
||||||
Text = Html "<p>This is your default home page.</p>" }
|
Text = Html "<p>This is your default home page.</p>" }
|
||||||
] }
|
] }
|
||||||
|
|
||||||
printfn $"Successfully initialized database for {args[2]} with URL base {args[1]}"
|
printfn $"Successfully initialized database for {webLog.Name} with URL base {webLog.UrlBase}"
|
||||||
match accessLevel with
|
match accessLevel with
|
||||||
| Administrator -> printfn $" ({args[3]} is an installation administrator)"
|
| Administrator -> printfn $" ({user.Email} is an installation administrator)"
|
||||||
| WebLogAdmin ->
|
| WebLogAdmin ->
|
||||||
printfn $" ({args[3]} is a web log administrator;"
|
printfn $" ({user.Email} is a web log administrator;"
|
||||||
printfn """ use "upgrade-user" to promote to installation administrator)"""
|
printfn """ use "upgrade-user" to promote to installation administrator)"""
|
||||||
| _ -> ()
|
| _ -> ()
|
||||||
}
|
}
|
||||||
|
@ -422,8 +420,9 @@ module Backup =
|
||||||
/// Generate a backup archive
|
/// Generate a backup archive
|
||||||
let generateBackup (args: string[]) (sp: IServiceProvider) = task {
|
let generateBackup (args: string[]) (sp: IServiceProvider) = task {
|
||||||
if args.Length > 1 && args.Length < 5 then
|
if args.Length > 1 && args.Length < 5 then
|
||||||
|
let url = args[1].ToLowerInvariant()
|
||||||
let data = sp.GetRequiredService<IData>()
|
let data = sp.GetRequiredService<IData>()
|
||||||
match! data.WebLog.FindByHost args[1] with
|
match! data.WebLog.FindByHost url with
|
||||||
| Some webLog ->
|
| Some webLog ->
|
||||||
let fileName =
|
let fileName =
|
||||||
if args.Length = 2 || (args.Length = 3 && args[2] = "pretty") then
|
if args.Length = 2 || (args.Length = 3 && args[2] = "pretty") then
|
||||||
|
@ -434,7 +433,7 @@ module Backup =
|
||||||
$"{args[2]}.json"
|
$"{args[2]}.json"
|
||||||
let prettyOutput = (args.Length = 3 && args[2] = "pretty") || (args.Length = 4 && args[3] = "pretty")
|
let prettyOutput = (args.Length = 3 && args[2] = "pretty") || (args.Length = 4 && args[3] = "pretty")
|
||||||
do! createBackup webLog fileName prettyOutput data
|
do! createBackup webLog fileName prettyOutput data
|
||||||
| None -> eprintfn $"Error: no web log found for {args[1]}"
|
| None -> eprintfn $"Error: no web log found for {url}"
|
||||||
else
|
else
|
||||||
eprintfn """Usage: myWebLog backup [url-base] [*backup-file-name] [**"pretty"]"""
|
eprintfn """Usage: myWebLog backup [url-base] [*backup-file-name] [**"pretty"]"""
|
||||||
eprintfn """ * optional - default is [web-log-slug].json"""
|
eprintfn """ * optional - default is [web-log-slug].json"""
|
||||||
|
@ -445,7 +444,7 @@ module Backup =
|
||||||
let restoreFromBackup (args: string[]) (sp: IServiceProvider) = task {
|
let restoreFromBackup (args: string[]) (sp: IServiceProvider) = task {
|
||||||
if args.Length = 2 || args.Length = 3 then
|
if args.Length = 2 || args.Length = 3 then
|
||||||
let data = sp.GetRequiredService<IData>()
|
let data = sp.GetRequiredService<IData>()
|
||||||
let newUrlBase = if args.Length = 3 then Some args[2] else None
|
let newUrlBase = if args.Length = 3 then Some (args[2].ToLowerInvariant()) else None
|
||||||
do! restoreBackup args[1] newUrlBase (args[0] <> "do-restore") true data
|
do! restoreBackup args[1] newUrlBase (args[0] <> "do-restore") true data
|
||||||
else
|
else
|
||||||
eprintfn "Usage: myWebLog restore [backup-file-name] [*url-base]"
|
eprintfn "Usage: myWebLog restore [backup-file-name] [*url-base]"
|
||||||
|
@ -472,7 +471,7 @@ let private doUserUpgrade urlBase email (data: IData) = task {
|
||||||
/// Upgrade a WebLogAdmin user to an Administrator user if the command-line arguments are good
|
/// Upgrade a WebLogAdmin user to an Administrator user if the command-line arguments are good
|
||||||
let upgradeUser (args: string[]) (sp: IServiceProvider) = task {
|
let upgradeUser (args: string[]) (sp: IServiceProvider) = task {
|
||||||
match args.Length with
|
match args.Length with
|
||||||
| 3 -> do! doUserUpgrade args[1] args[2] (sp.GetRequiredService<IData>())
|
| 3 -> do! doUserUpgrade (args[1].ToLowerInvariant()) (args[2].ToLowerInvariant()) (sp.GetRequiredService<IData>())
|
||||||
| _ -> eprintfn "Usage: myWebLog upgrade-user [web-log-url-base] [email-address]"
|
| _ -> eprintfn "Usage: myWebLog upgrade-user [web-log-url-base] [email-address]"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,6 +490,8 @@ let doSetPassword urlBase email password (data: IData) = task {
|
||||||
/// Set a user's password if the command-line arguments are good
|
/// Set a user's password if the command-line arguments are good
|
||||||
let setPassword (args: string[]) (sp: IServiceProvider) = task {
|
let setPassword (args: string[]) (sp: IServiceProvider) = task {
|
||||||
match args.Length with
|
match args.Length with
|
||||||
| 4 -> do! doSetPassword args[1] args[2] args[3] (sp.GetRequiredService<IData>())
|
| 4 ->
|
||||||
|
do! doSetPassword
|
||||||
|
(args[1].ToLowerInvariant()) (args[2].ToLowerInvariant()) args[3] (sp.GetRequiredService<IData>())
|
||||||
| _ -> eprintfn "Usage: myWebLog set-password [web-log-url-base] [email-address] [password]"
|
| _ -> eprintfn "Usage: myWebLog set-password [web-log-url-base] [email-address] [password]"
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,13 +31,13 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BitBadger.AspNetCore.CanonicalDomains" Version="1.0.0" />
|
<PackageReference Include="BitBadger.AspNetCore.CanonicalDomains" Version="1.0.0" />
|
||||||
<PackageReference Include="DotLiquid" Version="2.2.692" />
|
<PackageReference Include="DotLiquid" Version="2.2.692" />
|
||||||
<PackageReference Include="Giraffe" Version="6.3.0" />
|
<PackageReference Include="Giraffe" Version="6.4.0" />
|
||||||
<PackageReference Include="Giraffe.Htmx" Version="1.9.11" />
|
<PackageReference Include="Giraffe.Htmx" Version="2.0.0" />
|
||||||
<PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.9.11" />
|
<PackageReference Include="Giraffe.ViewEngine.Htmx" Version="2.0.0" />
|
||||||
<PackageReference Include="NeoSmart.Caching.Sqlite.AspNetCore" Version="8.0.0" />
|
<PackageReference Include="NeoSmart.Caching.Sqlite.AspNetCore" Version="8.0.0" />
|
||||||
<PackageReference Include="RethinkDB.DistributedCache" Version="1.0.0-rc1" />
|
<PackageReference Include="RethinkDB.DistributedCache" Version="1.0.0-rc1" />
|
||||||
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />
|
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />
|
||||||
<PackageReference Update="FSharp.Core" Version="8.0.200" />
|
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -218,8 +218,9 @@ let main args =
|
||||||
// Load admin and default themes, and all themes in the /themes directory
|
// Load admin and default themes, and all themes in the /themes directory
|
||||||
do! Maintenance.loadTheme [| ""; "./admin-theme.zip" |] app.Services
|
do! Maintenance.loadTheme [| ""; "./admin-theme.zip" |] app.Services
|
||||||
do! Maintenance.loadTheme [| ""; "./default-theme.zip" |] app.Services
|
do! Maintenance.loadTheme [| ""; "./default-theme.zip" |] app.Services
|
||||||
if Directory.Exists "./themes" then
|
let themePath = Path.Combine(".", "themes")
|
||||||
for themeFile in Directory.EnumerateFiles("./themes", "*-theme.zip") do
|
if Directory.Exists themePath then
|
||||||
|
for themeFile in Directory.EnumerateFiles(themePath, "*-theme.zip") do
|
||||||
do! Maintenance.loadTheme [| ""; themeFile |] app.Services
|
do! Maintenance.loadTheme [| ""; themeFile |] app.Services
|
||||||
|
|
||||||
let _ = app.UseForwardedHeaders()
|
let _ = app.UseForwardedHeaders()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"Generator": "myWebLog 2.1.1",
|
"Generator": "myWebLog 2.2",
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"MyWebLog.Handlers": "Information"
|
"MyWebLog.Handlers": "Information"
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
myWebLog Admin
|
myWebLog Admin
|
||||||
2.1.1
|
2.2
|
|
@ -1,2 +1,2 @@
|
||||||
myWebLog Default Theme
|
myWebLog Default Theme
|
||||||
2.1.1
|
2.2
|
Loading…
Reference in New Issue
Block a user