using System; using System.Collections.Generic; using System.Data.Common; using System.IO; using System.Linq; 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) { } public static async Task> GetCategoriesAsync() { HashSet categories = new HashSet(); 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 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 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(); 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, Title = feed.Title, CategoryId = feed.CategoryId, FeedType = feed.FeedType, Description = feed.Description, Language = feed.Language, Copyright = feed.Copyright, PublicationDate = feed.PublicationDate?.ToUnixTimeMilliseconds() ?? 0, LastUpdated = feed.LastUpdated?.ToUnixTimeMilliseconds() ?? 0, Categories = string.Join(',', feed.Categories), ImageUrl = feed.ImageUrl }); if (affected == 0) Log.Warning("Failed to add feed: {FeedUrl}", feed.OriginalUrl); } public static async Task> GetFeedsAsync(string[]? categoryIds = null) { await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); HashSet feeds = new HashSet(); 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 items) { } public static async Task> GetFeedItemsAsync(string[]? feedUrls) { await using SqliteConnection dbc = new SqliteConnection(ConnectionString); dbc.Open(); HashSet items = new HashSet(); 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 = int.TryParse(reader["read"].ToString(), out int parsedValue) && parsedValue != 0, 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 INT, 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; } } }