using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Dapper; using Microsoft.Data.Sqlite; using Serilog; using SharpRss.Models; namespace SharpRss.Services { internal class DatabaseService : IDisposable { internal DatabaseService() { _sqlConn = new SqliteConnection(_connectionString); InitializeDb(); } private readonly SqliteConnection _sqlConn; private readonly string _connectionString = $"Data Source={Path.Combine(Environment.CurrentDirectory, "sharp_rss.sqlite")};"; private readonly string _groupTable = "group_data"; private readonly string _feedTable = "feed_data"; private readonly string _feedItemTable = "feed_item_data"; public async Task RemoveGroupFromFeedsAsync(string groupId) { await _sqlConn.QueryAsync("UPDATE feed_data SET group_id=NULL WHERE group_id=@GroupId", new { GroupId = groupId }); } public async Task AddCategoriesAsync(HashSet categories) { bool result = true; _sqlConn.Open(); foreach (var categoryModel in categories) { await _sqlConn.QueryAsync("INSERT INTO category_data (name, hex_color, path_icon, category_id) VALUES(@catName, @hexColor, @pathIcon, @categoryId) ON CONFLICT(name) DO UPDATE SET hex_color=@hexColor, path_icon=@pathIcon", new { catName = categoryModel.Name, hexColor = categoryModel.HexColor, pathIcon = categoryModel.Icon, categoryId = categoryModel.Id }); } _sqlConn.Close(); return result; } public async Task AddFeedsAsync(HashSet feeds) { bool result = true; _sqlConn.Open(); foreach (var feedModel in feeds) { await _sqlConn.QueryAsync("INSERT OR REPLACE INTO feed_data(url, feed_id, category_id) VALUES(@url, @feedId, @categoryId) ON CONFLICT(url) DO UPDATE SET category_id=@categoryId", new { url = feedModel.FeedUrl, feedId = feedModel.FeedId, categoryId = feedModel.GroupId }); } _sqlConn.Close(); return result; } public async Task> GetCategoriesAsync() { HashSet categories = new HashSet(); _sqlConn.Open(); SqliteCommand cmd = _sqlConn.CreateCommand(); cmd.CommandText = "SELECT * FROM category_data"; await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); while (reader.Read()) { //categories.Add(GroupModel.Create(reader["name"].ToString(), reader["hex_color"].ToString(), reader["category_id"].ToString())); } _sqlConn.Close(); return categories; } public async Task> GetFeedsAsync(string? categoryId = null) { HashSet feeds = new HashSet(); _sqlConn.Open(); SqliteCommand cmd = _sqlConn.CreateCommand(); cmd.CommandText = categoryId == null ? "SELECT * FROM feed_data" : "SELECT * FROM feed_data WHERE category_id=@categoryId"; if (categoryId != null) cmd.Parameters.Add(new SqliteParameter("categoryId", categoryId)); await using SqliteDataReader reader = await cmd.ExecuteReaderAsync(); while (reader.Read()) { //feeds.Add(FeedModel.Create(reader["url"].ToString(), reader["feed_id"].ToString(), reader["category_id"].ToString())); } _sqlConn.Close(); return feeds; } private async void InitializeDb() { Log.Verbose("Checking database..."); HashSet failed = new HashSet(); _sqlConn.Open(); Log.Verbose("Checking table: {Table}", _groupTable); var queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_groupTable} (name STRING NOT NULL, hex_color STRING NOT NULL, icon STRING, id STRING PRIMARY KEY, CONSTRAINT name UNIQUE (name))"); if (queryResponse.Any()) failed.Add("category_data"); Log.Verbose("Checking table: {Table}", _feedTable); queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_feedTable} (url STRING NOT NULL, id STRING PRIMARY KEY, group_id STRING DEFAULT NULL, CONSTRAINT url, UNIQUE (url))"); if (queryResponse.Any()) failed.Add("feed_data"); Log.Verbose("Checking table: {Table}", _feedItemTable); queryResponse = await _sqlConn.QueryAsync($"CREATE TABLE IF NOT EXISTS {_feedItemTable} (id STRING PRIMARY KEY, feed_id STRING NOT NULL)"); if (queryResponse.Any()) failed.Add("feed_item_data"); _sqlConn.Close(); if (failed.Any()) { var joined = string.Join(',', failed); Log.Error("Failed to initialize table(s): {TableNames}", joined); } else Log.Verbose("Checking database done!"); } public void Dispose() { _sqlConn.Dispose(); } } }