2023-06-04 19:00:46 +02:00
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
{
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 )
{
2023-06-05 15:15:18 +02:00
if ( synContainer . Category ! = null )
2023-06-10 19:06:30 +02:00
{
CategoryModel ? catModel = await SetCategoryAsync ( synContainer . Category ) ;
if ( catModel ! = null )
synContainer . FeedModel . CategoryId = catModel . Id ;
}
2023-06-05 15:15:18 +02:00
if ( synContainer . FeedModel ! = null )
await SetFeedAsync ( synContainer . FeedModel ) ;
if ( synContainer . FeedItems ! = null & & synContainer . FeedItems . Any ( ) )
await SetFeedItemsAsync ( synContainer . FeedItems ) ;
2023-06-04 19:00:46 +02:00
}
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 ;
}
2023-06-10 19:06:30 +02:00
public static async Task < CategoryModel ? > SetCategoryAsync ( CategoryModel category )
2023-06-04 19:00:46 +02:00
{
2023-06-10 19:06:30 +02:00
CategoryModel ? modelReturn = null ;
if ( category = = null ) return modelReturn ;
2023-06-04 19:00:46 +02:00
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 } ) ;
2023-06-10 19:06:30 +02:00
if ( affected < = 0 ) return modelReturn ;
var catModel = await GetCategoriesAsync ( ) ;
modelReturn = catModel . Where ( x = > x . Name = = category . Name ) . ToHashSet ( ) . FirstOrDefault ( ) ? ? null ;
return modelReturn ;
2023-06-04 19:00:46 +02:00
}
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 ( ) ;
2023-06-10 19:06:30 +02:00
/ * await using SqliteCommand cmd = new SqliteCommand ( @ "INSERT OR REPLACE INTO feed
2023-06-09 15:13:53 +02:00
( 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 ) ", dbc)
2023-06-05 18:11:43 +02:00
{
Parameters =
{
new SqliteParameter ( "Url" , feed . OriginalUrl ? ? string . Empty ) ,
new SqliteParameter ( "Title" , feed . Title ? ? string . Empty ) ,
new SqliteParameter ( "CategoryId" , feed . CategoryId ? ? string . Empty ) ,
new SqliteParameter ( "FeedType" , feed . FeedType ? ? string . Empty ) ,
2023-06-09 15:13:53 +02:00
new SqliteParameter ( "FeedVersion" , feed . FeedVersion ? ? string . Empty ) ,
2023-06-05 18:11:43 +02:00
new SqliteParameter ( "Description" , feed . Description ? ? string . Empty ) ,
new SqliteParameter ( "Language" , feed . Language ? ? string . Empty ) ,
new SqliteParameter ( "Copyright" , feed . Copyright ? ? string . Empty ) ,
new SqliteParameter ( "PublicationDate" , feed . PublicationDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "LastUpdated" , feed . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "Categories" , feed . Categories ! = null & & feed . Categories . Any ( ) ? string . Join ( ',' , feed . Categories ) : string . Empty ) ,
new SqliteParameter ( "ImageUrl" , feed . ImageUrl ? ? string . Empty )
}
} ;
2023-06-10 19:06:30 +02:00
int exec = await cmd . ExecuteNonQueryAsync ( ) ; * /
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 ) ",
2023-06-05 15:15:18 +02:00
new
{
Url = feed . OriginalUrl ? ? string . Empty ,
Title = feed . Title ? ? string . Empty ,
CategoryId = feed . CategoryId ? ? string . Empty ,
FeedType = feed . FeedType ? ? string . Empty ,
2023-06-09 15:13:53 +02:00
FeedVersion = feed . FeedVersion ? ? string . Empty ,
2023-06-05 15:15:18 +02:00
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 ,
2023-06-10 19:06:30 +02:00
Categories = feed . Categories ! = null & & feed . Categories . Any ( ) ? string . Join ( ',' , feed . Categories ) : string . Empty ,
2023-06-05 15:15:18 +02:00
ImageUrl = feed . ImageUrl ? ? string . Empty
2023-06-09 15:13:53 +02:00
} ) ;
if ( affected = = 0 )
2023-06-10 19:06:30 +02:00
Log . Warning ( "Failed to add feed: {FeedUrl}" , feed . OriginalUrl ) ;
2023-06-04 19:00:46 +02:00
}
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 > ( ) ;
2023-06-10 19:06:30 +02:00
await using DbDataReader reader = await dbc . ExecuteReaderAsync ( categoryIds = = null ? "SELECT * FROM feed WHERE category_id == ''" : "SELECT * FROM feed WHERE category_id IN(@CatIds)" , new { CatIds = categoryIds } ) ;
2023-06-04 19:00:46 +02:00
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 )
{
2023-06-05 15:15:18 +02:00
await using SqliteConnection dbc = new SqliteConnection ( ConnectionString ) ;
dbc . Open ( ) ;
int totalAffected = 0 ;
await using SqliteTransaction dbTransaction = dbc . BeginTransaction ( ) ;
foreach ( FeedItemModel item in items )
{
2023-06-10 19:06:30 +02:00
/ * await using SqliteCommand cmd = new SqliteCommand ( @ "INSERT OR REPLACE INTO feed_item (id, feed_url, read, title, description, link, last_updated, publishing_date, authors, categories, content)
2023-06-09 15:13:53 +02:00
VALUES ( @Id , @FeedUrl , @Read , @Title , @Description , @Link , @LastUpdated , @PublishingDate , @Authors , @Categories , @Content ) ", dbc, dbTransaction)
2023-06-05 18:11:43 +02:00
{
Parameters =
{
new SqliteParameter ( "Id" , item . Id ? ? string . Empty ) ,
new SqliteParameter ( "FeedUrl" , item . FeedUrl ? ? string . Empty ) ,
new SqliteParameter ( "Read" , item . Read . ToString ( ) ) ,
2023-06-09 15:13:53 +02:00
new SqliteParameter ( "Title" , item . Title ) ,
2023-06-05 18:11:43 +02:00
new SqliteParameter ( "Description" , item . Description ? ? string . Empty ) ,
new SqliteParameter ( "Link" , item . Link ? ? string . Empty ) ,
new SqliteParameter ( "LastUpdated" , item . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "PublishingDate" , item . PublishingDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ) ,
new SqliteParameter ( "Authors" , item . Authors . Any ( ) ? string . Join ( ',' , item . Authors ) : string . Empty ) ,
new SqliteParameter ( "Categories" , item . Categories . Any ( ) ? string . Join ( ',' , item . Categories ) : string . Empty ) ,
new SqliteParameter ( "Content" , item . Content ? ? string . Empty )
}
} ;
2023-06-10 19:06:30 +02:00
int exec = await cmd . ExecuteNonQueryAsync ( ) ; * /
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)
2023-06-09 15:13:53 +02:00
VALUES ( @Id , @FeedUrl , @Read , @Title , @Description , @Link , @LastUpdated , @PublishingDate , @Authors , @Categories , @Content ) ",
2023-06-05 15:15:18 +02:00
transaction : dbTransaction ,
param : new
{
Id = item . Id ? ? string . Empty ,
FeedUrl = item . FeedUrl ? ? string . Empty ,
Read = item . Read . ToString ( ) ,
2023-06-09 15:13:53 +02:00
Title = item . Title ? ? string . Empty ,
2023-06-05 15:15:18 +02:00
Description = item . Description ? ? string . Empty ,
Link = item . Link ? ? string . Empty ,
LastUpdated = item . LastUpdated ? . ToUnixTimeMilliseconds ( ) ? ? 0 ,
PublishingDate = item . PublishingDate ? . ToUnixTimeMilliseconds ( ) ? ? 0 ,
2023-06-10 19:06:30 +02:00
Authors = item . Authors ! = null & & item . Authors . Any ( ) ? string . Join ( ',' , item . Authors ) : string . Empty ,
Categories = item . Categories ! = null & & item . Categories . Any ( ) ? string . Join ( ',' , item . Categories ) : string . Empty ,
2023-06-05 15:15:18 +02:00
Content = item . Content ? ? string . Empty
2023-06-09 15:13:53 +02:00
} ) ;
2023-06-10 19:06:30 +02:00
totalAffected + = affected ;
2023-06-05 15:15:18 +02:00
}
dbTransaction . Commit ( ) ;
2023-06-04 19:00:46 +02:00
}
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 ( ) ,
2023-06-05 15:15:18 +02:00
Read = bool . Parse ( reader [ "read" ] . ToString ( ) ) ,
2023-06-04 19:00:46 +02:00
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 ;
}
2023-06-10 19:06:30 +02:00
public static async void Initialize ( )
2023-06-04 19:00:46 +02:00
{
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" ) ;
2023-06-05 15:15:18 +02:00
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)" ) ;
2023-06-04 19:00:46 +02:00
Log . Verbose ( "Checking database done!" ) ;
_isInitialized = true ;
}
}
}