2023-05-18 01:27:11 +02:00
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
{
2023-05-18 20:15:31 +02:00
internal class DatabaseService : IDisposable
2023-05-18 01:27:11 +02:00
{
internal DatabaseService ( )
{
_sqlConn = new SqliteConnection ( _connectionString ) ;
InitializeDb ( ) ;
}
private readonly SqliteConnection _sqlConn ;
2023-05-20 00:04:45 +02:00
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" ;
2023-05-18 01:27:11 +02:00
2023-05-21 21:56:37 +02:00
// Group
public async Task < HashSet < GroupModel ? > > GetGroupsAsync ( string? groupName = null )
2023-05-20 00:04:45 +02:00
{
2023-05-21 21:56:37 +02:00
_sqlConn . Open ( ) ;
SqliteCommand cmd = new SqliteCommand ( groupName ! = null ? $"SELECT * FROM {_groupTable} WHERE name=@name;" : $"SELECT * FROM {_groupTable}" , _sqlConn )
{
Parameters =
{
new SqliteParameter ( "name" , groupName )
}
} ;
await using SqliteDataReader reader = await cmd . ExecuteReaderAsync ( ) ;
HashSet < GroupModel ? > groups = new HashSet < GroupModel ? > ( ) ;
while ( reader . Read ( ) )
{
groups . Add ( new GroupModel ( )
{
Name = reader [ "name" ] . ToString ( ) ,
HexColor = reader [ "hex_color" ] . ToString ( ) ,
Icon = reader [ "icon" ] . ToString ( ) ,
Id = reader [ "id" ] . ToString ( )
} ) ;
}
_sqlConn . Close ( ) ;
return groups ;
2023-05-20 00:04:45 +02:00
}
2023-05-21 21:56:37 +02:00
/// <summary>
/// Creates a group if not exists else will update the group.
/// </summary>
/// <param name="groupModel"></param>
/// <returns></returns>
public async Task < bool > SetGroupAsync ( GroupModel groupModel )
2023-05-18 01:27:11 +02:00
{
2023-05-21 21:56:37 +02:00
bool result = false ;
2023-05-18 01:27:11 +02:00
_sqlConn . Open ( ) ;
2023-05-21 21:56:37 +02:00
SqliteCommand cmd = new SqliteCommand ( $"INSERT OR REPLACE INTO {_groupTable} (id, hex_color, icon, name) VALUES (IFNULL((SELECT id FROM {_groupTable} WHERE name=@name), @id), @hexColor, @icon, @name)" , _sqlConn )
2023-05-18 01:27:11 +02:00
{
2023-05-21 21:56:37 +02:00
Parameters =
{
new SqliteParameter ( "id" , groupModel . Id ) ,
new SqliteParameter ( "hexColor" , groupModel . HexColor ) ,
new SqliteParameter ( "icon" , groupModel . Icon ) ,
new SqliteParameter ( "name" , groupModel . Name )
}
} ;
int affected = await cmd . ExecuteNonQueryAsync ( ) ;
if ( affected ! = 0 )
result = true ;
2023-05-18 01:27:11 +02:00
_sqlConn . Close ( ) ;
return result ;
}
2023-05-21 21:56:37 +02:00
public async Task < bool > RemoveGroupAsync ( GroupModel groupModel )
2023-05-18 01:27:11 +02:00
{
2023-05-21 21:56:37 +02:00
bool result = false ;
2023-05-18 01:27:11 +02:00
_sqlConn . Open ( ) ;
2023-05-21 21:56:37 +02:00
// Remove the group and remove the feeds that were part of the group.
SqliteCommand cmd = new SqliteCommand ( $"DELETE FROM {_groupTable} WHERE id=@id; UPDATE {_feedTable} SET group_id=NULL WHERE group_id=@id" , _sqlConn )
2023-05-18 01:27:11 +02:00
{
2023-05-21 21:56:37 +02:00
Parameters =
{
new SqliteParameter ( "id" , groupModel . Id )
}
} ;
int affected = await cmd . ExecuteNonQueryAsync ( ) ;
if ( affected ! = 0 )
result = true ;
2023-05-18 01:27:11 +02:00
_sqlConn . Close ( ) ;
return result ;
}
2023-05-21 21:56:37 +02:00
// Feed
public async Task < HashSet < FeedModel > > GetFeedsAsync ( string? feedName = null )
2023-05-18 01:27:11 +02:00
{
2023-05-21 21:56:37 +02:00
HashSet < FeedModel > feeds = new HashSet < FeedModel > ( ) ;
2023-05-18 01:27:11 +02:00
_sqlConn . Open ( ) ;
2023-05-21 21:56:37 +02:00
SqliteCommand cmd = new SqliteCommand ( feedName ! = null ? $"SELECT * FROM {_feedTable} WHERE name=@name" : $"SELECT * FROM {_feedTable}" , _sqlConn )
2023-05-20 00:04:45 +02:00
{
2023-05-21 21:56:37 +02:00
Parameters =
{
new SqliteParameter ( "name" , feedName )
}
} ;
int affected = await cmd . ExecuteNonQueryAsync ( ) ;
Log . Verbose ( "{FeedAmount} feeds found!" , affected ) ;
2023-05-18 01:27:11 +02:00
_sqlConn . Close ( ) ;
2023-05-21 21:56:37 +02:00
return feeds ;
2023-05-18 01:27:11 +02:00
}
2023-05-21 21:56:37 +02:00
public async Task < bool > AddFeedAsync ( FeedModel feedModel )
2023-05-18 01:27:11 +02:00
{
2023-05-21 21:56:37 +02:00
bool result = false ;
2023-05-18 01:27:11 +02:00
_sqlConn . Open ( ) ;
2023-05-21 21:56:37 +02:00
SqliteCommand cmd = new SqliteCommand ( $"INSERT OR REPLACE INTO {_feedTable} (id, url, group_id, feed_type, description, language, copyright, date_added, last_updated, image_url)" +
$"VALUES (IFNULL((SELECT id FROM {_feedTable} WHERE url=@url), @id), @url, @groupId, @feedType, @description, @language, @copyright, @dateAdded, @lastUpdated, @imageUrl)" , _sqlConn )
2023-05-20 00:04:45 +02:00
{
2023-05-21 21:56:37 +02:00
Parameters =
{
new SqliteParameter ( "id" , feedModel . Id ) ,
new SqliteParameter ( "url" , feedModel . Url ) ,
new SqliteParameter ( "groupId" , feedModel . GroupId ) ,
new SqliteParameter ( "feedType" , feedModel . FeedType ) ,
new SqliteParameter ( "description" , feedModel . Description ) ,
new SqliteParameter ( "language" , feedModel . Language ) ,
new SqliteParameter ( "copyright" , feedModel . Copyright ) ,
new SqliteParameter ( "dateAdded" , feedModel . DateAdded . ToUnixTimeMilliseconds ( ) ) ,
new SqliteParameter ( "lastUpdated" , feedModel . LastUpdated . ToUnixTimeMilliseconds ( ) ) ,
new SqliteParameter ( "imageUrl" , feedModel . ImageUrl )
}
} ;
int affected = await cmd . ExecuteNonQueryAsync ( ) ;
if ( affected ! = 0 )
result = true ;
2023-05-18 01:27:11 +02:00
_sqlConn . Close ( ) ;
2023-05-21 21:56:37 +02:00
return result ;
2023-05-18 01:27:11 +02:00
}
2023-05-21 21:56:37 +02:00
// Feed item
2023-05-18 01:27:11 +02:00
private async void InitializeDb ( )
{
Log . Verbose ( "Checking database..." ) ;
2023-05-19 14:40:57 +02:00
HashSet < string > failed = new HashSet < string > ( ) ;
2023-05-18 01:27:11 +02:00
_sqlConn . Open ( ) ;
2023-05-20 00:04:45 +02:00
Log . Verbose ( "Checking table: {Table}" , _groupTable ) ;
2023-05-21 21:56:37 +02:00
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)" ) ;
2023-05-19 14:40:57 +02:00
if ( queryResponse . Any ( ) ) failed . Add ( "category_data" ) ;
2023-05-20 00:04:45 +02:00
Log . Verbose ( "Checking table: {Table}" , _feedTable ) ;
2023-05-21 21:56:37 +02:00
queryResponse = await _sqlConn . QueryAsync ( $"CREATE TABLE IF NOT EXISTS {_feedTable} (id STRING PRIMARY KEY, url STRING NOT NULL, group_id STRING DEFAULT NULL, feed_type STRING, description STRING, language STRING, copyright STRING, date_added INT, last_updated INT, image_url STRING)" ) ;
2023-05-19 14:40:57 +02:00
if ( queryResponse . Any ( ) ) failed . Add ( "feed_data" ) ;
2023-05-20 00:04:45 +02:00
Log . Verbose ( "Checking table: {Table}" , _feedItemTable ) ;
2023-05-21 21:56:37 +02:00
queryResponse = await _sqlConn . QueryAsync ( $"CREATE TABLE IF NOT EXISTS {_feedItemTable} (id STRING PRIMARY KEY, feed_id STRING DEFAULT NULL, read INT, type STRING, title STRING, description STRING, link STRING, last_updated INT, publishing_date INT, author STRING, categories STRING, content STRING)" ) ;
2023-05-20 00:04:45 +02:00
if ( queryResponse . Any ( ) ) failed . Add ( "feed_item_data" ) ;
2023-05-19 14:40:57 +02:00
_sqlConn . Close ( ) ;
if ( failed . Any ( ) )
2023-05-18 01:27:11 +02:00
{
2023-05-19 14:40:57 +02:00
var joined = string . Join ( ',' , failed ) ;
Log . Error ( "Failed to initialize table(s): {TableNames}" , joined ) ;
2023-05-18 01:27:11 +02:00
}
2023-05-19 14:40:57 +02:00
else
Log . Verbose ( "Checking database done!" ) ;
2023-05-18 01:27:11 +02:00
}
public void Dispose ( )
{
_sqlConn . Dispose ( ) ;
}
}
}