Compare commits

...

14 Commits

Author SHA1 Message Date
06427d53f3 Merge pull request 'features/api-cleanup' (#15) from features/api-cleanup into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #15
2023-07-29 09:47:27 -07:00
82dea60126 Updated DiscordWebHooks to handle userId
All checks were successful
continuous-integration/drone/pr Build is passing
2023-07-29 09:44:15 -07:00
c92d13797f minor adjustments 2023-07-23 22:58:25 -07:00
463cb893e8 Logging update 2023-07-23 22:57:54 -07:00
a416746269 DiscordNotificationController.cs got a new route, moved over to ActionResults, records are now tracking the userId and returning common result classes 2023-07-23 22:57:35 -07:00
7344d2bdd2 CodeProjectController.cs was updated to support ActionResults and new route 2023-07-23 22:56:26 -07:00
19d8e3e925 ArticlesController.cs was cleaned up and supports results with details now 2023-07-23 22:56:06 -07:00
fa14562fd4 Updated namespaces, consts, startup and warnings are now errors in the api project 2023-07-23 22:55:20 -07:00
2bc99afe63 Moved controllers to v1 namespace and updated routes 2023-07-23 22:54:00 -07:00
5df2996947 Database project now has warnings converted to errors 2023-07-23 22:52:59 -07:00
7b24ba16f7 moved the IdentityService.cs to services and warnings are now errors 2023-07-23 22:52:18 -07:00
d7242c12c8 Created a master config to match the appsettings 2023-07-23 22:51:22 -07:00
9b86f9e84d Cleaned up the consts and moved the Authorization.cs to the correct namespace 2023-07-23 22:50:41 -07:00
83203f967d Moved domain objects from the API to Domain 2023-07-23 22:50:03 -07:00
49 changed files with 620 additions and 349 deletions

View File

@ -1,60 +0,0 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newsbot.Collector.Domain.Dto;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models;
namespace Newsbot.Collector.Api.Controllers;
[ApiController]
[Route("api/articles")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ArticlesController : ControllerBase
{
private readonly ILogger<ArticlesController> _logger;
private readonly IArticlesRepository _articles;
private readonly ISourcesRepository _sources;
public ArticlesController(ILogger<ArticlesController> logger, IArticlesRepository articles, ISourcesRepository sources)
{
_logger = logger;
_articles = articles;
_sources = sources;
}
[HttpGet(Name = "GetArticles")]
public IEnumerable<ArticleDto> Get()
{
var res = new List<ArticleDto>();
var items = _articles.List(0, 25);
foreach (var item in items) res.Add(ArticleDto.Convert(item));
return res;
}
[HttpGet("{id:guid}")]
[EndpointDescription("Returns the article based on the Id value given.")]
public ArticleDto GetById(Guid id)
{
var item = _articles.GetById(id);
return ArticleDto.Convert(item);
}
[HttpGet("{id:guid}/details")]
public ArticleDetailsDto GetDetailsById(Guid id)
{
var item = _articles.GetById(id);
var sourceItem = _sources.GetById(item.SourceId);
return ArticleDetailsDto.Convert(item, sourceItem);
}
[HttpGet("by/{sourceId:guid}")]
public IEnumerable<ArticleDto> GetBySourceId(Guid sourceId, int page = 0, int count = 25)
{
var res = new List<ArticleDto>();
var items = _articles.ListBySourceId(sourceId, page, count);
foreach (var item in items) res.Add(ArticleDto.Convert(item));
return res;
}
}

View File

@ -1,130 +0,0 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newsbot.Collector.Domain.Dto;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;
namespace Newsbot.Collector.Api.Controllers;
[ApiController]
[Route("api/subscriptions")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class DiscordNotificationController : ControllerBase
{
private readonly ILogger<DiscordNotificationController> _logger;
private readonly IDiscordWebHooksRepository _discord;
private readonly ISourcesRepository _sources;
private readonly IDiscordNotificationRepository _discordNotification;
public DiscordNotificationController(ILogger<DiscordNotificationController> logger, IDiscordWebHooksRepository discord, ISourcesRepository sources, IDiscordNotificationRepository discordNotification)
{
_logger = logger;
_discord = discord;
_sources = sources;
_discordNotification = discordNotification;
}
[HttpGet(Name = "ListSubscriptions")]
public IEnumerable<DiscordNotificationDto> List(int page)
{
var res = new List<DiscordNotificationDto>();
var items = _discordNotification.List(page);
foreach (var item in items) res.Add(DiscordNotificationDto.Convert(item));
return res;
}
[HttpGet("{id}")]
public DiscordNotificationDto GetById(Guid id)
{
return DiscordNotificationDto.Convert(_discordNotification.GetById(id));
}
[HttpGet("{id}/details")]
public DiscordNotificationDetailsDto GetDetailsById(Guid id)
{
var sub = _discordNotification.GetById(id);
var webhook = _discord.GetById(sub.DiscordWebHookId);
var source = _sources.GetById(sub.SourceId);
return DiscordNotificationDetailsDto.Convert(sub, source, webhook);
}
[HttpPost("{id}/delete")]
public void DeleteById(Guid id)
{
_discordNotification.Delete(id);
}
[HttpGet("by/discordId/{id}")]
public IEnumerable<DiscordNotificationDto> GetByDiscordId(Guid id)
{
var res = new List<DiscordNotificationDto>();
var items = _discordNotification.ListByWebhook(id);
foreach (var item in items) res.Add(DiscordNotificationDto.Convert(item));
return res;
}
[HttpGet("by/sourceId/{id}")]
public IEnumerable<DiscordNotificationDto> GetBySourceId(Guid id)
{
var res = new List<DiscordNotificationDto>();
var items = _discordNotification.ListBySourceId(id);
foreach (var item in items) res.Add(DiscordNotificationDto.Convert(item));
return res;
}
[HttpPost(Name = "New Subscription")]
public ActionResult<DiscordNotificationDto> New(Guid sourceId, Guid discordId)
{
if (sourceId == Guid.Empty) return new BadRequestResult();
if (discordId == Guid.Empty) return new BadRequestResult();
var exists = _discordNotification.GetByWebhookAndSource(discordId, sourceId);
if (exists.Id != Guid.Empty) return DiscordNotificationDto.Convert(exists);
var discord = _discord.GetById(discordId);
if (discord.Id == Guid.Empty) return new BadRequestResult();
var source = _sources.GetById(sourceId);
if (source.Id == Guid.Empty) return new BadRequestResult();
var item = _discordNotification.New(new DiscordNotificationEntity
{
Id = Guid.NewGuid(),
SourceId = sourceId,
DiscordWebHookId = discordId,
CodeAllowCommits = false,
CodeAllowReleases = false
});
return DiscordNotificationDto.Convert(item);
}
[HttpPost("new/codeproject")]
public ActionResult<DiscordNotificationDto> NewCodeProjectSubscription(Guid sourceId, Guid discordId, bool allowReleases,
bool allowCommits)
{
if (sourceId == Guid.Empty) return new BadRequestResult();
if (discordId == Guid.Empty) return new BadRequestResult();
var exists = _discordNotification.GetByWebhookAndSource(discordId, sourceId);
if (exists.Id != Guid.Empty) return DiscordNotificationDto.Convert(exists);
var discord = _discord.GetById(discordId);
if (discord.Id == Guid.Empty) return new BadRequestResult();
var source = _sources.GetById(sourceId);
if (source.Id == Guid.Empty) return new BadRequestResult();
var sub = _discordNotification.New(new DiscordNotificationEntity
{
DiscordWebHookId = discordId,
SourceId = sourceId,
CodeAllowCommits = allowCommits,
CodeAllowReleases = allowReleases
});
return new DiscordNotificationDto();
}
}

View File

@ -0,0 +1,84 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newsbot.Collector.Domain.Dto;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Results;
namespace Newsbot.Collector.Api.Controllers.v1;
[ApiController]
[Route("api/v1/articles")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ArticlesController : ControllerBase
{
//private readonly ILogger<ArticlesController> _logger;
private readonly IArticlesRepository _articles;
private readonly ISourcesRepository _sources;
public ArticlesController(IArticlesRepository articles, ISourcesRepository sources)
{
_articles = articles;
_sources = sources;
}
[HttpGet(Name = "GetArticles")]
public ActionResult<ArticleResult> Get()
{
var res = new List<ArticleDto>();
var items = _articles.List(0);
foreach (var item in items)
{
res.Add(ArticleDto.Convert(item));
}
return new OkObjectResult(new ArticleResult
{
IsSuccessful = true,
Items = res
});
}
[HttpGet("{id:guid}")]
[EndpointDescription("Returns the article based on the Id value given.")]
public ActionResult<ArticleResult> GetById(Guid id)
{
var item = _articles.GetById(id);
return new OkObjectResult(new ArticleResult
{
IsSuccessful = true,
Items = new List<ArticleDto>
{
ArticleDto.Convert(item)
}
});
}
[HttpGet("{id:guid}/details")]
public ActionResult<ArticleDetailsResult> GetDetailsById(Guid id)
{
var item = _articles.GetById(id);
var sourceItem = _sources.GetById(item.SourceId);
return new OkObjectResult(new ArticleDetailsResult
{
IsSuccessful = true,
Item = ArticleDetailsDto.Convert(item, sourceItem)
});
}
[HttpGet("by/{sourceId:guid}")]
public ActionResult<ArticleResult> GetBySourceId(Guid sourceId, int page = 0)
{
var res = new List<ArticleDto>();
var items = _articles.ListBySourceId(sourceId, page);
foreach (var item in items) res.Add(ArticleDto.Convert(item));
return new OkObjectResult(new ArticleResult
{
IsSuccessful = true,
Items = res
});
}
}

View File

@ -7,10 +7,10 @@ using Newsbot.Collector.Api.Domain;
using Newsbot.Collector.Domain.Models.Config;
using Newsbot.Collector.Services.Jobs;
namespace Newsbot.Collector.Api.Controllers;
namespace Newsbot.Collector.Api.Controllers.v1;
[ApiController]
[Route("api/codeprojects")]
[Route("api/v1/codeprojects")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class CodeProjectController
{
@ -26,7 +26,7 @@ public class CodeProjectController
[HttpPost("check")]
[Authorize(Roles = Authorization.AdministratorsRole)]
public void PullNow()
public ActionResult PullNow()
{
BackgroundJob.Enqueue<CodeProjectWatcherJob>(x => x.InitAndExecute(new CodeProjectWatcherJobOptions
{
@ -34,5 +34,7 @@ public class CodeProjectController
FeaturePullReleases = true,
FeaturePullCommits = true
}));
return new AcceptedResult();
}
}

View File

@ -0,0 +1,239 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newsbot.Collector.Api.Middleware;
using Newsbot.Collector.Domain.Dto;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Requests;
using Newsbot.Collector.Domain.Results;
namespace Newsbot.Collector.Api.Controllers.v1;
[ApiController]
[Route("api/v1/subscriptions")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class DiscordNotificationController : ControllerBase
{
private readonly ILogger<DiscordNotificationController> _logger;
private readonly IDiscordWebHooksRepository _discord;
private readonly ISourcesRepository _sources;
private readonly IDiscordNotificationRepository _discordNotification;
public DiscordNotificationController(ILogger<DiscordNotificationController> logger, IDiscordWebHooksRepository discord, ISourcesRepository sources, IDiscordNotificationRepository discordNotification)
{
_logger = logger;
_discord = discord;
_sources = sources;
_discordNotification = discordNotification;
}
[HttpGet(Name = "ListSubscriptions")]
public ActionResult<DiscordNotificationResult> List(int page)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
var res = new List<DiscordNotificationDto>();
var items = _discordNotification.List(userId, page);
foreach (var item in items) res.Add(DiscordNotificationDto.Convert(item));
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = true,
Items = res
});
}
[HttpGet("{id}")]
public ActionResult<DiscordNotificationResult> GetById(Guid id)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
var res = DiscordNotificationDto.Convert(_discordNotification.GetById(userId, id));
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = true,
Items = new List<DiscordNotificationDto>
{
res
}
});
}
[HttpGet("{id}/details")]
public ActionResult<DiscordNotificationDetailsResult> GetDetailsById(Guid id)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
var sub = _discordNotification.GetById(userId, id);
var webhook = _discord.GetById(userId, sub.DiscordWebHookId);
var source = _sources.GetById(sub.SourceId);
return new OkObjectResult(new DiscordNotificationDetailsResult
{
IsSuccessful = true,
Item = DiscordNotificationDetailsDto.Convert(sub, source, webhook)
});
}
[HttpPost("{id}/delete")]
public ActionResult<DiscordNotificationResult> DeleteById(Guid id)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
var rowsUpdated = _discordNotification.Delete(userId, id);
if (rowsUpdated == -1)
{
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "Record was not own by requested user." }
});
}
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = true
});
}
[HttpGet("by/discordId/{id}")]
public ActionResult<DiscordNotificationResult> GetByDiscordId(Guid id)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
var res = new List<DiscordNotificationDto>();
var items = _discordNotification.ListByWebhook(userId, id);
foreach (var item in items) res.Add(DiscordNotificationDto.Convert(item));
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = true,
Items = res
});
}
[HttpGet("by/sourceId/{id}")]
public ActionResult<DiscordNotificationResult> GetBySourceId(Guid id)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
var res = new List<DiscordNotificationDto>();
var items = _discordNotification.ListBySourceId(userId, id);
foreach (var item in items) res.Add(DiscordNotificationDto.Convert(item));
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = true,
Items = res
});
}
[HttpPost(Name = "New Subscription")]
public ActionResult<DiscordNotificationDto> New([FromBody] NewDiscordNotificationRequest request)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "User Id is missing from the request." }
});
}
if (request.SourceId == Guid.Empty) return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "SourceId is missing from the request." }
});
if (request.DiscordId == Guid.Empty) return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "DiscordId is missing from the request." }
});
var exists = _discordNotification.GetByWebhookAndSource(userId, request.DiscordId, request.SourceId);
if (exists.Id != Guid.Empty) return DiscordNotificationDto.Convert(exists);
var discord = _discord.GetById(userId, request.DiscordId);
if (discord.Id == Guid.Empty) return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "Unable to find the requested DiscordId in the database." }
});
var source = _sources.GetById(request.SourceId);
if (source.Id == Guid.Empty) return new BadRequestObjectResult(new DiscordNotificationResult
{
IsSuccessful = false,
ErrorMessage = new List<string> { "Unable to find the requested SourceId in the database." }
});
var item = _discordNotification.New(new DiscordNotificationEntity
{
Id = Guid.NewGuid(),
SourceId = request.SourceId,
DiscordWebHookId = request.DiscordId,
CodeAllowCommits = request.AllowCommits,
CodeAllowReleases = request.AllowReleases,
UserId = userId
});
return new OkObjectResult(new DiscordNotificationResult
{
IsSuccessful = true,
Items = new List<DiscordNotificationDto> { DiscordNotificationDto.Convert(item) }
});
}
}

