Version 2.1 #41
@ -17,16 +17,13 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
|||||||
Configuration.useSerializer (Utils.createDocumentSerializer ser)
|
Configuration.useSerializer (Utils.createDocumentSerializer ser)
|
||||||
|
|
||||||
let! tables =
|
let! tables =
|
||||||
Custom.list "SELECT tablename FROM pg_tables WHERE schemaname = 'public'" []
|
Custom.list
|
||||||
(fun row -> row.string "tablename")
|
"SELECT tablename FROM pg_tables WHERE schemaname = 'public'" [] (fun row -> row.string "tablename")
|
||||||
let needsTable table = not (List.contains table tables)
|
let needsTable table = not (List.contains table tables)
|
||||||
// Create a document table
|
|
||||||
let mutable isNew = false
|
|
||||||
|
|
||||||
let sql = seq {
|
let sql = seq {
|
||||||
// Theme tables
|
// Theme tables
|
||||||
if needsTable Table.Theme then
|
if needsTable Table.Theme then
|
||||||
isNew <- true
|
|
||||||
Query.Definition.ensureTable Table.Theme
|
Query.Definition.ensureTable Table.Theme
|
||||||
Query.Definition.ensureKey Table.Theme
|
Query.Definition.ensureKey Table.Theme
|
||||||
if needsTable Table.ThemeAsset then
|
if needsTable Table.ThemeAsset then
|
||||||
@ -152,8 +149,7 @@ type PostgresData(log: ILogger<PostgresData>, ser: JsonSerializer) =
|
|||||||
" - Drop all tables from the database"
|
" - Drop all tables from the database"
|
||||||
" - Use this executable to restore each backup"; ""
|
" - Use this executable to restore each backup"; ""
|
||||||
"Commands to back up all web logs:"
|
"Commands to back up all web logs:"
|
||||||
yield! webLogs |> List.map (fun (url, slug) -> $"./myWebLog backup {url} v2-rc2.{slug}.json")
|
yield! webLogs |> List.map (fun (url, slug) -> $"./myWebLog backup {url} v2-rc2.{slug}.json") ]
|
||||||
]
|
|
||||||
|> String.concat "\n"
|
|> String.concat "\n"
|
||||||
|> log.LogWarning
|
|> log.LogWarning
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
|
|||||||
let countTopLevel webLogId =
|
let countTopLevel webLogId =
|
||||||
log.LogTrace "Category.countTopLevel"
|
log.LogTrace "Category.countTopLevel"
|
||||||
conn.customScalar
|
conn.customScalar
|
||||||
$"{Document.Query.countByWebLog} AND data ->> '{parentIdField}' IS NULL"
|
$"{Document.Query.countByWebLog Table.Category} AND data ->> '{parentIdField}' IS NULL"
|
||||||
[ webLogParam webLogId ]
|
[ webLogParam webLogId ]
|
||||||
(toCount >> int)
|
(toCount >> int)
|
||||||
|
|
||||||
@ -79,11 +79,11 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
|
|||||||
match! findById catId webLogId with
|
match! findById catId webLogId with
|
||||||
| Some cat ->
|
| Some cat ->
|
||||||
// Reassign any children to the category's parent category
|
// Reassign any children to the category's parent category
|
||||||
let! children = conn.countByField Table.Category parentIdField EQ catId
|
let! children = conn.countByField Table.Category parentIdField EQ (string catId)
|
||||||
if children > 0 then
|
if children > 0 then
|
||||||
do! conn.patchByField Table.Category parentIdField EQ catId {| ParentId = cat.ParentId |}
|
do! conn.patchByField Table.Category parentIdField EQ (string catId) {| ParentId = cat.ParentId |}
|
||||||
// Delete the category off all posts where it is assigned, and the category itself
|
// Delete the category off all posts where it is assigned, and the category itself
|
||||||
let catIdField = Post.Empty.CategoryIds
|
let catIdField = nameof Post.Empty.CategoryIds
|
||||||
let! posts =
|
let! posts =
|
||||||
conn.customList
|
conn.customList
|
||||||
$"SELECT data ->> '{Post.Empty.Id}', data -> '{catIdField}'
|
$"SELECT data ->> '{Post.Empty.Id}', data -> '{catIdField}'
|
||||||
@ -94,7 +94,7 @@ type SQLiteCategoryData(conn: SqliteConnection, ser: JsonSerializer, log: ILogge
|
|||||||
FROM json_each({Table.Post}.data -> '{catIdField}')
|
FROM json_each({Table.Post}.data -> '{catIdField}')
|
||||||
WHERE json_each.value = @id)"
|
WHERE json_each.value = @id)"
|
||||||
[ idParam catId; webLogParam webLogId ]
|
[ idParam catId; webLogParam webLogId ]
|
||||||
(fun rdr -> rdr.GetString(0), Utils.deserialize<string list> ser (rdr.GetString(1)))
|
(fun rdr -> rdr.GetString 0, Utils.deserialize<string list> ser (rdr.GetString 1))
|
||||||
for postId, cats in posts do
|
for postId, cats in posts do
|
||||||
do! conn.patchById
|
do! conn.patchById
|
||||||
Table.Post postId {| CategoryIds = cats |> List.filter (fun it -> it <> string catId) |}
|
Table.Post postId {| CategoryIds = cats |> List.filter (fun it -> it <> string catId) |}
|
||||||
|
@ -255,7 +255,7 @@ module Document =
|
|||||||
|
|
||||||
/// Count documents for the given web log ID
|
/// Count documents for the given web log ID
|
||||||
let countByWebLog table (webLogId: WebLogId) conn = backgroundTask {
|
let countByWebLog table (webLogId: WebLogId) conn = backgroundTask {
|
||||||
let! count = Count.byField table "WebLogId" EQ webLogId conn
|
let! count = Count.byField table "WebLogId" EQ (string webLogId) conn
|
||||||
return int count
|
return int count
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ module Document =
|
|||||||
|
|
||||||
/// Find documents for the given web log
|
/// Find documents for the given web log
|
||||||
let findByWebLog<'TDoc> table (webLogId: WebLogId) conn =
|
let findByWebLog<'TDoc> table (webLogId: WebLogId) conn =
|
||||||
Find.byField<'TDoc> table "WebLogId" EQ webLogId conn
|
Find.byField<'TDoc> table "WebLogId" EQ (string webLogId) conn
|
||||||
|
|
||||||
|
|
||||||
/// Functions to support revisions
|
/// Functions to support revisions
|
||||||
@ -302,6 +302,6 @@ module Revisions =
|
|||||||
for addRev in toAdd do
|
for addRev in toAdd do
|
||||||
do! Custom.nonQuery
|
do! Custom.nonQuery
|
||||||
$"INSERT INTO {revTable} VALUES (@id, @asOf, @text)"
|
$"INSERT INTO {revTable} VALUES (@id, @asOf, @text)"
|
||||||
[ idParam key; sqlParam "asOf" (instantParam addRev.AsOf); sqlParam "@text" addRev.Text ]
|
[ idParam key; sqlParam "asOf" (instantParam addRev.AsOf); sqlParam "@text" (string addRev.Text) ]
|
||||||
conn
|
conn
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
|
|||||||
let countListed webLogId =
|
let countListed webLogId =
|
||||||
log.LogTrace "Page.countListed"
|
log.LogTrace "Page.countListed"
|
||||||
conn.customScalar
|
conn.customScalar
|
||||||
$"""{Document.Query.countByWebLog} AND {Query.whereByField pgListName EQ "'true'"}"""
|
$"""{Document.Query.countByWebLog Table.Page} AND {Query.whereByField pgListName EQ "'true'"}"""
|
||||||
[ webLogParam webLogId ]
|
[ webLogParam webLogId ]
|
||||||
(toCount >> int)
|
(toCount >> int)
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
|
|||||||
let findByPermalink (permalink: Permalink) webLogId =
|
let findByPermalink (permalink: Permalink) webLogId =
|
||||||
log.LogTrace "Page.findByPermalink"
|
log.LogTrace "Page.findByPermalink"
|
||||||
conn.customSingle
|
conn.customSingle
|
||||||
$"""{Document.Query.selectByWebLog} AND {Query.whereByField linkName EQ "@link"}"""
|
$"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField linkName EQ "@link"}"""
|
||||||
[ webLogParam webLogId; SqliteParameter("@link", string permalink) ]
|
[ webLogParam webLogId; SqliteParameter("@link", string permalink) ]
|
||||||
fromData<Page>
|
fromData<Page>
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) =
|
|||||||
let countByStatus (status: PostStatus) webLogId =
|
let countByStatus (status: PostStatus) webLogId =
|
||||||
log.LogTrace "Post.countByStatus"
|
log.LogTrace "Post.countByStatus"
|
||||||
conn.customScalar
|
conn.customScalar
|
||||||
$"""{Document.Query.countByWebLog} AND {Query.whereByField statName EQ "@status"}"""
|
$"""{Document.Query.countByWebLog Table.Post} AND {Query.whereByField statName EQ "@status"}"""
|
||||||
[ webLogParam webLogId; SqliteParameter("@status", string status) ]
|
[ webLogParam webLogId; SqliteParameter("@status", string status) ]
|
||||||
(toCount >> int)
|
(toCount >> int)
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ type SQLiteThemeAssetData(conn : SqliteConnection, log: ILogger) =
|
|||||||
$"INSERT INTO {Table.ThemeAsset} (
|
$"INSERT INTO {Table.ThemeAsset} (
|
||||||
theme_id, path, updated_on, data
|
theme_id, path, updated_on, data
|
||||||
) VALUES (
|
) VALUES (
|
||||||
@themeId, @path, @updatedOn, ZEROBLOB(@dataLength)
|
@id, @path, @updatedOn, ZEROBLOB(@dataLength)
|
||||||
) ON CONFLICT (theme_id, path) DO UPDATE
|
) ON CONFLICT (theme_id, path) DO UPDATE
|
||||||
SET updated_on = @updatedOn,
|
SET updated_on = @updatedOn,
|
||||||
data = ZEROBLOB(@dataLength)"
|
data = ZEROBLOB(@dataLength)"
|
||||||
|
@ -13,7 +13,7 @@ module private Helpers =
|
|||||||
/// Create a new ID (short GUID)
|
/// Create a new ID (short GUID)
|
||||||
// https://www.madskristensen.net/blog/A-shorter-and-URL-friendly-GUID
|
// https://www.madskristensen.net/blog/A-shorter-and-URL-friendly-GUID
|
||||||
let newId () =
|
let newId () =
|
||||||
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-')[..22]
|
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-')[..21]
|
||||||
|
|
||||||
/// Pipeline with most extensions enabled
|
/// Pipeline with most extensions enabled
|
||||||
let markdownPipeline = MarkdownPipelineBuilder().UseSmartyPants().UseAdvancedExtensions().UseColorCode().Build()
|
let markdownPipeline = MarkdownPipelineBuilder().UseSmartyPants().UseAdvancedExtensions().UseColorCode().Build()
|
||||||
|
@ -443,7 +443,7 @@ module Theme =
|
|||||||
let themeName = fileName.Split(".").[0].ToLowerInvariant().Replace(" ", "-")
|
let themeName = fileName.Split(".").[0].ToLowerInvariant().Replace(" ", "-")
|
||||||
if themeName.EndsWith "-theme" then
|
if themeName.EndsWith "-theme" then
|
||||||
if Regex.IsMatch(themeName, """^[a-z0-9\-]+$""") then
|
if Regex.IsMatch(themeName, """^[a-z0-9\-]+$""") then
|
||||||
Ok(ThemeId(themeName[..themeName.Length - 6]))
|
Ok(ThemeId(themeName[..themeName.Length - 7]))
|
||||||
else Error $"Theme ID {fileName} is invalid"
|
else Error $"Theme ID {fileName} is invalid"
|
||||||
else Error "Theme .zip file name must end in \"-theme.zip\""
|
else Error "Theme .zip file name must end in \"-theme.zip\""
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ module CatchAll =
|
|||||||
let textLink =
|
let textLink =
|
||||||
let extra = webLog.ExtraPath
|
let extra = webLog.ExtraPath
|
||||||
let url = string ctx.Request.Path
|
let url = string ctx.Request.Path
|
||||||
(if extra = "" then url else url[..extra.Length]).ToLowerInvariant()
|
(if extra = "" then url else url[extra.Length..]).ToLowerInvariant()
|
||||||
let await it = (Async.AwaitTask >> Async.RunSynchronously) it
|
let await it = (Async.AwaitTask >> Async.RunSynchronously) it
|
||||||
seq {
|
seq {
|
||||||
debug (fun () -> $"Considering URL {textLink}")
|
debug (fun () -> $"Considering URL {textLink}")
|
||||||
|
@ -79,7 +79,7 @@ module DataImplementation =
|
|||||||
let createSQLite connStr : IData =
|
let createSQLite connStr : IData =
|
||||||
Sqlite.Configuration.useConnectionString connStr
|
Sqlite.Configuration.useConnectionString connStr
|
||||||
let log = sp.GetRequiredService<ILogger<SQLiteData>>()
|
let log = sp.GetRequiredService<ILogger<SQLiteData>>()
|
||||||
let conn = new SqliteConnection(connStr)
|
let conn = Sqlite.Configuration.dbConn ()
|
||||||
log.LogInformation $"Using SQLite database {conn.DataSource}"
|
log.LogInformation $"Using SQLite database {conn.DataSource}"
|
||||||
SQLiteData(conn, log, Json.configure (JsonSerializer.CreateDefault()))
|
SQLiteData(conn, log, Json.configure (JsonSerializer.CreateDefault()))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user