SharpRSS/SharpRss/DbAccess.cs
2023-06-05 15:15:18 +02:00

221 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Security.Policy;
using System.Threading.Tasks;
using Dapper;
using Microsoft.Data.Sqlite;
using Serilog;
using SharpRss.Models;
namespace SharpRss
{
internal static class DbAccess
{
static DbAccess()
{
Initialize();
}
private static readonly string ConnectionString = $"Data Source={Path.Combine(Environment.CurrentDirectory, "sharp_rss.sqlite")};";
private static bool _isInitialized;
public static async Task SetSyndicationAsync(SyndicationContainer synContainer)
{
if (synContainer.Category != null)
await SetCategoryAsync(synContainer.Category);
if (synContainer.FeedModel != null)
await SetFeedAsync(synContainer.FeedModel);
if (synContainer.FeedItems != null && synContainer.FeedItems.Any())
await SetFeedItemsAsync(synContainer.FeedItems);
}
public static async Task<HashSet<CategoryModel>> GetCategoriesAsync()
{
HashSet<CategoryModel> categories = new HashSet<CategoryModel>();
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
await using DbDataReader reader = await dbc.ExecuteReaderAsync("SELECT * FROM category");
while (await reader.ReadAsync())
{
CategoryModel categoryModel = new CategoryModel()
{
Id = reader["id"].ToString(),
Name = reader["name"].ToString(),
HexColor = reader["hex_color"].ToString(),
FeedCount = 0, // Not implemented.
Icon = reader["icon"].ToString()
};
categories.Add(categoryModel);
}
return categories;
}
public static async Task<bool> SetCategoryAsync(CategoryModel category)
{
if (category == null) return false;
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO category (id, hex_color, icon, name) VALUES (IFNULL((SELECT id FROM category WHERE name=@Name), @Id), @HexColor, @Icon, @Name)",
new { category.Id, category.HexColor, category.Icon, category.Name });
return affected > 0;
}
public static async Task<bool> DeleteCategory(CategoryModel category)
{
if (category == null) return false;
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
int affected = await dbc.ExecuteAsync("DELETE FROM category WHERE id=@Id; UPDATE feed SET category_id=NULL WHERE category_id=@Id",new { category.Id });
return affected > 0;
}
public static async Task SetFeedAsync(FeedModel feed)
{
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
DynamicParameters parameters = new DynamicParameters();
parameters.Add("url", feed.OriginalUrl ?? string.Empty);
parameters.Add("title", feed.Title ?? string.Empty);
parameters.Add("categoryId", feed.CategoryId ?? string.Empty);
parameters.Add("feedType", feed.FeedType ?? string.Empty);
parameters.Add("description", feed.Description ?? string.Empty);
parameters.Add("language", feed.Language ?? string.Empty);
parameters.Add("copyright", feed.Copyright ?? string.Empty);
parameters.Add("publicationDate", feed.PublicationDate?.ToUnixTimeMilliseconds() ?? 0);
parameters.Add("lastUpdated", feed.LastUpdated?.ToUnixTimeMilliseconds() ?? 0);
parameters.Add("categories", feed.Categories.Any() ? string.Join(',', feed.Categories) : string.Empty);
parameters.Add("imageUrl", feed.ImageUrl ?? string.Empty);
int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO feed (url, title, category_id, feed_type, feed_version, description, language, copyright, publication_date, last_updated, categories, image_url) VALUES (@url, @title, @categoryId, @feedType, @feedVersion, @description, @language, @copyright, @publicationDate, @lastUpdated, @categories, @imageUrl)", param: parameters);
/*int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO feed (url, title, category_id, feed_type, feed_version, description, language, copyright, publication_date, last_updated, categories, image_url) VALUES (@Url, @Title, @CategoryId, @FeedType, @FeedVersion, @Description, @language, @Copyright, @PublicationDate, @LastUpdated, @Categories, @ImageUrl)",
new
{
Url = feed.OriginalUrl ?? string.Empty,
Title = feed.Title ?? string.Empty,
CategoryId = feed.CategoryId ?? string.Empty,
FeedType = feed.FeedType ?? string.Empty,
Description = feed.Description ?? string.Empty,
Language = feed.Language ?? string.Empty,
Copyright = feed.Copyright ?? string.Empty,
PublicationDate = feed.PublicationDate?.ToUnixTimeMilliseconds() ?? 0,
LastUpdated = feed.LastUpdated?.ToUnixTimeMilliseconds() ?? 0,
Categories = feed.Categories.Any() ? string.Join(',', feed.Categories) : string.Empty,
ImageUrl = feed.ImageUrl ?? string.Empty
});*/
if (affected == 0)
Log.Warning("Failed to add feed: {FeedUrl}", feed.OriginalUrl);
}
public static async Task<HashSet<FeedModel>> GetFeedsAsync(string[]? categoryIds = null)
{
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
HashSet<FeedModel> feeds = new HashSet<FeedModel>();
await using DbDataReader reader = await dbc.ExecuteReaderAsync(categoryIds == null ? "SELECT * FROM feed" : "SELECT * FROM feed WHERE category_id IN(@CatIds)", new { CatIds = categoryIds });
while (await reader.ReadAsync())
{
FeedModel feedModel = new FeedModel()
{
OriginalUrl = reader["url"].ToString(),
Title = reader["title"].ToString(),
CategoryId = reader["category_id"].ToString(),
FeedType = reader["feed_type"].ToString(),
FeedVersion = reader["feed_version"].ToString(),
Description = reader["description"].ToString(),
Language = reader["language"].ToString(),
Copyright = reader["copyright"].ToString(),
PublicationDate = DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["publication_date"].ToString())),
LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["last_updated"].ToString())),
Categories = reader["categories"].ToString().Split(','),
ImageUrl = reader["image_url"].ToString()
};
feeds.Add(feedModel);
}
return feeds;
}
public static async Task SetFeedItemsAsync(HashSet<FeedItemModel> items)
{
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
int totalAffected = 0;
await using SqliteTransaction dbTransaction = dbc.BeginTransaction();
foreach (FeedItemModel item in items)
{
DynamicParameters parameters = new DynamicParameters();
parameters.Add("id", item.Id ?? string.Empty);
parameters.Add("feedurl", item.FeedUrl ?? string.Empty);
parameters.Add("read", item.Read.ToString());
parameters.Add("description", item.Description ?? string.Empty);
parameters.Add("link", item.Link ?? string.Empty);
parameters.Add("lastUpdated", item.LastUpdated?.ToUnixTimeMilliseconds() ?? 0);
parameters.Add("publishingDate", item.PublishingDate?.ToUnixTimeMilliseconds() ?? 0);
parameters.Add("authors", item.Authors.Any() ? string.Join(',', item.Authors) : string.Empty);
parameters.Add("categories", item.Categories.Any() ? string.Join(',', item.Categories) : string.Empty);
parameters.Add("content", item.Content ?? string.Empty);
int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) VALUES (@id, @feedUrl, @read, @title, @description, @link, @lastUpdated, @publishingDate, @authors, @categories, @content)",
parameters,
transaction: dbTransaction);
/*int affected = await dbc.ExecuteAsync("INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content) VALUES (@Id, @FeedUrl, @Read, @Title, Description, @Link, @LastUpdated, @PublishingDate, @Authors, @Categories, @Content)",
transaction: dbTransaction,
param: new
{
Id = item.Id ?? string.Empty,
FeedUrl = item.FeedUrl ?? string.Empty,
Read = item.Read.ToString(),
Description = item.Description ?? string.Empty,
Link = item.Link ?? string.Empty,
LastUpdated = item.LastUpdated?.ToUnixTimeMilliseconds() ?? 0,
PublishingDate = item.PublishingDate?.ToUnixTimeMilliseconds() ?? 0,
Authors = item.Authors.Any() ? string.Join(',', item.Authors) : string.Empty,
Categories = item.Categories.Any() ? string.Join(',', item.Categories) : string.Empty,
Content = item.Content ?? string.Empty
});*/
totalAffected += affected;
}
dbTransaction.Commit();
}
public static async Task<HashSet<FeedItemModel>> GetFeedItemsAsync(string[]? feedUrls)
{
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
HashSet<FeedItemModel> items = new HashSet<FeedItemModel>();
await using DbDataReader reader = await dbc.ExecuteReaderAsync(feedUrls == null ? "SELECT * FROM feed_item" : "SELECT * FROM feed_item WHERE feed_item.feed_url IN(@Urls)", new { Urls = feedUrls });
while (await reader.ReadAsync())
{
FeedItemModel feedItemModel = new FeedItemModel()
{
Id = reader["id"].ToString(),
FeedUrl = reader["feed_url"].ToString(),
Read = bool.Parse(reader["read"].ToString()),
Title = reader["title"].ToString(),
Description = reader["description"].ToString(),
Link = reader["link"].ToString(),
LastUpdated = DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["last_updated"].ToString())),
PublishingDate = DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(reader["publishing_date"].ToString())),
Authors = reader["authors"].ToString().ToString().Split(','),
Categories = reader["categories"].ToString().Split(','),
Content = reader["content"].ToString()
};
items.Add(feedItemModel);
}
return items;
}
private static async void Initialize()
{
if (_isInitialized) return;
Log.Verbose("Checking database...");
await using SqliteConnection dbc = new SqliteConnection(ConnectionString);
dbc.Open();
Log.Verbose("Checking table: {Table}", "category");
await dbc.ExecuteAsync("CREATE TABLE IF NOT EXISTS category (id STRING PRIMARY KEY, name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING)");
Log.Verbose("Checking table: {Table}", "feed");
await dbc.ExecuteAsync("CREATE TABLE IF NOT EXISTS feed (url STRING PRIMARY KEY, title STRING, category_id STRING, feed_type STRING, feed_version STRING, description STRING, language STRING, copyright STRING, publication_date INT, last_updated INT, categories STRING, image_url STRING)");
Log.Verbose("Checking table: {Table}", "feed_item");
await dbc.ExecuteAsync("CREATE TABLE IF NOT EXISTS feed_item (id STRING PRIMARY KEY, feed_url STRING, read STRING, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, authors STRING, categories STRING, content STRING)");
Log.Verbose("Checking database done!");
_isInitialized = true;
}
}
}