View File

@ -1,18 +1,19 @@
using System.Net;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newsbot.Collector.Database.Repositories;
using Newsbot.Collector.Api.Middleware;
using Newsbot.Collector.Domain.Dto;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models;
using Newsbot.Collector.Domain.Requests;
using Newsbot.Collector.Domain.Results;
namespace Newsbot.Collector.Api.Controllers;
namespace Newsbot.Collector.Api.Controllers.v1;
[ApiController]
[Route("api/discord/webhooks")]
[Route("api/v1/discord/webhooks")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class DiscordWebHookController : ControllerBase
{
@ -26,43 +27,64 @@ public class DiscordWebHookController : ControllerBase
}
[HttpGet(Name = "GetDiscordWebhooks")]
public IEnumerable<DiscordWebHookDto> Get(int page)
public ActionResult<IEnumerable<DiscordWebHookDto>> Get(int page)
{
var userId = HttpContext.GetUserId();
if (userId.Equals(string.Empty))
{
_logger.LogWarning("Unable to find the user ID in the JWD Token");
return new BadRequestResult();
}
var items = new List<DiscordWebHookDto>();
var res = _webhooks.List(page);
var res = _webhooks.ListByUserId(userId, page);
foreach (var item in res)
{
items.Add(DiscordWebHookDto.Convert(item));
}
return items;
return new OkObjectResult(items);
}
[HttpPost(Name = "New")]
public DiscordWebHookDto New(string url, string server, string channel)
public ActionResult<DiscordWebhookResult> New([FromBody] NewDiscordWebhookRequest request)
{
var exists = _webhooks.GetByUrl(url);
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
var userId = HttpContext.GetUserId();
var exists = _webhooks.GetByUrl(request.Url ?? "");
if (exists.Id != Guid.Empty)
{
return DiscordWebHookDto.Convert(exists);
return new BadRequestObjectResult(new DiscordWebhookResult
{
IsSuccessful = true,
Items = new List<DiscordWebHookDto> { DiscordWebHookDto.Convert(exists) }
});
}
var res = _webhooks.New(new DiscordWebhookEntity
{
Url = url,
Server = server,
Channel = channel,
UserId = userId,
Url = request.Url ?? "",
Server = request.Server ?? "",
Channel = request.Channel ?? "",
Enabled = true,
});
return DiscordWebHookDto.Convert(res);
return new OkObjectResult(new DiscordWebhookResult
{
IsSuccessful = true,
Items = new List<DiscordWebHookDto>
{
DiscordWebHookDto.Convert(res)
}
});
}
[HttpGet("by/serverAndChannel")]
public IEnumerable<DiscordWebHookDto> GetByServerAndChannel(string server, string channel)
{
var items = new List<DiscordWebHookDto>();
var res = _webhooks.ListByServerAndChannel(server, channel, 25);
var res = _webhooks.ListByServerAndChannel(HttpContext.GetUserId(), server, channel, 25);
foreach (var item in res)
{
@ -72,21 +94,21 @@ public class DiscordWebHookController : ControllerBase
}
[HttpGet("{id}")]
public DiscordWebHookDto GetById(Guid id)
public DiscordWebHookDto GetById(string userId, Guid id)
{
var res = _webhooks.GetById(id);
var res = _webhooks.GetById(userId, id);
return DiscordWebHookDto.Convert(res);
}
[HttpPost("{id}/disable")]
public void DisableById(Guid id)
{
_webhooks.Disable(id);
_webhooks.Disable(HttpContext.GetUserId(), id);
}
[HttpPost("{id}/enable")]
public void EnableById(Guid id)
{
_webhooks.Enable(id);
_webhooks.Enable(HttpContext.GetUserId(), id);
}
}

View File

@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newsbot.Collector.Api.Domain;
using Newsbot.Collector.Api.Domain.Requests;
using Newsbot.Collector.Api.Domain.Response;
using Newsbot.Collector.Api.Domain.Results;
using Newsbot.Collector.Api.Services;
using Newsbot.Collector.Domain.Requests;
using Newsbot.Collector.Domain.Response;
using Newsbot.Collector.Domain.Results;
using Newsbot.Collector.Services;
namespace Newsbot.Collector.Api.Controllers.v1;
@ -12,11 +12,13 @@ namespace Newsbot.Collector.Api.Controllers.v1;
[Route("/api/v1/account")]
public class IdentityController : ControllerBase
{
private IIdentityService _identityService;
private readonly ILogger<IdentityController> _logger;
private readonly IIdentityService _identityService;
public IdentityController(IIdentityService identityService)
public IdentityController(IIdentityService identityService, ILogger<IdentityController> logger)
{
_identityService = identityService;
_logger = logger;
}
[HttpPost("register")]
@ -81,6 +83,7 @@ public class IdentityController : ControllerBase
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to add role to user");
return new BadRequestResult();
}
}

