Merge pull request #3 from NicolasConstant/topic_debug-signature-call
Topic debug signature call
This commit is contained in:
commit
a16a200cd6
30 changed files with 611 additions and 47 deletions
2
.github/workflows/dotnet-core.yml
vendored
2
.github/workflows/dotnet-core.yml
vendored
|
@ -15,6 +15,8 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- name: Launch Db for testing
|
||||||
|
run: docker run --name postgres -e POSTGRES_DB=mytestdb -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432:5432 postgres
|
||||||
- name: Setup .NET Core
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub.Converters
|
||||||
|
{
|
||||||
|
public class ContextArrayConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanWrite { get { return false; } }
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
var list = serializer.Deserialize<List<object>>(reader);
|
||||||
|
foreach (var l in list)
|
||||||
|
{
|
||||||
|
if (l is string s)
|
||||||
|
result.Add(s);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var str = JsonConvert.SerializeObject(l);
|
||||||
|
result.Add(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
using System;
|
using BirdsiteLive.ActivityPub.Converters;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using JsonConverter = Newtonsoft.Json.JsonConverter;
|
|
||||||
|
|
||||||
namespace BirdsiteLive.ActivityPub
|
namespace BirdsiteLive.ActivityPub
|
||||||
{
|
{
|
||||||
|
@ -22,37 +20,4 @@ namespace BirdsiteLive.ActivityPub
|
||||||
public Image icon { get; set; }
|
public Image icon { get; set; }
|
||||||
public Image image { get; set; }
|
public Image image { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContextArrayConverter : JsonConverter
|
|
||||||
{
|
|
||||||
public override bool CanWrite { get { return false; } }
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var result = new List<string>();
|
|
||||||
|
|
||||||
var list = serializer.Deserialize<List<object>>(reader);
|
|
||||||
foreach (var l in list)
|
|
||||||
{
|
|
||||||
if (l is string s)
|
|
||||||
result.Add(s);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var str = JsonConvert.SerializeObject(l);
|
|
||||||
result.Add(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using BirdsiteLive.ActivityPub.Converters;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BirdsiteLive.ActivityPub
|
namespace BirdsiteLive.ActivityPub
|
||||||
{
|
{
|
||||||
public class Note
|
public class Note
|
||||||
{
|
{
|
||||||
|
[JsonProperty("@context")]
|
||||||
|
[JsonConverter(typeof(ContextArrayConverter))]
|
||||||
|
public string[] context { get; set; } = new[] { "https://www.w3.org/ns/activitystreams" };
|
||||||
|
|
||||||
public string id { get; set; }
|
public string id { get; set; }
|
||||||
public string type { get; } = "Note";
|
public string type { get; } = "Note";
|
||||||
public string summary { get; set; }
|
public string summary { get; set; }
|
||||||
|
|
|
@ -10,6 +10,7 @@ using BirdsiteLive.Common.Settings;
|
||||||
using BirdsiteLive.Cryptography;
|
using BirdsiteLive.Cryptography;
|
||||||
using BirdsiteLive.Twitter.Models;
|
using BirdsiteLive.Twitter.Models;
|
||||||
using Tweetinvi.Core.Exceptions;
|
using Tweetinvi.Core.Exceptions;
|
||||||
|
using Tweetinvi.Models;
|
||||||
|
|
||||||
namespace BirdsiteLive.Domain
|
namespace BirdsiteLive.Domain
|
||||||
{
|
{
|
||||||
|
@ -17,6 +18,7 @@ namespace BirdsiteLive.Domain
|
||||||
{
|
{
|
||||||
Actor GetUser(TwitterUser twitterUser);
|
Actor GetUser(TwitterUser twitterUser);
|
||||||
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
|
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
|
||||||
|
Note GetStatus(TwitterUser user, ITweet tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserService : IUserService
|
public class UserService : IUserService
|
||||||
|
@ -65,6 +67,39 @@ namespace BirdsiteLive.Domain
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Note GetStatus(TwitterUser user, ITweet tweet)
|
||||||
|
{
|
||||||
|
var actor = GetUser(user);
|
||||||
|
|
||||||
|
var actorUrl = $"{_host}/users/{user.Acct}";
|
||||||
|
var noteId = $"{_host}/users/{user.Acct}/statuses/{tweet.Id}";
|
||||||
|
var noteUrl = $"{_host}/@{user.Acct}/{tweet.Id}";
|
||||||
|
|
||||||
|
var to = $"{actor}/followers";
|
||||||
|
var apPublic = "https://www.w3.org/ns/activitystreams#Public";
|
||||||
|
|
||||||
|
var note = new Note
|
||||||
|
{
|
||||||
|
id = $"{noteId}/activity",
|
||||||
|
|
||||||
|
published = tweet.CreatedAt.ToString("s") + "Z",
|
||||||
|
url = noteUrl,
|
||||||
|
attributedTo = actorUrl,
|
||||||
|
|
||||||
|
//to = new [] {to},
|
||||||
|
//cc = new [] { apPublic },
|
||||||
|
|
||||||
|
to = new[] { apPublic },
|
||||||
|
cc = new[] { to },
|
||||||
|
|
||||||
|
sensitive = false,
|
||||||
|
content = $"<p>{tweet.Text}</p>",
|
||||||
|
attachment = new string[0],
|
||||||
|
tag = new string[0]
|
||||||
|
};
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
|
@ -91,7 +126,7 @@ namespace BirdsiteLive.Domain
|
||||||
var result = await _activityPubService.PostDataAsync(acceptFollow, targetHost, activity.apObject);
|
var result = await _activityPubService.PostDataAsync(acceptFollow, targetHost, activity.apObject);
|
||||||
return result == HttpStatusCode.Accepted;
|
return result == HttpStatusCode.Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
private async Task<bool> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
||||||
{
|
{
|
||||||
var signatures = rawSig.Split(',');
|
var signatures = rawSig.Split(',');
|
||||||
|
|
|
@ -3,12 +3,14 @@ using System.Threading.Tasks;
|
||||||
using BirdsiteLive.Common.Settings;
|
using BirdsiteLive.Common.Settings;
|
||||||
using BirdsiteLive.Twitter.Models;
|
using BirdsiteLive.Twitter.Models;
|
||||||
using Tweetinvi;
|
using Tweetinvi;
|
||||||
|
using Tweetinvi.Models;
|
||||||
|
|
||||||
namespace BirdsiteLive.Twitter
|
namespace BirdsiteLive.Twitter
|
||||||
{
|
{
|
||||||
public interface ITwitterService
|
public interface ITwitterService
|
||||||
{
|
{
|
||||||
TwitterUser GetUser(string username);
|
TwitterUser GetUser(string username);
|
||||||
|
ITweet GetTweet(long statusId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TwitterService : ITwitterService
|
public class TwitterService : ITwitterService
|
||||||
|
@ -40,5 +42,12 @@ namespace BirdsiteLive.Twitter
|
||||||
ProfileBannerURL = user.ProfileBannerURL
|
ProfileBannerURL = user.ProfileBannerURL
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ITweet GetTweet(long statusId)
|
||||||
|
{
|
||||||
|
Auth.SetApplicationOnlyCredentials(_settings.ConsumerKey, _settings.ConsumerSecret, true);
|
||||||
|
var tweet = Tweet.GetTweet(statusId);
|
||||||
|
return tweet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{A32D3458
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Cryptography.Tests", "Tests\BirdsiteLive.Cryptography.Tests\BirdsiteLive.Cryptography.Tests.csproj", "{155D46A4-2D05-47F2-8FFC-0B7C412A7652}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Cryptography.Tests", "Tests\BirdsiteLive.Cryptography.Tests\BirdsiteLive.Cryptography.Tests.csproj", "{155D46A4-2D05-47F2-8FFC-0B7C412A7652}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.Domain", "BirdsiteLive.Domain\BirdsiteLive.Domain.csproj", "{D48450EE-D8BD-4228-9864-043AC88F7EE0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.Domain", "BirdsiteLive.Domain\BirdsiteLive.Domain.csproj", "{D48450EE-D8BD-4228-9864-043AC88F7EE0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.ActivityPub", "BirdsiteLive.ActivityPub\BirdsiteLive.ActivityPub.csproj", "{7463E1E2-9736-4A46-8507-010BDD8ECFBB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.ActivityPub.Tests", "Tests\BirdsiteLive.ActivityPub.Tests\BirdsiteLive.ActivityPub.Tests.csproj", "{1D713961-9926-41FF-8D6A-8A4B8D548484}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BirdsiteLive.ActivityPub.Tests", "Tests\BirdsiteLive.ActivityPub.Tests\BirdsiteLive.ActivityPub.Tests.csproj", "{1D713961-9926-41FF-8D6A-8A4B8D548484}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataAccessLayers", "DataAccessLayers", "{CFAB3509-3931-42DB-AC97-4F91FC2D849C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.DAL", "DataAccessLayers\BirdsiteLive.DAL\BirdsiteLive.DAL.csproj", "{47058CAB-DC43-4DD1-8F68-D3D625332905}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.DAL.Postgres", "DataAccessLayers\BirdsiteLive.DAL.Postgres\BirdsiteLive.DAL.Postgres.csproj", "{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BirdsiteLive.DAL.Postgres.Tests", "Tests\BirdsiteLive.DAL.Postgres.Tests\BirdsiteLive.DAL.Postgres.Tests.csproj", "{CD9489BF-69C8-4705-8774-81C45F4F8FE1}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -61,6 +69,18 @@ Global
|
||||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1D713961-9926-41FF-8D6A-8A4B8D548484}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{47058CAB-DC43-4DD1-8F68-D3D625332905}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{CD9489BF-69C8-4705-8774-81C45F4F8FE1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -73,6 +93,9 @@ Global
|
||||||
{D48450EE-D8BD-4228-9864-043AC88F7EE0} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
{D48450EE-D8BD-4228-9864-043AC88F7EE0} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
||||||
{7463E1E2-9736-4A46-8507-010BDD8ECFBB} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
{7463E1E2-9736-4A46-8507-010BDD8ECFBB} = {4FEAD6BC-3C8E-451A-8CA1-FF1AF47D26CC}
|
||||||
{1D713961-9926-41FF-8D6A-8A4B8D548484} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
{1D713961-9926-41FF-8D6A-8A4B8D548484} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
|
{47058CAB-DC43-4DD1-8F68-D3D625332905} = {CFAB3509-3931-42DB-AC97-4F91FC2D849C}
|
||||||
|
{87E46519-BBF2-437C-8A5B-CF6CDE7CDAA6} = {CFAB3509-3931-42DB-AC97-4F91FC2D849C}
|
||||||
|
{CD9489BF-69C8-4705-8774-81C45F4F8FE1} = {A32D3458-09D0-4E0A-BA4B-8C411B816B94}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
SolutionGuid = {69E8DCAD-4C37-4010-858F-5F94E6FBABCE}
|
||||||
|
|
|
@ -13,14 +13,14 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BirdsiteLive.Controllers
|
namespace BirdsiteLive.Controllers
|
||||||
{
|
{
|
||||||
public class DebugController : Controller
|
public class DebugingController : Controller
|
||||||
{
|
{
|
||||||
private readonly InstanceSettings _instanceSettings;
|
private readonly InstanceSettings _instanceSettings;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IActivityPubService _activityPubService;
|
private readonly IActivityPubService _activityPubService;
|
||||||
|
|
||||||
#region Ctor
|
#region Ctor
|
||||||
public DebugController(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService)
|
public DebugingController(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService)
|
||||||
{
|
{
|
||||||
_instanceSettings = instanceSettings;
|
_instanceSettings = instanceSettings;
|
||||||
_cryptoService = cryptoService;
|
_cryptoService = cryptoService;
|
|
@ -33,18 +33,43 @@ namespace BirdsiteLive.Controllers
|
||||||
{
|
{
|
||||||
var user = _twitterService.GetUser(id);
|
var user = _twitterService.GetUser(id);
|
||||||
if (user == null) return NotFound();
|
if (user == null) return NotFound();
|
||||||
|
|
||||||
var r = Request.Headers["Accept"].First();
|
var r = Request.Headers["Accept"].First();
|
||||||
if (r.Contains("application/activity+json"))
|
if (r.Contains("application/activity+json"))
|
||||||
{
|
{
|
||||||
var apUser = _userService.GetUser(user);
|
var apUser = _userService.GetUser(user);
|
||||||
var jsonApUser = JsonConvert.SerializeObject(apUser);
|
var jsonApUser = JsonConvert.SerializeObject(apUser);
|
||||||
return Content(jsonApUser, "application/json");
|
return Content(jsonApUser, "application/activity+json; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
return View(user);
|
return View(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/@{id}/{statusId}")]
|
||||||
|
[Route("/users/{id}/statuses/{statusId}")]
|
||||||
|
public IActionResult Tweet(string id, string statusId)
|
||||||
|
{
|
||||||
|
var r = Request.Headers["Accept"].First();
|
||||||
|
if (r.Contains("application/activity+json"))
|
||||||
|
{
|
||||||
|
if (!long.TryParse(statusId, out var parsedStatusId))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var tweet = _twitterService.GetTweet(parsedStatusId);
|
||||||
|
if(tweet == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var user = _twitterService.GetUser(id);
|
||||||
|
if (user == null) return NotFound();
|
||||||
|
|
||||||
|
var status = _userService.GetStatus(user, tweet);
|
||||||
|
var jsonApUser = JsonConvert.SerializeObject(status);
|
||||||
|
return Content(jsonApUser, "application/activity+json; charset=utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("Tweet", statusId);
|
||||||
|
}
|
||||||
|
|
||||||
[Route("/users/{id}/inbox")]
|
[Route("/users/{id}/inbox")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Inbox()
|
public async Task<IActionResult> Inbox()
|
||||||
|
@ -58,7 +83,7 @@ namespace BirdsiteLive.Controllers
|
||||||
|
|
||||||
switch (activity.type)
|
switch (activity.type)
|
||||||
{
|
{
|
||||||
case "Follow":
|
case "Follow":
|
||||||
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
||||||
if (succeeded) return Accepted();
|
if (succeeded) return Accepted();
|
||||||
else return Unauthorized();
|
else return Unauthorized();
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
"applicationUrl": "http://localhost:5000"
|
||||||
},
|
},
|
||||||
"Docker": {
|
"Docker": {
|
||||||
"commandName": "Docker",
|
"commandName": "Docker",
|
||||||
|
|
19
src/BirdsiteLive/Views/Debuging/Index.cshtml
Normal file
19
src/BirdsiteLive/Views/Debuging/Index.cshtml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Index";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1>Debug</h1>
|
||||||
|
|
||||||
|
<form asp-controller="Debug" asp-action="Follow" method="post">
|
||||||
|
<!-- Input and Submit elements -->
|
||||||
|
|
||||||
|
<button type="submit" value="Submit">Follow</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<form asp-controller="Debug" asp-action="PostNote" method="post">
|
||||||
|
<!-- Input and Submit elements -->
|
||||||
|
|
||||||
|
<button type="submit" value="Submit">Post Note</button>
|
||||||
|
</form>
|
|
@ -10,6 +10,6 @@
|
||||||
|
|
||||||
@if (HtmlHelperExtensions.IsDebug())
|
@if (HtmlHelperExtensions.IsDebug())
|
||||||
{
|
{
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Debug" asp-action="Index">Debug</a>
|
<a class="nav-link text-dark" asp-area="" asp-controller="Debuging" asp-action="Index">Debug</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
8
src/BirdsiteLive/Views/Users/Tweet.cshtml
Normal file
8
src/BirdsiteLive/Views/Users/Tweet.cshtml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Tweet";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Embedded tweet <a href="https://twitter.com/TwitterSupport/status/@ViewData.Model">Tweet</a></blockquote>
|
||||||
|
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||||
|
</div>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Dapper" Version="2.0.35" />
|
||||||
|
<PackageReference Include="Npgsql" Version="4.1.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\BirdsiteLive.DAL\BirdsiteLive.DAL.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,25 @@
|
||||||
|
using BirdsiteLive.DAL.Postgres.Settings;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers.Base
|
||||||
|
{
|
||||||
|
public class PostgresBase
|
||||||
|
{
|
||||||
|
protected readonly PostgresSettings _settings;
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
protected PostgresBase(PostgresSettings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected NpgsqlConnection Connection
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new NpgsqlConnection(_settings.ConnString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
using System;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
using BirdsiteLive.DAL.Contracts;
|
||||||
|
using BirdsiteLive.DAL.Postgres.DataAccessLayers.Base;
|
||||||
|
using BirdsiteLive.DAL.Postgres.Settings;
|
||||||
|
using BirdsiteLive.DAL.Postgres.Tools;
|
||||||
|
using Dapper;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
||||||
|
{
|
||||||
|
internal class DbVersion
|
||||||
|
{
|
||||||
|
public string Type { get; set; }
|
||||||
|
public int Major { get; set; }
|
||||||
|
public int Minor { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DbInitializerPostgresDal : PostgresBase, IDbInitializerDal
|
||||||
|
{
|
||||||
|
private readonly PostgresTools _tools;
|
||||||
|
private readonly Version _currentVersion = new Version(1,0);
|
||||||
|
private const string DbVersionType = "db-version";
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
public DbInitializerPostgresDal(PostgresSettings settings, PostgresTools tools) : base(settings)
|
||||||
|
{
|
||||||
|
_tools = tools;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task<Version> GetCurrentDbVersionAsync()
|
||||||
|
{
|
||||||
|
var query = $"SELECT * FROM {_settings.DbVersionTableName} WHERE type = @type";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var dbConnection = Connection)
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
var result = (await dbConnection.QueryAsync<DbVersion>(query, new { type = DbVersionType })).FirstOrDefault();
|
||||||
|
|
||||||
|
if (result == default)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Version(result.Major, result.Minor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (PostgresException e)
|
||||||
|
{
|
||||||
|
if (e.Message.Contains("42P01"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Version GetMandatoryDbVersion()
|
||||||
|
{
|
||||||
|
return _currentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tuple<Version, Version>[] GetMigrationPatterns()
|
||||||
|
{
|
||||||
|
return new Tuple<Version, Version>[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task MigrateDbAsync(Version from, Version to)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitDbAsync()
|
||||||
|
{
|
||||||
|
// Create version table
|
||||||
|
var createVersion = $@"CREATE TABLE {_settings.DbVersionTableName}
|
||||||
|
(
|
||||||
|
type VARCHAR(128) PRIMARY KEY,
|
||||||
|
|
||||||
|
major SMALLINT NOT NULL,
|
||||||
|
minor SMALLINT NOT NULL
|
||||||
|
);";
|
||||||
|
await _tools.ExecuteRequestAsync(createVersion);
|
||||||
|
|
||||||
|
// Create Twitter User table
|
||||||
|
var createTwitter = $@"CREATE TABLE {_settings.TwitterUserTableName}
|
||||||
|
(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
acct VARCHAR(20) UNIQUE,
|
||||||
|
|
||||||
|
lastTweetPostedId BIGINT,
|
||||||
|
lastTweetSynchronizedForAllFollowersId BIGINT
|
||||||
|
);";
|
||||||
|
await _tools.ExecuteRequestAsync(createTwitter);
|
||||||
|
|
||||||
|
// Create Followers table
|
||||||
|
var createFollowers = $@"CREATE TABLE {_settings.FollowersTableName}
|
||||||
|
(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
|
||||||
|
followings INTEGER[],
|
||||||
|
followingsSyncStatus JSONB,
|
||||||
|
|
||||||
|
acct VARCHAR(20) UNIQUE,
|
||||||
|
host VARCHAR(20)
|
||||||
|
);";
|
||||||
|
await _tools.ExecuteRequestAsync(createFollowers);
|
||||||
|
|
||||||
|
// Create Cached Tweet table
|
||||||
|
var createCachedTweets = $@"CREATE TABLE {_settings.CachedTweetsTableName}
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY,
|
||||||
|
twitterUserId INTEGER,
|
||||||
|
data JSONB
|
||||||
|
);";
|
||||||
|
await _tools.ExecuteRequestAsync(createCachedTweets);
|
||||||
|
|
||||||
|
// Insert version to db
|
||||||
|
using (var dbConnection = Connection)
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
await dbConnection.ExecuteAsync(
|
||||||
|
$"INSERT INTO {_settings.DbVersionTableName} (type,major,minor) VALUES(@type,@major,@minor)",
|
||||||
|
new { type = DbVersionType, major = _currentVersion.Major, minor = _currentVersion.Minor });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAllAsync()
|
||||||
|
{
|
||||||
|
var dropsRequests = new[]
|
||||||
|
{
|
||||||
|
$@"DROP TABLE {_settings.DbVersionTableName};",
|
||||||
|
$@"DROP TABLE {_settings.TwitterUserTableName};",
|
||||||
|
$@"DROP TABLE {_settings.FollowersTableName};",
|
||||||
|
$@"DROP TABLE {_settings.CachedTweetsTableName};"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var r in dropsRequests)
|
||||||
|
{
|
||||||
|
await _tools.ExecuteRequestAsync(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using BirdsiteLive.DAL.Contracts;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
||||||
|
{
|
||||||
|
public class FollowersPostgresDal : IFollowersDal
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using BirdsiteLive.DAL.Contracts;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
|
||||||
|
{
|
||||||
|
public class TwitterUserPostgresDal : ITwitterUserDal
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.Settings
|
||||||
|
{
|
||||||
|
public class PostgresSettings
|
||||||
|
{
|
||||||
|
public string ConnString { get; set; }
|
||||||
|
|
||||||
|
public string DbVersionTableName { get; set; } = "db-version";
|
||||||
|
public string TwitterUserTableName { get; set; } = "twitter-users";
|
||||||
|
public string FollowersTableName { get; set; } = "followers";
|
||||||
|
public string CachedTweetsTableName { get; set; } = "cached-tweets";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BirdsiteLive.DAL.Postgres.Settings;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.Tools
|
||||||
|
{
|
||||||
|
public class PostgresTools
|
||||||
|
{
|
||||||
|
private readonly PostgresSettings _settings;
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
public PostgresTools(PostgresSettings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task ExecuteRequestAsync(string request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var conn = new NpgsqlConnection(_settings.ConnString))
|
||||||
|
using (var cmd = new NpgsqlCommand(request, conn))
|
||||||
|
{
|
||||||
|
await conn.OpenAsync();
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Contracts
|
||||||
|
{
|
||||||
|
public interface IDbInitializerDal
|
||||||
|
{
|
||||||
|
Task<Version> GetCurrentDbVersionAsync();
|
||||||
|
Version GetMandatoryDbVersion();
|
||||||
|
Tuple<Version, Version>[] GetMigrationPatterns();
|
||||||
|
Task MigrateDbAsync(Version from, Version to);
|
||||||
|
Task InitDbAsync();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace BirdsiteLive.DAL.Contracts
|
||||||
|
{
|
||||||
|
public interface IFollowersDal
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace BirdsiteLive.DAL.Contracts
|
||||||
|
{
|
||||||
|
public interface ITwitterUserDal
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
10
src/DataAccessLayers/BirdsiteLive.DAL/Models/CachedTweet.cs
Normal file
10
src/DataAccessLayers/BirdsiteLive.DAL/Models/CachedTweet.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace BirdsiteLive.DAL.Models
|
||||||
|
{
|
||||||
|
public class CachedTweet
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public long TwitterUserId { get; set; }
|
||||||
|
|
||||||
|
public string TweetData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs
Normal file
13
src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace BirdsiteLive.DAL.Models
|
||||||
|
{
|
||||||
|
public class Follower
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int FollowingAccountId { get; set; }
|
||||||
|
|
||||||
|
public string Acct { get; set; }
|
||||||
|
public string Host { get; set; }
|
||||||
|
|
||||||
|
public long LastTweetSynchronizedId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace BirdsiteLive.DAL.Models
|
||||||
|
{
|
||||||
|
public class SyncTwitterUser
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Acct { get; set; }
|
||||||
|
|
||||||
|
public long LastTweetPostedId { get; set; }
|
||||||
|
public long LastTweetSynchronizedForAllFollowersId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Tools
|
||||||
|
{
|
||||||
|
public static class RandomGenerator
|
||||||
|
{
|
||||||
|
private static readonly Random Random = new Random();
|
||||||
|
|
||||||
|
public static string GetString(int length)
|
||||||
|
{
|
||||||
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
return new string(Enumerable.Repeat(chars, length)
|
||||||
|
.Select(s => s[Random.Next(s.Length)]).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\DataAccessLayers\BirdsiteLive.DAL.Postgres\BirdsiteLive.DAL.Postgres.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BirdsiteLive.DAL.Postgres.DataAccessLayers;
|
||||||
|
using BirdsiteLive.DAL.Postgres.Settings;
|
||||||
|
using BirdsiteLive.DAL.Postgres.Tools;
|
||||||
|
using BirdsiteLive.DAL.Tools;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class DbInitializerPostgresDalTests
|
||||||
|
{
|
||||||
|
private readonly PostgresSettings _settings;
|
||||||
|
private readonly PostgresTools _tools;
|
||||||
|
|
||||||
|
#region Ctor
|
||||||
|
public DbInitializerPostgresDalTests()
|
||||||
|
{
|
||||||
|
_settings = new PostgresSettings
|
||||||
|
{
|
||||||
|
ConnString = "Host=127.0.0.1;Username=postgres;Password=mysecretpassword;Database=mytestdb",
|
||||||
|
DbVersionTableName = "DbVersionTableName" + RandomGenerator.GetString(4),
|
||||||
|
CachedTweetsTableName = "CachedTweetsTableName" + RandomGenerator.GetString(4),
|
||||||
|
FollowersTableName = "FollowersTableName" + RandomGenerator.GetString(4),
|
||||||
|
TwitterUserTableName = "TwitterUserTableName" + RandomGenerator.GetString(4),
|
||||||
|
};
|
||||||
|
_tools = new PostgresTools(_settings);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[TestCleanup]
|
||||||
|
public async Task CleanUp()
|
||||||
|
{
|
||||||
|
var dal = new DbInitializerPostgresDal(_settings, _tools);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await dal.DeleteAllAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task GetCurrentDbVersionAsync_UninitializedDb()
|
||||||
|
{
|
||||||
|
var dal = new DbInitializerPostgresDal(_settings, _tools);
|
||||||
|
|
||||||
|
var current = await dal.GetCurrentDbVersionAsync();
|
||||||
|
Assert.IsNull(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task InitDbAsync()
|
||||||
|
{
|
||||||
|
var dal = new DbInitializerPostgresDal(_settings, _tools);
|
||||||
|
|
||||||
|
await dal.InitDbAsync();
|
||||||
|
var current = await dal.GetCurrentDbVersionAsync();
|
||||||
|
var mandatory = dal.GetMandatoryDbVersion();
|
||||||
|
Assert.IsNotNull(current);
|
||||||
|
Assert.AreEqual(mandatory.Minor, current.Minor);
|
||||||
|
Assert.AreEqual(mandatory.Major, current.Major);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue