Add webhook support for card transactions and notifications.
Some optimizations and fixes Signed-off-by: Dmitrii <computer@yawaflua.tech> Took 2 hours 4 minutes
This commit is contained in:
@@ -71,13 +71,35 @@ public class AuthController(AppDbContext dbContext, TokenService tokenService, I
|
||||
|
||||
var dto = await resp.Content.ReadFromJsonAsync<MojangDto>();
|
||||
if (dto == null || dto.Name != session.UserName || Guid.Parse(dto.Id) != session.UserId) throw new Exception("Session expired, or dto is not acceptable.");
|
||||
|
||||
|
||||
var token = tokenService.GenerateAccessToken(session.UserName, body.userUUID);
|
||||
var user = await dbContext.Users.FirstOrDefaultAsync(k => k.Id == session.UserId);
|
||||
|
||||
var shortId = string.Empty;
|
||||
|
||||
if (user?.ShortId == null)
|
||||
{
|
||||
shortId = Program.GenerateRandomString(2);
|
||||
while (true)
|
||||
{
|
||||
var a = await dbContext.Users.FirstOrDefaultAsync(k => k.ShortId == shortId);
|
||||
if (a != null)
|
||||
{
|
||||
shortId = Program.GenerateRandomString(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user != null)
|
||||
{
|
||||
user.Token = token;
|
||||
user.Username = session.UserName;
|
||||
user.UpdatedAt = DateTime.UtcNow;
|
||||
user.ShortId ??= shortId;
|
||||
|
||||
dbContext.Update(user);
|
||||
}
|
||||
@@ -88,7 +110,8 @@ public class AuthController(AppDbContext dbContext, TokenService tokenService, I
|
||||
Id = body.userUUID,
|
||||
Username = session.UserName,
|
||||
Token = token,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
ShortId = shortId,
|
||||
};
|
||||
await dbContext.AddAsync(user);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public class TelemetryController : ControllerBase
|
||||
private static readonly ActivitySource Source = new("SpMega.ModTelemetry");
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Post([FromBody] ModTelemetryBatchDto batch)
|
||||
{
|
||||
if (batch?.Events == null || batch.Events.Count == 0)
|
||||
@@ -28,7 +28,7 @@ public class TelemetryController : ControllerBase
|
||||
|
||||
foreach (var e in batch.Events)
|
||||
{
|
||||
using var activity = Source.StartActivity("mod.telemetry.event", ActivityKind.Internal);
|
||||
using var activity = Source.StartActivity("mod.telemetry.event", ActivityKind.Client);
|
||||
if (activity is null) continue;
|
||||
|
||||
activity.SetTag("user.id", userId);
|
||||
@@ -38,6 +38,7 @@ public class TelemetryController : ControllerBase
|
||||
activity.SetTag("event.timestamp", e.Timestamp.ToString("O"));
|
||||
activity.SetTag("event.payload", JsonSerializer.Serialize(e.Payload));
|
||||
activity.SetTag("batch.sent_at", batch.SentAt.ToString("O"));
|
||||
|
||||
}
|
||||
|
||||
return Ok(new { received = batch.Events.Count });
|
||||
|
||||
@@ -37,12 +37,7 @@ public class TransactionsController(AppDbContext context, ILogger<TransactionsCo
|
||||
ShortId = "00000000",
|
||||
ReceiverName = "yawaflua",
|
||||
ReceiverCardNumber = "00000",
|
||||
Sender = new User
|
||||
{
|
||||
Id = default,
|
||||
Username = "yawaflua",
|
||||
Token = "123",
|
||||
},
|
||||
SenderMinecraftName = "yawaflua",
|
||||
SenderCardNumber = "010101",
|
||||
Amount = 42,
|
||||
Comment = "API FIRST",
|
||||
@@ -76,13 +71,13 @@ public class TransactionsController(AppDbContext context, ILogger<TransactionsCo
|
||||
}
|
||||
|
||||
|
||||
var shortId = Program.GenerateRandomString(5);
|
||||
var shortId = Program.GenerateRandomString(2);
|
||||
while (true)
|
||||
{
|
||||
var a = await context.Transactions.FirstOrDefaultAsync(k => k.ShortId == shortId);
|
||||
var a = await context.Transactions.FirstOrDefaultAsync(k => k.ShortId == shortId && EF.Property<Guid>(k, "SenderId") == user.Id);
|
||||
if (a != null)
|
||||
{
|
||||
shortId = Program.GenerateRandomString(5);
|
||||
shortId = Program.GenerateRandomString(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -92,24 +87,23 @@ public class TransactionsController(AppDbContext context, ILogger<TransactionsCo
|
||||
var transaction = new Transaction
|
||||
{
|
||||
ReceiverName = body.receiverName,
|
||||
ShortId = shortId,
|
||||
ShortId = $"{user.ShortId}{shortId}",
|
||||
ReceiverCardNumber = body.receiverCard,
|
||||
Sender = user,
|
||||
SenderCardNumber = body.cardId,
|
||||
SenderCardNumber = cardToUse.SpworldsID,
|
||||
SenderMinecraftName = user.Username,
|
||||
Amount = body.amount,
|
||||
Comment = body.comment,
|
||||
};
|
||||
try
|
||||
{
|
||||
var uri = "s.ywfl.dev" + "/" + shortId;
|
||||
var uri = "ywfl.dev" + "/s" + shortId;
|
||||
var transitionInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "receiver", body.receiverCard },
|
||||
{ "amount", body.amount },
|
||||
{ "comment", (body.comment)[8..] + "..;Чек:"+ uri }
|
||||
{ "comment", (body.comment)[10..] + "..;Чек:"+ uri }
|
||||
};
|
||||
Console.WriteLine(((body.comment)[8..] + "..;Чек:"+ uri).Length);
|
||||
Console.WriteLine(((body.comment)[8..] + "..;Чек:"+ uri));
|
||||
var resp = await SendRequest(endpoint: "transactions", body: transitionInfo, AuthHeader: new("Bearer", cardToUse.Token));
|
||||
var balance = (int?)JsonNode.Parse(resp)?["balance"];
|
||||
if (balance == null)
|
||||
@@ -131,6 +125,12 @@ public class TransactionsController(AppDbContext context, ILogger<TransactionsCo
|
||||
{
|
||||
Id = transaction.Id,
|
||||
ReceiverName = transaction.ReceiverName,
|
||||
ReceiverCardNumber = transaction.ReceiverCardNumber,
|
||||
SenderMinecraftName = transaction.SenderMinecraftName,
|
||||
SenderCardNumber = transaction.SenderCardNumber,
|
||||
Amount = transaction.Amount,
|
||||
Comment = transaction.Comment,
|
||||
TransactionDate = transaction.TransactionDate
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Driver.Linq;
|
||||
using SpMega.Backend.Persistent.Database;
|
||||
using SpMega.Backend.Persistent.Models.Transactions;
|
||||
using SpMega.Backend.Persistent.Models.Users;
|
||||
|
||||
namespace SpMega.Backend.Controllers.v1;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/v1/webhook")]
|
||||
public class WebhookController( AppDbContext dbContext, ILogger<WebhookController> logger, IConfiguration config) : ControllerBase
|
||||
{
|
||||
private const string BASE_URL = "https://spworlds.ru/api/public/";
|
||||
|
||||
|
||||
[HttpPut("{cardId}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> RegisterWebhookForCard(Guid cardId)
|
||||
{
|
||||
var user = HttpContext.Items["@me"] as User;
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
var cardToUse = user.Cards.FirstOrDefault(c => c.Id == cardId);
|
||||
if (cardToUse == null)
|
||||
return BadRequest("Card not found");
|
||||
|
||||
var shortId = Program.GenerateRandomString(6);
|
||||
while (true)
|
||||
{
|
||||
var a = user.Cards.FirstOrDefault(k => k.ShortId == shortId);
|
||||
if (a != null)
|
||||
{
|
||||
shortId = Program.GenerateRandomString(6);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
cardToUse.ShortId = shortId;
|
||||
cardToUse.WebhookConnected = true;
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
var webhookUrl = new UriBuilder();
|
||||
webhookUrl.Scheme = "https";
|
||||
webhookUrl.Host = config["Url"];
|
||||
webhookUrl.Path = $"api/v1/webhook/{user.ShortId}/{cardToUse.ShortId}/{cardId}";
|
||||
|
||||
var resp = await SendRequest($"card/webhook", new AuthenticationHeaderValue("Bearer", cardToUse.Token), HttpMethod.Put, new { url = webhookUrl.ToString() });
|
||||
var jsonResponse = JsonSerializer.Deserialize<JsonObject>(resp);
|
||||
if (jsonResponse != null && jsonResponse.TryGetPropertyValue("id", out var respId)
|
||||
&& respId?.GetValue<Guid>() == cardId) return Ok();
|
||||
logger.LogError("Failed to register webhook for card {CardId}. Response: {Response}", cardId, resp);
|
||||
cardToUse.WebhookConnected = false;
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
return BadRequest("Failed to register webhook for card");
|
||||
}
|
||||
|
||||
[HttpPost("{userShortId}/{cardShortId}/{cardId}")]
|
||||
public async Task<IActionResult> ReceiveWebhook(string userShortId, string cardShortId, Guid cardId)
|
||||
{
|
||||
var user = await dbContext.Users.FirstOrDefaultAsync(k => k.ShortId == userShortId);
|
||||
if (user == null)
|
||||
return BadRequest("User not found");
|
||||
var cardToUse = user.Cards.FirstOrDefault(c => c.ShortId == cardShortId && c.Id == cardId && c.WebhookConnected);
|
||||
if (cardToUse == null)
|
||||
return BadRequest("Card not found");
|
||||
|
||||
var bodyHash = Request.Headers["X-Body-Hash"].ToString();
|
||||
var rawBody = await new StreamReader(Request.Body).ReadToEndAsync();
|
||||
var keyBytes = Encoding.UTF8.GetBytes(cardToUse.Token);
|
||||
var messageBytes = Encoding.UTF8.GetBytes(rawBody);
|
||||
|
||||
using var hmac = new HMACSHA256(keyBytes);
|
||||
var hashBytes = hmac.ComputeHash(messageBytes);
|
||||
|
||||
byte[] receivedHash;
|
||||
try
|
||||
{
|
||||
receivedHash = Convert.FromBase64String(bodyHash);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return BadRequest("Invalid body hash");
|
||||
}
|
||||
|
||||
if (!CryptographicOperations.FixedTimeEquals(hashBytes, receivedHash))
|
||||
{
|
||||
logger.LogError("Invalid body hash for webhook. Expected: {ExpectedHash}, Received: {ReceivedHash}", Convert.ToBase64String(hashBytes), bodyHash);
|
||||
return BadRequest("Invalid body hash");
|
||||
}
|
||||
|
||||
var body = JsonSerializer.Deserialize<Webhook>(rawBody, new JsonSerializerOptions(JsonSerializerDefaults.Web));
|
||||
if (body == null || !Guid.TryParse(body.Id, out var notificationId))
|
||||
return BadRequest("Invalid webhook body");
|
||||
|
||||
if (await dbContext.Notifications.FirstOrDefaultAsync(notification => notification.Id == notificationId) != null)
|
||||
return Ok();
|
||||
|
||||
var notify = new Notification
|
||||
{
|
||||
Id = notificationId,
|
||||
ReceiverId = user.Id,
|
||||
ReceiverName = body.Receiver.Username,
|
||||
ReceiverNumber = body.Receiver.Number,
|
||||
SenderName = body.Sender.Username,
|
||||
SenderNumber = body.Sender.Number,
|
||||
Comment = body.Comment,
|
||||
Amount = body.Amount,
|
||||
Type = body.Type,
|
||||
IsRead = false,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await dbContext.Notifications.AddAsync(notify);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("read")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<Notification>>> ReadAllNotifications()
|
||||
{
|
||||
var user = HttpContext.Items["@me"] as User;
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
var notifications = await dbContext.Notifications
|
||||
.Where(notification => notification.ReceiverId == user.Id && !notification.IsRead)
|
||||
.ToListAsync();
|
||||
foreach (var notification in notifications)
|
||||
{
|
||||
notification.IsRead = true;
|
||||
notification.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
if (notifications.Count > 0) await dbContext.SaveChangesAsync();
|
||||
return Ok(notifications);
|
||||
}
|
||||
|
||||
[HttpGet("all")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<Notification>>> GetAllNotifications([FromQuery] int after, [FromQuery] int limit = 20)
|
||||
{
|
||||
var user = HttpContext.Items["@me"] as User;
|
||||
if (user == null) return Unauthorized();
|
||||
var notification = await dbContext.Notifications.Where(k => k.ReceiverId == user.Id).Skip(after).Take(limit).ToListAsync();
|
||||
return Ok(notification);
|
||||
}
|
||||
|
||||
[NonAction]
|
||||
internal async Task<string> SendRequest(string endpoint, AuthenticationHeaderValue AuthHeader, HttpMethod method = null, object body = null)
|
||||
{
|
||||
method ??= body == null ? HttpMethod.Get : HttpMethod.Post;
|
||||
|
||||
HttpResponseMessage message;
|
||||
var client = new System.Net.Http.HttpClient();
|
||||
|
||||
using (var requestMessage = new HttpRequestMessage(method, BASE_URL + endpoint))
|
||||
{
|
||||
requestMessage.Content = new StringContent(
|
||||
JsonSerializer.Serialize(body),
|
||||
Encoding.UTF8, "application/json"
|
||||
);
|
||||
|
||||
requestMessage.Headers.Authorization = AuthHeader;
|
||||
|
||||
message = await client.SendAsync(requestMessage);
|
||||
|
||||
}
|
||||
|
||||
client.Dispose();
|
||||
|
||||
return await message.Content.ReadAsStringAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public record Webhook(
|
||||
string Id,
|
||||
int Amount,
|
||||
string Type,
|
||||
WebhookUser Sender,
|
||||
WebhookUser Receiver,
|
||||
string Comment,
|
||||
string CreatedAt
|
||||
);
|
||||
|
||||
public record WebhookUser(string Username, string Number);
|
||||
@@ -12,8 +12,7 @@ public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(op
|
||||
internal DbSet<Transaction> Transactions { get; set; }
|
||||
internal DbSet<User> Users { get; set; }
|
||||
internal DbSet<UserSession> UserSessions { get; set; }
|
||||
internal DbSet<ModTelemetryDocument> ModTelemetry { get; set; }
|
||||
internal DbSet<BackendRequestTelemetryDocument> BackendRequestTelemetry { get; set; }
|
||||
internal DbSet<Notification> Notifications { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using SpMega.Backend.Persistent.Models.Users;
|
||||
|
||||
namespace SpMega.Backend.Persistent.Models.Transactions;
|
||||
|
||||
public class Notification
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid ReceiverId { get; set; }
|
||||
public string ReceiverName { get; set; }
|
||||
public string ReceiverNumber { get; set; }
|
||||
public string SenderName { get; set; }
|
||||
public string SenderNumber { get; set; }
|
||||
|
||||
public string Comment { get; set; }
|
||||
public int Amount { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
public bool IsRead { get; set; } = false;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
using SpMega.Backend.Persistent.Models.Users;
|
||||
|
||||
@@ -7,13 +8,15 @@ public class Transaction
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string ShortId { get; set; } = Program.GenerateRandomString(8);
|
||||
public string ReceiverName { get; set; }
|
||||
public string ReceiverCardNumber { get; set; }
|
||||
[MaxLength(2048)] public string ReceiverName { get; set; }
|
||||
[MaxLength(2048)] public string ReceiverCardNumber { get; set; }
|
||||
[JsonIgnore] public User Sender { get; set; }
|
||||
public string SenderCardNumber { get; set; }
|
||||
|
||||
[MaxLength(2048)] public string SenderMinecraftName { get; set; }
|
||||
[MaxLength(2048)] public string SenderCardNumber { get; set; }
|
||||
|
||||
public int Amount { get; set; } = 0;
|
||||
public string Comment { get; set; } = "";
|
||||
[MaxLength(2048)] public string Comment { get; set; } = "";
|
||||
|
||||
public DateTime TransactionDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -9,6 +9,8 @@ public class Card
|
||||
public string Name { get; set; }
|
||||
public string SpworldsID { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string ShortId { get; set; } = "";
|
||||
public bool WebhookConnected { get; set; } = false;
|
||||
|
||||
public int Balance { get; set; } = 0;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using SpMega.Backend.Persistent.Models.Transactions;
|
||||
|
||||
namespace SpMega.Backend.Persistent.Models.Users;
|
||||
@@ -5,8 +6,10 @@ namespace SpMega.Backend.Persistent.Models.Users;
|
||||
public class User
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Token { get; set; }
|
||||
[MaxLength(2048)] public string Username { get; set; }
|
||||
[MaxLength(2048)] public string Token { get; set; }
|
||||
|
||||
[MaxLength(2048)] public string? ShortId { get; set; }
|
||||
|
||||
public List<Card> Cards { get; set; } = [];
|
||||
public List<Transaction> Transactions { get; set; } = [];
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SpMega.Backend.Persistent.Models.Users;
|
||||
|
||||
public class UserSession
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Token { get; set; }
|
||||
[MaxLength(2048)] public string Token { get; set; }
|
||||
public User? User { get; set; } = null;
|
||||
public Guid UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
[MaxLength(2048)] public string UserName { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Text;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MongoDB.Driver;
|
||||
using OpenTelemetry.Exporter;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Trace;
|
||||
using SpMega.Backend.Authetication;
|
||||
@@ -68,7 +70,10 @@ public class Program
|
||||
options.UseMongoDB(mongoClient, "spmega");
|
||||
});
|
||||
|
||||
var otlpEndpoint = conf["Otlp:Endpoint"] ?? "http://localhost:4317";
|
||||
var otlpEndpoint = (conf["Otlp__Endpoint"] ?? "http://curiosity:5080/api/default").TrimEnd('/');
|
||||
var otlpHeaders = conf["Otlp__Headers"] ?? throw new InvalidOperationException("Otlp__Headers is not configured.");
|
||||
var tracesEndpoint = new Uri($"{otlpEndpoint}/v1/traces");
|
||||
var metricsEndpoint = new Uri($"{otlpEndpoint}/v1/metrics");
|
||||
builder.Services.AddOpenTelemetry()
|
||||
.ConfigureResource(res => res.AddService("SpMega.Backend"))
|
||||
.WithTracing(tracing =>
|
||||
@@ -79,9 +84,24 @@ public class Program
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddOtlpExporter(opts =>
|
||||
{
|
||||
opts.Endpoint = new Uri(otlpEndpoint);
|
||||
opts.Protocol = OtlpExportProtocol.HttpProtobuf;
|
||||
opts.Endpoint = tracesEndpoint;
|
||||
opts.Headers = otlpHeaders;
|
||||
});
|
||||
});
|
||||
})
|
||||
.WithMetrics(metrics => metrics
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddMeter(
|
||||
"Microsoft.AspNetCore.Hosting",
|
||||
"Microsoft.AspNetCore.Server.Kestrel",
|
||||
"System.Net.Http",
|
||||
"System.Runtime")
|
||||
.AddOtlpExporter(opts =>
|
||||
{
|
||||
opts.Protocol = OtlpExportProtocol.HttpProtobuf;
|
||||
opts.Endpoint = metricsEndpoint;
|
||||
opts.Headers = otlpHeaders;
|
||||
}));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@@ -113,4 +133,4 @@ public class Program
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"SpMega.Backend/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore.Abstractions": "10.0.9",
|
||||
"Microsoft.EntityFrameworkCore.Relational": "10.0.9",
|
||||
"Microsoft.IdentityModel.Tokens": "8.19.1",
|
||||
"MongoDB.EntityFrameworkCore": "10.0.2",
|
||||
"System.IdentityModel.Tokens.Jwt": "8.19.1",
|
||||
"spworlds-api": "1.0.3"
|
||||
},
|
||||
"runtime": {
|
||||
"SpMega.Backend.dll": {}
|
||||
}
|
||||
},
|
||||
"DnsClient/1.6.1": {
|
||||
"runtime": {
|
||||
"lib/net5.0/DnsClient.dll": {
|
||||
"assemblyVersion": "1.6.1.0",
|
||||
"fileVersion": "1.6.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.Cryptography/10.0.2": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Bcl.Cryptography.dll": {
|
||||
"assemblyVersion": "10.0.0.2",
|
||||
"fileVersion": "10.0.225.61305"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore/10.0.9": {
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore.Abstractions": "10.0.9"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.EntityFrameworkCore.dll": {
|
||||
"assemblyVersion": "10.0.9.0",
|
||||
"fileVersion": "10.0.926.27113"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Abstractions/10.0.9": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.9.0",
|
||||
"fileVersion": "10.0.926.27113"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Relational/10.0.9": {
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore": "10.0.9"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.EntityFrameworkCore.Relational.dll": {
|
||||
"assemblyVersion": "10.0.9.0",
|
||||
"fileVersion": "10.0.926.27113"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.19.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "8.19.1.0",
|
||||
"fileVersion": "8.19.1.26153"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.19.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Tokens": "8.19.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
|
||||
"assemblyVersion": "8.19.1.0",
|
||||
"fileVersion": "8.19.1.26153"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.19.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.19.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "8.19.1.0",
|
||||
"fileVersion": "8.19.1.26153"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.19.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.Cryptography": "10.0.2",
|
||||
"Microsoft.IdentityModel.Logging": "8.19.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "8.19.1.0",
|
||||
"fileVersion": "8.19.1.26153"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Bson/3.9.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Bson.dll": {
|
||||
"assemblyVersion": "3.9.0.0",
|
||||
"fileVersion": "3.9.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.Driver/3.9.0": {
|
||||
"dependencies": {
|
||||
"DnsClient": "1.6.1",
|
||||
"MongoDB.Bson": "3.9.0",
|
||||
"SharpCompress": "0.48.1",
|
||||
"Snappier": "1.3.1",
|
||||
"ZstdSharp.Port": "0.7.3"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/MongoDB.Driver.dll": {
|
||||
"assemblyVersion": "3.9.0.0",
|
||||
"fileVersion": "3.9.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MongoDB.EntityFrameworkCore/10.0.2": {
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore": "10.0.9",
|
||||
"MongoDB.Driver": "3.9.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/MongoDB.EntityFrameworkCore.dll": {
|
||||
"assemblyVersion": "10.0.2.0",
|
||||
"fileVersion": "10.0.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SharpCompress/0.48.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/SharpCompress.dll": {
|
||||
"assemblyVersion": "0.48.1.0",
|
||||
"fileVersion": "0.48.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Snappier/1.3.1": {
|
||||
"runtime": {
|
||||
"lib/net8.0/Snappier.dll": {
|
||||
"assemblyVersion": "1.3.1.0",
|
||||
"fileVersion": "1.3.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"spworlds-api/1.0.3": {
|
||||
"runtime": {
|
||||
"lib/net8.0/spworlds-api.dll": {
|
||||
"assemblyVersion": "1.0.3.0",
|
||||
"fileVersion": "1.0.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/8.19.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "8.19.1",
|
||||
"Microsoft.IdentityModel.Tokens": "8.19.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/System.IdentityModel.Tokens.Jwt.dll": {
|
||||
"assemblyVersion": "8.19.1.0",
|
||||
"fileVersion": "8.19.1.26153"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port/0.7.3": {
|
||||
"runtime": {
|
||||
"lib/net7.0/ZstdSharp.dll": {
|
||||
"assemblyVersion": "0.7.3.0",
|
||||
"fileVersion": "0.7.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"SpMega.Backend/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"DnsClient/1.6.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==",
|
||||
"path": "dnsclient/1.6.1",
|
||||
"hashPath": "dnsclient.1.6.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Bcl.Cryptography/10.0.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-LG9Yll3B5aNpxv0+D47g6LiOiKBIlodhcHdQwcYzo8VeexFLGqx5ymetmA2aBRyo9cCcWsQWrFsdbsr8LvmWDw==",
|
||||
"path": "microsoft.bcl.cryptography/10.0.2",
|
||||
"hashPath": "microsoft.bcl.cryptography.10.0.2.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore/10.0.9": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-tu85SRzOT021V7EQlViCiAE7TqldVn469Y6lt5TEn/+XC4/MeNCHgMRSxqYuWqvF4zAQZUhCmtNEZuM3ss4LeA==",
|
||||
"path": "microsoft.entityframeworkcore/10.0.9",
|
||||
"hashPath": "microsoft.entityframeworkcore.10.0.9.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Abstractions/10.0.9": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-GRMaiPkqYna/gCsyDffYDWmefGPC3hDrdMw+2rrGcQwhs6uZOsaMQXMJnoXQ35tx9SkBV2ieRRU9N/jLOO6BZw==",
|
||||
"path": "microsoft.entityframeworkcore.abstractions/10.0.9",
|
||||
"hashPath": "microsoft.entityframeworkcore.abstractions.10.0.9.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Relational/10.0.9": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-dEoyYKgiaZHHgOFm1WMWm1sFEsEuhPWufX4L9PekKtqd/RaIcPjkCjvbrVvJtApErb5wPSJhYvnTlxhH+p9h2g==",
|
||||
"path": "microsoft.entityframeworkcore.relational/10.0.9",
|
||||
"hashPath": "microsoft.entityframeworkcore.relational.10.0.9.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.19.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-gFA8THIk23uNF/vMdOHnjIdXD1LyA2g12cHzMJ+Xag6WpgWLw6E/6uCXxvA0gp9d2yAvkRt3xzFzMUiO/hofnQ==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.19.1",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.19.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/8.19.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6eeY+y2QFyjj3XnCz/8gJdoP5smYHTS9ow1bw2nsZzDIPjPhBZlackYTIduSMipVpxnoT/B62LkrXX2jPggOXg==",
|
||||
"path": "microsoft.identitymodel.jsonwebtokens/8.19.1",
|
||||
"hashPath": "microsoft.identitymodel.jsonwebtokens.8.19.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.19.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-H+sMrMpdbWnwkQnpb/ESkQovtOgdefmj0ecGCcP40mDKzE5i4dUYkH6599M9mWYFNGNJnTp92l/9wLubYXWimw==",
|
||||
"path": "microsoft.identitymodel.logging/8.19.1",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.19.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.19.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-KDiuSLXud2AFVNAOottd8ztVysfPeHyr4r8gofU3/VKUXlI7oytzGTnPsNJ/B3nui17rgz8wAdWNJOtzPjkUxw==",
|
||||
"path": "microsoft.identitymodel.tokens/8.19.1",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.19.1.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Bson/3.9.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-J6vB61zKwMSfxbkN+/amGvj1qVMDKrKjV3kmoOWttMcv8JJScLDCFh89FyL3f2lDUyJrnfgZCR6/KX+07e99eg==",
|
||||
"path": "mongodb.bson/3.9.0",
|
||||
"hashPath": "mongodb.bson.3.9.0.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.Driver/3.9.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-XKUa+y5RtNH1iInfxj3Y7c1FN1BQ16/7hFxoqU6fzc3+BKM1D3mGa+pB/yBbAk8jNxf7+JWEnCfuQOyCo7dQLg==",
|
||||
"path": "mongodb.driver/3.9.0",
|
||||
"hashPath": "mongodb.driver.3.9.0.nupkg.sha512"
|
||||
},
|
||||
"MongoDB.EntityFrameworkCore/10.0.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-GBN9+OAoIyBEgVPlYTXW3tyIsnpGPCA5AWZPy+X7ca3zJu+2Lli9FODZWl//w0yZHqwGwsLUL6XhcPPEaBYvBQ==",
|
||||
"path": "mongodb.entityframeworkcore/10.0.2",
|
||||
"hashPath": "mongodb.entityframeworkcore.10.0.2.nupkg.sha512"
|
||||
},
|
||||
"SharpCompress/0.48.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SqGaVniGG943Gph/gHhUQUiZPyC7y0tXZyMf0/B2oGsMav9dqs7JJOuUA+xOkwKYaWM2TM7aZQjNK91f4bX71A==",
|
||||
"path": "sharpcompress/0.48.1",
|
||||
"hashPath": "sharpcompress.0.48.1.nupkg.sha512"
|
||||
},
|
||||
"Snappier/1.3.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-DOdDQiO8YZ5rBtVLY+6CmR1yp9WYoJRgEEktPBrR0tEj9QO2djA/zv0O3DX0OZpEAfosbY8pytQ9tQUogwQsEA==",
|
||||
"path": "snappier/1.3.1",
|
||||
"hashPath": "snappier.1.3.1.nupkg.sha512"
|
||||
},
|
||||
"spworlds-api/1.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-rLQsC3MiXBfPKf3v45Qcrj0yypfZktTPuT/eGUJpcAlSZix1FBSRUbQVtfXbA8BHGIqn7nhZMVetaP3dmk+n1g==",
|
||||
"path": "spworlds-api/1.0.3",
|
||||
"hashPath": "spworlds-api.1.0.3.nupkg.sha512"
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/8.19.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-2VHcRtT95GAcW1E3aVBLvL2rAAMxKHXKMXKXFyWzwgkdFXZPMMvP8tVOfnRydL4vTr1RirNuGC6T8VSEF2YsPQ==",
|
||||
"path": "system.identitymodel.tokens.jwt/8.19.1",
|
||||
"hashPath": "system.identitymodel.tokens.jwt.8.19.1.nupkg.sha512"
|
||||
},
|
||||
"ZstdSharp.Port/0.7.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==",
|
||||
"path": "zstdsharp.port/0.7.3",
|
||||
"hashPath": "zstdsharp.port.0.7.3.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "10.0.0"
|
||||
}
|
||||
],
|
||||
"configProperties": {
|
||||
"System.GC.Server": true,
|
||||
"System.Reflection.NullabilityInfoContext.IsSupported": true,
|
||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"ManifestType": "Build",
|
||||
"Endpoints": []
|
||||
}
|
||||
Reference in New Issue
Block a user