View File

@ -8,10 +8,10 @@ using Newsbot.Collector.Domain.Models.Config;
using Newsbot.Collector.Domain.Models.Config.Sources;
using Newsbot.Collector.Services.Jobs;
namespace Newsbot.Collector.Api.Controllers;
namespace Newsbot.Collector.Api.Controllers.v1;
[ApiController]
[Route("api/rss")]
[Route("api/v1/rss")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class RssController
{

View File

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newsbot.Collector.Api.Authentication;
using Newsbot.Collector.Api.Middleware;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;

View File

@ -9,10 +9,10 @@ using Newsbot.Collector.Domain.Models.Config.Sources;
using Newsbot.Collector.Services.Jobs;
using ILogger = Grpc.Core.Logging.ILogger;
namespace Newsbot.Collector.Api.Controllers;
namespace Newsbot.Collector.Api.Controllers.v1;
[ApiController]
[Route("api/youtube")]
[Route("api/v1/youtube")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class YoutubeController
{

View File

@ -1,12 +0,0 @@
using System.Collections;
namespace Newsbot.Collector.Api.Domain.Results;
public class AuthenticationResult
{
public string? Token { get; set; }
public string? RefreshToken { get; set; }
public bool IsSuccessful { get; set; }
public IEnumerable<string>? ErrorMessage { get; set; }
}

View File

@ -1,14 +1,9 @@
namespace Newsbot.Collector.Api.Authentication;
namespace Newsbot.Collector.Api.Middleware;
public static class JwtUserIdExtension
{
public static string GetUserId(this HttpContext context)
{
if (context.User == null)
{
return string.Empty;
}
return context.User.Claims.Single(x => x.Type == "id").Value;
}
}

View File

@ -4,6 +4,8 @@
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>

View File

@ -35,7 +35,7 @@ GlobalConfiguration.Configuration.UseSerilogLogProvider();
// Build Health Checks
builder.Services.AddHealthChecks()
.AddNpgSql(config.GetValue<string>(ConfigConnectionStringConst.Database) ?? "");
.AddNpgSql(config.GetValue<string>(ConfigConst.ConnectionStringDatabase) ?? "");
builder.Services.AddControllers();
@ -109,7 +109,7 @@ static IConfiguration GetConfiguration()
static ILogger GetLogger(IConfiguration configuration)
{
var otel = configuration.GetValue<string>(ConfigConnectionStringConst.OpenTelemetry) ?? "";
var otel = configuration.GetValue<string>(ConfigConst.ConnectionStringOpenTelemetry) ?? "";
if (otel == "")
return Log.Logger = new LoggerConfiguration()

View File

@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Newsbot.Collector.Api.Domain;
using Newsbot.Collector.Api.Services;
using Newsbot.Collector.Database;
using Newsbot.Collector.Database.Repositories;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Services;
namespace Newsbot.Collector.Api.Startup;

View File

@ -17,8 +17,8 @@ public class DatabaseContext : IdentityDbContext
public DbSet<SourceEntity> Sources { get; set; } = null!;
public DbSet<UserSourceSubscriptionEntity> UserSourceSubscription { get; set; } = null!;
public DbSet<RefreshTokenEntity> RefreshTokens { get; set; }
public DbSet<RefreshTokenEntity> RefreshTokens { get; set; } = null!;
private string ConnectionString { get; set; } = "";

View File

@ -21,6 +21,9 @@
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>8981</NoWarn>
</PropertyGroup>
</Project>

View File

@ -1,10 +1,5 @@
using System.Data;
using Dapper;
using Microsoft.Extensions.Configuration;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models;
using Npgsql;
namespace Newsbot.Collector.Database.Repositories;
@ -27,66 +22,76 @@ public class DiscordNotificationTable : IDiscordNotificationRepository
public DiscordNotificationEntity New(DiscordNotificationEntity model)
{
model.Id = new Guid();
//using var context = new DatabaseContext(_connectionString);
_context.DiscordNotification.Add(model);
_context.SaveChanges();
return model;
}
public List<DiscordNotificationEntity> List(int page = 0, int count = 25)
public List<DiscordNotificationEntity> List(string userId, int page = 0, int count = 25)
{
//using var context = new DatabaseContext(_connectionString);
return _context.DiscordNotification.Skip(page * count).Take(count).ToList();
return _context.DiscordNotification
.Where(x => x.UserId != null && x.UserId.Equals(userId))
.Skip(page * count)
.Take(count)
.ToList();
}
public List<DiscordNotificationEntity> ListBySourceId(string userId, Guid id, int page = 0, int count = 25)
{
return _context.DiscordNotification
.Where(f => f.SourceId.Equals(id))
.Where(f => f.UserId != null && f.UserId.Equals(userId))
.Skip(page * count)
.ToList();
}
public List<DiscordNotificationEntity> ListBySourceId(Guid id, int page = 0, int count = 25)
{
//using var context = new DatabaseContext(_connectionString);
return _context.DiscordNotification.Where(f => f.SourceId.Equals(id))
return _context.DiscordNotification
.Where(f => f.SourceId.Equals(id))
.Skip(page * count)
.ToList();
}
public List<DiscordNotificationEntity> ListByWebhook(Guid id, int page = 0, int count = 25)
public List<DiscordNotificationEntity> ListByWebhook(string userId, Guid id, int page = 0, int count = 25)
{
//using var context = new DatabaseContext(_connectionString);
return _context.DiscordNotification.Where(f => f.DiscordWebHookId.Equals(id)).Skip(page * count).ToList();
return _context.DiscordNotification
.Where(f => f.DiscordWebHookId.Equals(id))
.Where(f => f.UserId != null && f.UserId.Equals(userId))
.Skip(page * count)
.ToList();
}
public DiscordNotificationEntity GetById(Guid id)
public DiscordNotificationEntity GetById(string userId, Guid id)
{
//using var context = new DatabaseContext(_connectionString);
var res = _context.DiscordNotification
.Where(f => f.UserId != null && f.UserId.Equals(userId))
.FirstOrDefault(f => f.Id.Equals(id));
return res ??= new DiscordNotificationEntity();
}
public DiscordNotificationEntity GetByWebhookAndSource(Guid webhookId, Guid sourceId)
public DiscordNotificationEntity GetByWebhookAndSource(string userId, Guid webhookId, Guid sourceId)
{
//using var context = new DatabaseContext(_connectionString);
var res = _context.DiscordNotification
.Where(f => f.UserId != null && f.UserId.Equals(userId))
.Where(f => f.DiscordWebHookId.Equals(webhookId))
.FirstOrDefault(f => f.SourceId.Equals(sourceId));
return res ??= new DiscordNotificationEntity();
}
public void Delete(Guid id)
public int Delete(string userId, Guid id)
{
//using var context = new DatabaseContext(_connectionString);
var res = _context.DiscordNotification.FirstOrDefault(f => f.Id.Equals(id));
var res = _context.DiscordNotification
.Where(f => f.UserId != null && f.UserId.Equals(userId))
.FirstOrDefault(f => f.Id.Equals(id));
if (res is null)
{
return;
return -1;
}
_context.DiscordNotification.Remove(res);
_context.SaveChanges();
return _context.SaveChanges();
}
//private IDbConnection OpenConnection(string connectionString)
//{
// var conn = new NpgsqlConnection(_connectionString);
// conn.Open();
// return conn;
//}
}

View File

@ -1,5 +1,6 @@
using System.Data;
using Dapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Entities;
@ -33,10 +34,20 @@ public class DiscordWebhooksTable : IDiscordWebHooksRepository
return model;
}
public DiscordWebhookEntity GetById(Guid id)
public DiscordWebhookEntity GetById(string userId, Guid id)
{
//using var context = new DatabaseContext(_connectionString);
var res = _context.DiscordWebhooks.FirstOrDefault(d => d.Id.Equals(id));
var res = _context.DiscordWebhooks
.Where(i => i.UserId != null && i.UserId.Equals(userId))
.FirstOrDefault(d => d.Id.Equals(id));
res ??= new DiscordWebhookEntity();
return res;
}
public DiscordWebhookEntity GetById(Guid id)
{
var res = _context.DiscordWebhooks
.FirstOrDefault(d => d.Id.Equals(id));
res ??= new DiscordWebhookEntity();
return res;
}
@ -60,6 +71,16 @@ public class DiscordWebhooksTable : IDiscordWebHooksRepository
return res;
}
public List<DiscordWebhookEntity> ListByUserId(string userId, int page)
{
var query = _context.DiscordWebhooks
.Where(p => p.UserId != null && p.UserId.Equals(userId))
.Skip(page * 25)
.Take(25)
.ToList();
return query;
}
public List<DiscordWebhookEntity> ListByServer(string server, int limit = 25)
{
//using var context = new DatabaseContext(_connectionString);
@ -71,10 +92,11 @@ public class DiscordWebhooksTable : IDiscordWebHooksRepository
return res;
}
public List<DiscordWebhookEntity> ListByServerAndChannel(string server, string channel, int limit = 25)
public List<DiscordWebhookEntity> ListByServerAndChannel(string userId, string server, string channel, int limit = 25)
{
//using var context = new DatabaseContext(_connectionString);
var res = _context.DiscordWebhooks
.Where(i => i.UserId != null && i.UserId.Equals(userId))
.Where(s => s.Server.Equals(server))
.Where(c => c.Channel.Equals(channel))
.Take(limit)
@ -83,9 +105,9 @@ public class DiscordWebhooksTable : IDiscordWebHooksRepository
return res;
}
public int Disable(Guid id)
public int Disable(string userId, Guid id)
{
var res = GetById(id);
var res = GetById(userId, id);
//using var context = new DatabaseContext(_connectionString);
res.Enabled = true;
@ -103,9 +125,9 @@ public class DiscordWebhooksTable : IDiscordWebHooksRepository
}
}
public int Enable(Guid id)
public int Enable(string userId, Guid id)
{
var res = GetById(id);
var res = GetById(userId, id);
//using var context = new DatabaseContext(_connectionString);
res.Enabled = false;

View File

@ -19,7 +19,8 @@ public class UserSourceSubscriptionTable : IUserSourceSubscription
public List<UserSourceSubscriptionEntity> ListUserSubscriptions(Guid userId)
{
var results =_context.UserSourceSubscription.Where(i => i.UserId.Equals(userId)).ToList();
var results =_context.UserSourceSubscription
.Where(i => i.UserId != null && i.UserId.Equals(userId)).ToList();
return results;
}
}

View File

@ -1,10 +0,0 @@
namespace Newsbot.Collector.Domain.Consts;
/// <summary>
/// This class contains const entries to access keys within IConfiguration.
/// </summary>
public static class ConfigConnectionStringConst
{
public const string Database = "ConnectionStrings:Database";
public const string OpenTelemetry = "ConnectionStrings:OpenTelemetry";
}

View File

@ -1,11 +0,0 @@
namespace Newsbot.Collector.Domain.Consts;
/// <summary>
/// This class contains const entries to access keys within IConfiguration.
/// </summary>
public class ConfigTwitchConst
{
public const string IsEnabled = "Twitch:IsEnabled";
public const string ClientID = "Twitch:ClientID";
public const string ClientSecret = "Twitch:ClientSecret";
}

View File

@ -1,10 +0,0 @@
namespace Newsbot.Collector.Domain.Consts;
/// <summary>
/// This class contains const entries to access keys within IConfiguration.
/// </summary>
public class ConfigYoutubeConst
{
public const string IsEnable = "Youtube:IsEnabled";
public const string Debug = "Youtube:Debug";
}

View File

@ -1,3 +1,6 @@
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity;
namespace Newsbot.Collector.Domain.Entities;
public class DiscordWebhookEntity
@ -7,4 +10,8 @@ public class DiscordWebhookEntity
public string Server { get; set; } = "";
public string Channel { get; set; } = "";
public bool Enabled { get; set; }
public string? UserId { get; set; }
[ForeignKey(nameof(UserId))]
public IdentityUser? User { get; set; }
}

