add FoxchatError type
This commit is contained in:
parent
f6629fbb33
commit
d34c41a126
7 changed files with 42 additions and 20 deletions
|
@ -29,7 +29,7 @@ public abstract class IDatabaseContext : DbContext
|
||||||
public async Task<Instance> GetInstanceAsync(CancellationToken ct = default)
|
public async Task<Instance> GetInstanceAsync(CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
var instance = await Instance.FirstOrDefaultAsync(ct)
|
var instance = await Instance.FirstOrDefaultAsync(ct)
|
||||||
?? throw new Exception("GetInstanceAsync called without Instance being initialized"); // TODO: replace this with specific exception type
|
?? throw new FoxchatError.DatabaseError("GetInstanceAsync called without Instance being initialized");
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using Foxchat.Core.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
@ -9,9 +10,6 @@ public partial class RequestSigningService
|
||||||
public const string USER_AGENT_HEADER = "User-Agent";
|
public const string USER_AGENT_HEADER = "User-Agent";
|
||||||
public const string USER_AGENT = "Foxchat.NET";
|
public const string USER_AGENT = "Foxchat.NET";
|
||||||
public const string DATE_HEADER = "Date";
|
public const string DATE_HEADER = "Date";
|
||||||
public const string CONTENT_LENGTH_HEADER = "Content-Length";
|
|
||||||
public const string CONTENT_TYPE_HEADER = "Content-Type";
|
|
||||||
public const string CONTENT_TYPE = "application/json; charset=utf-8";
|
|
||||||
|
|
||||||
public const string SERVER_HEADER = "X-Foxchat-Server";
|
public const string SERVER_HEADER = "X-Foxchat-Server";
|
||||||
public const string SIGNATURE_HEADER = "X-Foxchat-Signature";
|
public const string SIGNATURE_HEADER = "X-Foxchat-Signature";
|
||||||
|
@ -28,19 +26,16 @@ public partial class RequestSigningService
|
||||||
public async Task<T> RequestAsync<T>(HttpMethod method, string domain, string requestPath, string? userId = null, object? body = null)
|
public async Task<T> RequestAsync<T>(HttpMethod method, string domain, string requestPath, string? userId = null, object? body = null)
|
||||||
{
|
{
|
||||||
var request = BuildHttpRequest(method, domain, requestPath, userId, body);
|
var request = BuildHttpRequest(method, domain, requestPath, userId, body);
|
||||||
_logger.Debug("Content length in header: '{ContentLength}'", request.Headers.Where(c => c.Key == "Content-Length"));
|
|
||||||
var resp = await _httpClient.SendAsync(request);
|
var resp = await _httpClient.SendAsync(request);
|
||||||
if (!resp.IsSuccessStatusCode)
|
if (!resp.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var error = await resp.Content.ReadAsStringAsync();
|
var error = await resp.Content.ReadAsStringAsync();
|
||||||
_logger.Error("Received {Status}, body: {Error}", resp.StatusCode, error);
|
throw new FoxchatError.OutgoingFederationError($"Request to {domain}/{requestPath} returned an error", DeserializeObject<ApiError>(error));
|
||||||
// TODO: replace this with specific exception type
|
|
||||||
throw new Exception("oh no a request error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var bodyString = await resp.Content.ReadAsStringAsync();
|
var bodyString = await resp.Content.ReadAsStringAsync();
|
||||||
// TODO: replace this with specific exception type
|
return DeserializeObject<T>(bodyString)
|
||||||
return DeserializeObject<T>(bodyString) ?? throw new Exception("oh no invalid json");
|
?? throw new FoxchatError.OutgoingFederationError($"Request to {domain}/{requestPath} returned invalid response body");
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpRequestMessage BuildHttpRequest(HttpMethod method, string domain, string requestPath, string? userId = null, object? bodyData = null)
|
private HttpRequestMessage BuildHttpRequest(HttpMethod method, string domain, string requestPath, string? userId = null, object? bodyData = null)
|
||||||
|
|
|
@ -27,7 +27,7 @@ public partial class RequestSigningService(ILogger logger, IClock clock, IDataba
|
||||||
formatter.SetHashAlgorithm(nameof(SHA256));
|
formatter.SetHashAlgorithm(nameof(SHA256));
|
||||||
var signature = formatter.CreateSignature(hash);
|
var signature = formatter.CreateSignature(hash);
|
||||||
|
|
||||||
_logger.Debug("Generated signature for {Host} {RequestPath}: {Signature}", data.Host, data.RequestPath, WebEncoders.Base64UrlEncode(signature));
|
_logger.Debug("Generated signature for {Host} {RequestPath}", data.Host, data.RequestPath);
|
||||||
return WebEncoders.Base64UrlEncode(signature);
|
return WebEncoders.Base64UrlEncode(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,13 +41,11 @@ public partial class RequestSigningService(ILogger logger, IClock clock, IDataba
|
||||||
var time = ParseTime(dateHeader);
|
var time = ParseTime(dateHeader);
|
||||||
if ((now + Duration.FromMinutes(1)) < time)
|
if ((now + Duration.FromMinutes(1)) < time)
|
||||||
{
|
{
|
||||||
// TODO: replace this with specific exception type
|
throw new FoxchatError.IncomingFederationError("Request was made in the future");
|
||||||
throw new Exception("Request was made in the future");
|
|
||||||
}
|
}
|
||||||
else if ((now - Duration.FromMinutes(1)) > time)
|
else if ((now - Duration.FromMinutes(1)) > time)
|
||||||
{
|
{
|
||||||
// TODO: replace this with specific exception type
|
throw new FoxchatError.IncomingFederationError("Request was made too long ago");
|
||||||
throw new Exception("Request was made too long ago");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var plaintext = GeneratePlaintext(new SignatureData(time, host, requestPath, contentLength, userId));
|
var plaintext = GeneratePlaintext(new SignatureData(time, host, requestPath, contentLength, userId));
|
||||||
|
@ -67,8 +65,6 @@ public partial class RequestSigningService(ILogger logger, IClock clock, IDataba
|
||||||
var contentLength = data.ContentLength != null ? data.ContentLength.ToString() : "";
|
var contentLength = data.ContentLength != null ? data.ContentLength.ToString() : "";
|
||||||
var userId = data.UserId ?? "";
|
var userId = data.UserId ?? "";
|
||||||
|
|
||||||
Log.Information("Plaintext string: {Plaintext}", $"{time}:{data.Host}:{data.RequestPath}:{contentLength}:{userId}");
|
|
||||||
|
|
||||||
return $"{time}:{data.Host}:{data.RequestPath}:{contentLength}:{userId}";
|
return $"{time}:{data.Host}:{data.RequestPath}:{contentLength}:{userId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,4 @@ public record SignatureData(
|
||||||
string RequestPath,
|
string RequestPath,
|
||||||
int? ContentLength,
|
int? ContentLength,
|
||||||
string? UserId
|
string? UserId
|
||||||
) { }
|
);
|
||||||
|
|
15
Foxchat.Core/FoxchatError.cs
Normal file
15
Foxchat.Core/FoxchatError.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Foxchat.Core;
|
||||||
|
|
||||||
|
public class FoxchatError(string message) : Exception(message)
|
||||||
|
{
|
||||||
|
public class ApiError(string message) : FoxchatError(message);
|
||||||
|
public class BadRequest(string message) : ApiError(message);
|
||||||
|
public class IncomingFederationError(string message) : FoxchatError(message);
|
||||||
|
|
||||||
|
public class OutgoingFederationError(string message, Models.ApiError? innerError = null) : FoxchatError(message)
|
||||||
|
{
|
||||||
|
public Models.ApiError? InnerError => innerError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DatabaseError(string message) : FoxchatError(message);
|
||||||
|
}
|
17
Foxchat.Core/Models/ApiError.cs
Normal file
17
Foxchat.Core/Models/ApiError.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Foxchat.Core.Models;
|
||||||
|
|
||||||
|
public record ApiError(int Status, ErrorCode Code, string Message);
|
||||||
|
|
||||||
|
public enum ErrorCode
|
||||||
|
{
|
||||||
|
INTERNAL_SERVER_ERROR,
|
||||||
|
OBJECT_NOT_FOUND,
|
||||||
|
INVALID_SERVER,
|
||||||
|
INVALID_HEADER,
|
||||||
|
INVALID_DATE,
|
||||||
|
INVALID_SIGNATURE,
|
||||||
|
MISSING_SIGNATURE,
|
||||||
|
GUILD_NOT_FOUND,
|
||||||
|
UNAUTHORIZED,
|
||||||
|
INVALID_REST_EVENT,
|
||||||
|
}
|
|
@ -6,5 +6,4 @@ namespace Foxchat.Core;
|
||||||
public class UlidConverter() : ValueConverter<Ulid, Guid>(
|
public class UlidConverter() : ValueConverter<Ulid, Guid>(
|
||||||
convertToProviderExpression: x => x.ToGuid(),
|
convertToProviderExpression: x => x.ToGuid(),
|
||||||
convertFromProviderExpression: x => new Ulid(x)
|
convertFromProviderExpression: x => new Ulid(x)
|
||||||
)
|
);
|
||||||
{ }
|
|
||||||
|
|
Loading…
Reference in a new issue