View File

@ -5,7 +5,7 @@ namespace Newsbot.Collector.Domain.Interfaces;
public interface IArticlesRepository : ITableRepository
{
List<ArticlesEntity> List(int page, int count);
List<ArticlesEntity> List(int page, int count = 25);
List<ArticlesEntity> ListBySourceId(Guid id, int page = 0, int count = 25);
ArticlesEntity GetById(Guid id);
ArticlesEntity GetByUrl(string url);

View File

@ -7,12 +7,22 @@ public interface IDiscordNotificationRepository
{
DiscordNotificationEntity New(DiscordNotificationEntity model);
List<DiscordNotificationEntity> List(int page = 0, int count = 25);
List<DiscordNotificationEntity> List(string userId, int page = 0, int count = 25);
List<DiscordNotificationEntity> ListBySourceId(string userId, Guid id, int page = 0, int count = 25);
/// <summary>
/// This will collect all the records based on the SourceId.
/// Background jobs can use this but user facing calls need to define UserId
/// </summary>
/// <param name="id"></param>
/// <param name="page"></param>
/// <param name="count"></param>
/// <returns></returns>
List<DiscordNotificationEntity> ListBySourceId(Guid id, int page = 0, int count = 25);
List<DiscordNotificationEntity> ListByWebhook(Guid id, int page = 0, int count = 25);
List<DiscordNotificationEntity> ListByWebhook(string userId, Guid id, int page = 0, int count = 25);
DiscordNotificationEntity GetById(Guid id);
DiscordNotificationEntity GetByWebhookAndSource(Guid webhookId, Guid sourceId);
DiscordNotificationEntity GetById(string userId, Guid id);
DiscordNotificationEntity GetByWebhookAndSource(string userId, Guid webhookId, Guid sourceId);
void Delete(Guid id);
int Delete(string userId, Guid id);
}

View File

@ -6,13 +6,16 @@ public interface IDiscordWebHooksRepository
{
DiscordWebhookEntity New(DiscordWebhookEntity model);
DiscordWebhookEntity GetById(string userId, Guid id);
DiscordWebhookEntity GetById(Guid id);
DiscordWebhookEntity GetByUrl(string url);
List<DiscordWebhookEntity> List(int page, int count = 25);
List<DiscordWebhookEntity> ListByUserId(string userId, int page);
List<DiscordWebhookEntity> ListByServer(string server, int limit);
List<DiscordWebhookEntity> ListByServerAndChannel(string server, string channel, int limit);
List<DiscordWebhookEntity> ListByServerAndChannel(string userId, string server, string channel, int limit);
int Disable(Guid id);
int Enable(Guid id);
int Disable(string userId, Guid id);
int Enable(string userId, Guid id);
}

View File

@ -1,13 +1,24 @@
using Newsbot.Collector.Domain.Models.Config;
namespace Newsbot.Collector.Domain.Models;
public class ConfigModel
{
public string? ServerAddress { get; set; }
public string? SqlConnectionString { get; set; }
public RedditConfigModel? Reddit { get; set; }
public ConnectionStrings? ConnectionStrings { get; set; }
public RedditConfig? Reddit { get; set; }
public YoutubeConfig? Youtube { get; set; }
public TwitchConfig? Twitch { get; set; }
public BasicSourceConfig? FinalFantasyXiv { get; set; }
public BasicSourceConfig? Rss { get; set; }
public BasicSourceConfig? CodeProjects { get; set; }
public NotificationsConfig? Notifications { get; set; }
public bool EnableSwagger { get; set; }
public bool RunDatabaseMigrationsOnStartup { get; set; }
public List<string>? ApiKeys { get; set; }
public JwtSettings? JwtSettings { get; set; }
}
public class RedditConfigModel
public class RedditConfig
{
public bool IsEnabled { get; set; }
public bool PullHot { get; set; }
@ -17,5 +28,29 @@ public class RedditConfigModel
public class ConnectionStrings
{
public string Database { get; set; } = "";
}
public string? Database { get; set; }
public string? OpenTelemetry { get; set; }
}
public class BasicSourceConfig
{
public bool IsEnabled { get; set; }
}
public class YoutubeConfig
{
public bool IsEnabled { get; set; }
public bool Debug { get; set; }
}
public class TwitchConfig
{
public bool IsEnabled { get; set; }
public string? ClientId { get; set; }
public string? ClientSecret { get; set; }
}
public class NotificationsConfig
{
public BasicSourceConfig? Discord { get; set; }
}

View File

@ -1,9 +0,0 @@
namespace Newsbot.Collector.Domain.Models.Config.Sources;
public class ConfigSectionRedditModel
{
public bool IsEnabled { get; set; }
public bool PullHot { get; set; }
public bool PullNsfw { get; set; }
public bool PullTop { get; set; }
}

View File

@ -4,6 +4,8 @@
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>

View File

@ -0,0 +1,10 @@
namespace Newsbot.Collector.Domain.Requests;
public class NewDiscordNotificationRequest
{
public Guid SourceId { get; set; }
public Guid DiscordId { get; set; }
public bool AllowReleases { get; set; } = false;
public bool AllowCommits { get; set; } = false;
}

View File

@ -0,0 +1,8 @@
namespace Newsbot.Collector.Domain.Requests;
public class NewDiscordWebhookRequest
{
public string? Url { get; set; }
public string? Server { get; set; }
public string? Channel { get; set; }
}

View File

@ -1,4 +1,4 @@
namespace Newsbot.Collector.Api.Domain.Requests;
namespace Newsbot.Collector.Domain.Requests;
public class AddRoleRequest
{

View File

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Newsbot.Collector.Api.Domain.Requests;
namespace Newsbot.Collector.Domain.Requests;
public class RegisterUserRequest
{

View File

@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
namespace Newsbot.Collector.Api.Domain.Requests;
namespace Newsbot.Collector.Domain.Requests;
public class UserLoginRequest
{

View File

@ -1,4 +1,4 @@
namespace Newsbot.Collector.Api.Domain.Requests;
namespace Newsbot.Collector.Domain.Requests;
public class UserRefreshTokenRequest
{

View File

@ -1,4 +1,4 @@
namespace Newsbot.Collector.Api.Domain.Response;
namespace Newsbot.Collector.Domain.Response;
public class AuthFailedResponse
{

View File

@ -1,4 +1,4 @@
namespace Newsbot.Collector.Api.Domain.Response;
namespace Newsbot.Collector.Domain.Response;
public class AuthSuccessfulResponse
{

View File

@ -0,0 +1,8 @@
using Newsbot.Collector.Domain.Dto;
namespace Newsbot.Collector.Domain.Results;
public class ArticleDetailsResult : BaseResult
{
public ArticleDetailsDto? Item { get; set; }
}

View File

@ -0,0 +1,8 @@
using Newsbot.Collector.Domain.Dto;
namespace Newsbot.Collector.Domain.Results;
public class ArticleResult : BaseResult
{
public IEnumerable<ArticleDto>? Items { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Newsbot.Collector.Domain.Results;
public class AuthenticationResult : BaseResult
{
public string? Token { get; set; }
public string? RefreshToken { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Newsbot.Collector.Domain.Results;
public class BaseResult
{
public bool IsSuccessful { get; set; }
public IEnumerable<string>? ErrorMessage { get; set; }
}

View File

@ -0,0 +1,8 @@
using Newsbot.Collector.Domain.Dto;
namespace Newsbot.Collector.Domain.Results;
public class DiscordNotificationDetailsResult : BaseResult
{
public DiscordNotificationDetailsDto? Item { get; set; }
}

View File

@ -0,0 +1,8 @@
using Newsbot.Collector.Domain.Dto;
namespace Newsbot.Collector.Domain.Results;
public class DiscordNotificationResult : BaseResult
{
public IEnumerable<DiscordNotificationDto>? Items { get; set; }
}

View File

@ -0,0 +1,8 @@
using Newsbot.Collector.Domain.Dto;
namespace Newsbot.Collector.Domain.Results;
public class DiscordWebhookResult : BaseResult
{
public List<DiscordWebHookDto>? Items { get; set; }
}

View File

@ -3,13 +3,13 @@ using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using Newsbot.Collector.Api.Domain.Results;
using Newsbot.Collector.Database;
using Newsbot.Collector.Domain.Results;
using Newsbot.Collector.Domain.Entities;
using Newsbot.Collector.Domain.Interfaces;
using Newsbot.Collector.Domain.Models.Config;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
namespace Newsbot.Collector.Api.Services;
namespace Newsbot.Collector.Services;
public interface IIdentityService
{
@ -60,7 +60,7 @@ public class IdentityService : IIdentityService
{
return new AuthenticationResult
{
ErrorMessage = new[] { createdUser.Result.Errors.Select(x => x.Description).ToString() }
ErrorMessage = new List<string>(createdUser.Result.Errors.Select(x => x.Description))
};
}

View File

@ -11,11 +11,14 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="6.15.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.15.1" />
<PackageReference Include="Selenium.WebDriver" Version="4.8.1" />
<PackageReference Include="Selenium.WebDriver.GeckoDriver" Version="0.32.2" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.OpenTelemetry" Version="1.0.0-dev-00113" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.1" />
<PackageReference Include="System.ServiceModel.Syndication" Version="7.0.0" />
</ItemGroup>
@ -23,6 +26,8 @@
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>