mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 11:44:56 +00:00
Add cryptographic session key management for users
Introduces a new cryptographic key management system for user sessions, including models, DTOs, interfaces, and services for handling identity keys, signed pre-keys, and one-time pre-keys. Updates the SessionKeysController and DI configuration to use the new services. Adds related EF Core configurations and migrations, and updates the UserSession model to reference the new UserCryptoSession. Removes the obsolete ISessionKeyService interface.
This commit is contained in:
@@ -11,6 +11,7 @@ using Govor.Application.Interfaces.Medias;
|
|||||||
using Govor.Application.Interfaces.Messages;
|
using Govor.Application.Interfaces.Messages;
|
||||||
using Govor.Application.Interfaces.UserOnlineStatus;
|
using Govor.Application.Interfaces.UserOnlineStatus;
|
||||||
using Govor.Application.Interfaces.UserSession;
|
using Govor.Application.Interfaces.UserSession;
|
||||||
|
using Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
using Govor.Application.Services;
|
using Govor.Application.Services;
|
||||||
using Govor.Application.Services.Authentication;
|
using Govor.Application.Services.Authentication;
|
||||||
using Govor.Application.Services.Friends;
|
using Govor.Application.Services.Friends;
|
||||||
@@ -18,6 +19,7 @@ using Govor.Application.Services.Medias;
|
|||||||
using Govor.Application.Services.Messages;
|
using Govor.Application.Services.Messages;
|
||||||
using Govor.Application.Services.UserOnlineStatus;
|
using Govor.Application.Services.UserOnlineStatus;
|
||||||
using Govor.Application.Services.UserSessions;
|
using Govor.Application.Services.UserSessions;
|
||||||
|
using Govor.Application.Services.UserSessions.Crypto;
|
||||||
using Govor.Core.Infrastructure.Extensions;
|
using Govor.Core.Infrastructure.Extensions;
|
||||||
using Govor.Core.Infrastructure.Validators;
|
using Govor.Core.Infrastructure.Validators;
|
||||||
using Govor.Core.Models;
|
using Govor.Core.Models;
|
||||||
@@ -91,6 +93,10 @@ public static class ConfigurationProgramExtensions
|
|||||||
|
|
||||||
services.AddScoped<IUserSessionReader, UserSessionReader>();
|
services.AddScoped<IUserSessionReader, UserSessionReader>();
|
||||||
services.AddScoped<IUserSessionRevoker, UserSessionRevoker>();
|
services.AddScoped<IUserSessionRevoker, UserSessionRevoker>();
|
||||||
|
|
||||||
|
services.AddScoped<ISessionKeyAttacher, SessionKeyAttacher>();
|
||||||
|
services.AddScoped<ISessionKeysReader, SessionKeysReader>();
|
||||||
|
services.AddScoped<IOneTimePreKeysRotator, OneTimePreKeysRotator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRepositories(this IServiceCollection services)
|
public static void AddRepositories(this IServiceCollection services)
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
using Govor.Application.Interfaces.Friends;
|
||||||
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||||
|
using Govor.Application.Interfaces.UserSession;
|
||||||
|
using Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
using Govor.Contracts.Requests;
|
using Govor.Contracts.Requests;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -12,32 +15,96 @@ namespace Govor.API.Controllers.Authentication;
|
|||||||
public class SessionKeysController : Controller
|
public class SessionKeysController : Controller
|
||||||
{
|
{
|
||||||
private readonly ILogger<SessionKeysController> _logger;
|
private readonly ILogger<SessionKeysController> _logger;
|
||||||
|
private readonly IFriendshipService _friendshipService;
|
||||||
private readonly ICurrentUserSessionService _currentSession;
|
private readonly ICurrentUserSessionService _currentSession;
|
||||||
|
private readonly ISessionKeyAttacher _sessionKeyAttacher;
|
||||||
|
private readonly ISessionKeysReader _sessionKeysReader;
|
||||||
|
private readonly IOneTimePreKeysRotator _oneTimePreKeysRotator;
|
||||||
private readonly ICurrentUserService _currentUser;
|
private readonly ICurrentUserService _currentUser;
|
||||||
/*
|
|
||||||
|
public SessionKeysController(
|
||||||
|
ILogger<SessionKeysController> logger,
|
||||||
|
IFriendshipService friendshipService,
|
||||||
|
ICurrentUserSessionService currentSession,
|
||||||
|
ISessionKeyAttacher sessionKeyAttacher,
|
||||||
|
ISessionKeysReader sessionKeysReader,
|
||||||
|
ICurrentUserService currentUser)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_friendshipService = friendshipService;
|
||||||
|
_currentSession = currentSession;
|
||||||
|
_sessionKeyAttacher = sessionKeyAttacher;
|
||||||
|
_sessionKeysReader = sessionKeysReader;
|
||||||
|
_currentUser = currentUser;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("keys")]
|
[HttpPost("keys")]
|
||||||
public async Task<IActionResult> UploadSessionKeys([FromBody] UploadKeysRequest request)
|
public async Task<IActionResult> UploadSessionKeys([FromBody] UploadKeysRequest request)
|
||||||
{
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
if(request.OneTimePreKeys.Count > 100)
|
||||||
|
return BadRequest("Too many one time pre keys");
|
||||||
|
|
||||||
var sessionId = _currentSession.GetUserSessionId();
|
var sessionId = _currentSession.GetUserSessionId();
|
||||||
|
|
||||||
await _sessionKeyService.AttachKeysAsync(sessionId, request.PublicEncryptionKey, request.PublicSigningKey);
|
await _sessionKeyAttacher.AttachKeysAsync(sessionId,
|
||||||
|
request.IdentityKey,
|
||||||
|
request.SignedPreKey,
|
||||||
|
request.SignedPreKeySignature,
|
||||||
|
request.OneTimePreKeys);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet("users/{userId}/keys")]
|
[HttpGet("users/{userId}/keys")]
|
||||||
public async Task<IActionResult> GetUserPublicKeys(Guid userId)
|
public async Task<IActionResult> GetUserPublicKeys(Guid userId)
|
||||||
{
|
{
|
||||||
var requesterId = _currentUserService.UserId;
|
var requesterId = _currentUser.GetCurrentUserId();
|
||||||
|
|
||||||
if (!await _friendshipService.AreFriendsAsync(requesterId, userId))
|
if (!(await _friendshipService.GetFriendsAsync(userId)).Select(u => u.Id).Contains(requesterId))
|
||||||
return Forbid("You can only access keys of your friends");
|
return Forbid("You can only access keys of your friends");
|
||||||
|
|
||||||
var keys = await _sessionKeyService.GetAllActiveKeysAsync(userId);
|
var keys = await _sessionKeysReader.GetAllActiveKeysAsync(userId);
|
||||||
|
|
||||||
return Ok(keys);
|
return Ok(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
[HttpPost("keys/rotate")]
|
||||||
|
public async Task<IActionResult> RotateOneTimePreKeys([FromBody] RotateOneTimePreKeysRequest request)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
if (request.NewOneTimePreKeys.Count > 100)
|
||||||
|
return BadRequest("Too many new one time pre keys");
|
||||||
|
|
||||||
|
var sessionId = _currentSession.GetUserSessionId();
|
||||||
|
|
||||||
|
await _oneTimePreKeysRotator.RotateOneTimePreKeysAsync(sessionId, request.NewOneTimePreKeys);
|
||||||
|
|
||||||
|
return Ok("One-Time PreKeys rotated successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("keys/remaining")]
|
||||||
|
public async Task<IActionResult> GetRemainingOneTimePreKeysCount()
|
||||||
|
{
|
||||||
|
var sessionId = _currentSession.GetUserSessionId();
|
||||||
|
|
||||||
|
var count = await _sessionKeysReader.GetRemainingOneTimePreKeysCountAsync(sessionId);
|
||||||
|
|
||||||
|
return Ok(new { remaining = count });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("keys/{preKeyId}/used")]
|
||||||
|
public async Task<IActionResult> MarkOneTimePreKeyAsUsed([FromRoute] Guid preKeyId)
|
||||||
|
{
|
||||||
|
var sessionId = _currentSession.GetUserSessionId();
|
||||||
|
|
||||||
|
await _oneTimePreKeysRotator.MarkOneTimePreKeyAsUsedAsync(sessionId, preKeyId);
|
||||||
|
|
||||||
|
return Ok("Marked as used.");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,5 +16,8 @@
|
|||||||
},
|
},
|
||||||
"JwtRefreshOption": {
|
"JwtRefreshOption": {
|
||||||
"RefreshTokenLifetimeDays": 30
|
"RefreshTokenLifetimeDays": 30
|
||||||
|
},
|
||||||
|
"EncryptionOption": {
|
||||||
|
"Secret": "4r8B2j9kP5mX7nQwE2zY3A=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
|
|
||||||
|
public interface IOneTimePreKeysRotator
|
||||||
|
{
|
||||||
|
Task RotateOneTimePreKeysAsync(Guid sessionId, IEnumerable<byte[]> newOneTimePreKeys);
|
||||||
|
|
||||||
|
Task MarkOneTimePreKeyAsUsedAsync(Guid sessionId, Guid oneTimePreKeyId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
|
||||||
|
namespace Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
|
|
||||||
|
public interface ISessionKeyAttacher
|
||||||
|
{
|
||||||
|
Task AttachKeysAsync(
|
||||||
|
Guid sessionId,
|
||||||
|
byte[] identityKey,
|
||||||
|
byte[] signedPreKey,
|
||||||
|
byte[] signedPreKeySignature,
|
||||||
|
IEnumerable<byte[]> oneTimePreKeys);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
|
||||||
|
namespace Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
|
|
||||||
|
public interface ISessionKeysReader
|
||||||
|
{
|
||||||
|
Task<bool> HasKeysAttachedAsync(Guid sessionId);
|
||||||
|
Task<IReadOnlyList<UserCryptoSession>> GetAllActiveKeysAsync(Guid userId);
|
||||||
|
Task<int> GetRemainingOneTimePreKeysCountAsync(Guid sessionId);
|
||||||
|
Task<UserCryptoSession?> GetKeysBySessionIdAsync(Guid sessionId);
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
namespace Govor.Application.Interfaces.UserSession;
|
|
||||||
|
|
||||||
public interface ISessionKeyService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Привязать публичные ключи текущего клиента к сессии.
|
|
||||||
/// </summary>
|
|
||||||
Task AttachKeysAsync(Guid userId, Guid sessionId, string publicEncryptionKey, string publicSigningKey);
|
|
||||||
|
|
||||||
Task<SessionPublicKeys?> GetKeysAsync(Guid userId, Guid sessionId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получить публичные ключи пользователя (например, последнюю активную сессию или все активные).
|
|
||||||
/// </summary>
|
|
||||||
Task<IEnumerable<SessionPublicKeys>> GetAllActiveKeysAsync(Guid userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SessionPublicKeys
|
|
||||||
{
|
|
||||||
public Guid UserId { get; set; }
|
|
||||||
public Guid SessionId { get; set; }
|
|
||||||
public string PublicEncryptionKey { get; set; } = string.Empty;
|
|
||||||
public string PublicSigningKey { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||||
|
using Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
using Govor.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Govor.Application.Services.UserSessions.Crypto;
|
||||||
|
|
||||||
|
public class OneTimePreKeysRotator : IOneTimePreKeysRotator
|
||||||
|
{
|
||||||
|
private readonly ILogger<SessionKeyAttacher> _logger;
|
||||||
|
private readonly ICurrentUserService _current;
|
||||||
|
private readonly GovorDbContext _context;
|
||||||
|
|
||||||
|
public OneTimePreKeysRotator(ILogger<SessionKeyAttacher> logger, ICurrentUserService current, GovorDbContext context)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_current = current;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RotateOneTimePreKeysAsync(Guid sessionId, IEnumerable<byte[]> newOneTimePreKeys)
|
||||||
|
{
|
||||||
|
var cryptoSession = await _context.UserCryptoSessions
|
||||||
|
.Include(c => c.OneTimePreKeys)
|
||||||
|
.FirstOrDefaultAsync(c => c.UserSessionId == sessionId);
|
||||||
|
|
||||||
|
if (cryptoSession == null)
|
||||||
|
throw new ArgumentException("Crypto session not found", nameof(sessionId));
|
||||||
|
|
||||||
|
// Удаляем все использованные ключи
|
||||||
|
var usedKeys = cryptoSession.OneTimePreKeys.Where(k => k.IsUsed).ToList();
|
||||||
|
if (usedKeys.Any())
|
||||||
|
{
|
||||||
|
_context.OneTimePreKeys.RemoveRange(usedKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем новые ключи (возможно, стоит ограничить количество)
|
||||||
|
foreach (var key in newOneTimePreKeys)
|
||||||
|
{
|
||||||
|
cryptoSession.OneTimePreKeys.Add(new OneTimePreKey
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
PublicKey = key,
|
||||||
|
IsUsed = false,
|
||||||
|
UploadedAt = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MarkOneTimePreKeyAsUsedAsync(Guid sessionId, Guid oneTimePreKeyId)
|
||||||
|
{
|
||||||
|
var key = await _context.OneTimePreKeys
|
||||||
|
.Include(k => k.UserCryptoSession)
|
||||||
|
.FirstOrDefaultAsync(k => k.Id == oneTimePreKeyId && k.UserCryptoSession.UserSessionId == sessionId);
|
||||||
|
|
||||||
|
if (key == null)
|
||||||
|
throw new ArgumentException("One-Time PreKey not found for this session", nameof(oneTimePreKeyId));
|
||||||
|
|
||||||
|
if (key.IsUsed)
|
||||||
|
return; // Уже помечен
|
||||||
|
|
||||||
|
key.IsUsed = true;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||||
|
using Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
using Govor.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Govor.Application.Services.UserSessions.Crypto;
|
||||||
|
|
||||||
|
public class SessionKeyAttacher : ISessionKeyAttacher
|
||||||
|
{
|
||||||
|
private readonly ILogger<SessionKeyAttacher> _logger;
|
||||||
|
private readonly ICurrentUserService _current;
|
||||||
|
private readonly GovorDbContext _context;
|
||||||
|
|
||||||
|
public SessionKeyAttacher(ILogger<SessionKeyAttacher> logger, ICurrentUserService current, GovorDbContext context)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_current = current;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AttachKeysAsync(
|
||||||
|
Guid sessionId,
|
||||||
|
byte[] identityKey,
|
||||||
|
byte[] signedPreKey,
|
||||||
|
byte[] signedPreKeySignature,
|
||||||
|
IEnumerable<byte[]> oneTimePreKeys)
|
||||||
|
{
|
||||||
|
var session = await _context.UserSessions
|
||||||
|
.Include(s => s.CryptoSession)
|
||||||
|
.ThenInclude(c => c.OneTimePreKeys)
|
||||||
|
.FirstOrDefaultAsync(s => s.Id == sessionId);
|
||||||
|
|
||||||
|
if (session == null)
|
||||||
|
throw new ArgumentException("Session not found", nameof(sessionId));
|
||||||
|
|
||||||
|
if (session.CryptoSession != null)
|
||||||
|
throw new InvalidOperationException("Keys are already attached to this session");
|
||||||
|
|
||||||
|
if(session.UserId != _current.GetCurrentUserId())
|
||||||
|
throw new InvalidOperationException("You cannot attach keys to this session");
|
||||||
|
|
||||||
|
var cryptoSessionId = Guid.NewGuid();
|
||||||
|
|
||||||
|
var cryptoSession = new UserCryptoSession
|
||||||
|
{
|
||||||
|
Id = cryptoSessionId,
|
||||||
|
UserSessionId = sessionId,
|
||||||
|
PublicIdentityKey = identityKey,
|
||||||
|
SignedPreKey = new SignedPreKey()
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
UserCryptoSessionId = cryptoSessionId,
|
||||||
|
PublicSignedPreKey = signedPreKey,
|
||||||
|
SignedPreKeySignature = signedPreKeySignature,
|
||||||
|
},
|
||||||
|
OneTimePreKeys = oneTimePreKeys.Select(key => new OneTimePreKey
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
PublicKey = key,
|
||||||
|
IsUsed = false,
|
||||||
|
UploadedAt = DateTime.UtcNow
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
session.CryptoSession = cryptoSession;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("Attached keys to session {SessionId}", sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||||
|
using Govor.Application.Interfaces.UserSession.Crypto;
|
||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
using Govor.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Govor.Application.Services.UserSessions.Crypto;
|
||||||
|
|
||||||
|
public class SessionKeysReader : ISessionKeysReader
|
||||||
|
{
|
||||||
|
private readonly ILogger<SessionKeyAttacher> _logger;
|
||||||
|
private readonly GovorDbContext _context;
|
||||||
|
|
||||||
|
public SessionKeysReader(ILogger<SessionKeyAttacher> logger, GovorDbContext context)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasKeysAttachedAsync(Guid sessionId)
|
||||||
|
{
|
||||||
|
return await _context.UserCryptoSessions.AnyAsync(c => c.UserSessionId == sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<UserCryptoSession>> GetAllActiveKeysAsync(Guid userId)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Getting all active keys for user {UserId}.", userId);
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
return await _context.UserCryptoSessions
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(c => c.OneTimePreKeys)
|
||||||
|
.Include(c => c.UserSession)
|
||||||
|
.Where(c => c.UserSession.UserId == userId
|
||||||
|
&& !c.UserSession.IsRevoked
|
||||||
|
&& c.UserSession.ExpiresAt > now)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> GetRemainingOneTimePreKeysCountAsync(Guid sessionId)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Getting count of one time pre keys for session {UserId}.", sessionId);
|
||||||
|
|
||||||
|
return await _context.OneTimePreKeys
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(f => f.UserCryptoSession)
|
||||||
|
.CountAsync(f => f.UserCryptoSession.UserSessionId == sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserCryptoSession?> GetKeysBySessionIdAsync(Guid sessionId)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Getting keys for session {session}.", sessionId);
|
||||||
|
|
||||||
|
return await _context.UserCryptoSessions
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(c => c.OneTimePreKeys)
|
||||||
|
.Include(c => c.UserSession)
|
||||||
|
.FirstOrDefaultAsync(c => c.UserSessionId == sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Govor.Contracts.DTOs;
|
||||||
|
|
||||||
|
public class OneTimePreKeyDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Key { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Govor.Contracts.DTOs;
|
||||||
|
|
||||||
|
public class PublicSessionKeysDto
|
||||||
|
{
|
||||||
|
public string IdentityKey { get; set; } = string.Empty; // base64
|
||||||
|
public SignedPreKeyDto SignedPreKey { get; set; }
|
||||||
|
public List<OneTimePreKeyDto> OneTimePreKeys { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Govor.Contracts.DTOs;
|
||||||
|
|
||||||
|
public class SignedPreKeyDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Key { get; set; } = string.Empty;
|
||||||
|
public string Signature { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Govor.Contracts.Requests;
|
||||||
|
|
||||||
|
public class RotateOneTimePreKeysRequest
|
||||||
|
{
|
||||||
|
public ICollection<byte[]> NewOneTimePreKeys { get; set; } = new List<byte[]>();
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ namespace Govor.Contracts.Requests;
|
|||||||
|
|
||||||
public class UploadKeysRequest
|
public class UploadKeysRequest
|
||||||
{
|
{
|
||||||
public string PublicEncryptionKey { get; set; }
|
public byte[] IdentityKey { get; set; }
|
||||||
public string PublicSigningKey { get; set; }
|
public byte[] SignedPreKey { get; set; }
|
||||||
|
public byte[] SignedPreKeySignature { get; set; }
|
||||||
|
public List<byte[]> OneTimePreKeys { get; set; } = new();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Govor.Core.Models.Users.Crypto;
|
||||||
|
|
||||||
|
public class OneTimePreKey
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid UserCryptoSessionId { get; set; }
|
||||||
|
public UserCryptoSession UserCryptoSession { get; set; }
|
||||||
|
|
||||||
|
public byte[] PublicKey { get; set; }
|
||||||
|
public bool IsUsed { get; set; }
|
||||||
|
public DateTime UploadedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Govor.Core.Models.Users.Crypto;
|
||||||
|
|
||||||
|
public class SignedPreKey
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid UserCryptoSessionId { get; set; }
|
||||||
|
public UserCryptoSession UserCryptoSession { get; set; }
|
||||||
|
public byte[] PublicSignedPreKey { get; set; }
|
||||||
|
public byte[] SignedPreKeySignature { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Govor.Core.Models.Users.Crypto;
|
||||||
|
|
||||||
|
public class UserCryptoSession
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
public Guid UserSessionId { get; set; }
|
||||||
|
public UserSession UserSession { get; set; }
|
||||||
|
|
||||||
|
public byte[] PublicIdentityKey { get; set; }
|
||||||
|
|
||||||
|
public SignedPreKey SignedPreKey { get; set; }
|
||||||
|
public ICollection<OneTimePreKey> OneTimePreKeys { get; set; }
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
|
||||||
namespace Govor.Core.Models.Users;
|
namespace Govor.Core.Models.Users;
|
||||||
|
|
||||||
public class UserSession
|
public class UserSession
|
||||||
@@ -6,12 +8,12 @@ public class UserSession
|
|||||||
public Guid UserId { get; set; }
|
public Guid UserId { get; set; }
|
||||||
public string RefreshToken { get; set; } = string.Empty;
|
public string RefreshToken { get; set; } = string.Empty;
|
||||||
public string DeviceInfo { get; set; } = string.Empty; // "Chrome on Windows"
|
public string DeviceInfo { get; set; } = string.Empty; // "Chrome on Windows"
|
||||||
public string PublicEncryptionKey { get; set; }
|
|
||||||
public string PublicSigningKey { get; set; }
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
public DateTime ExpiresAt { get; set; }
|
public DateTime ExpiresAt { get; set; }
|
||||||
public bool IsRevoked { get; set; } = false;
|
public bool IsRevoked { get; set; } = false;
|
||||||
|
|
||||||
|
public UserCryptoSession CryptoSession { get; set; }
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
UserSession? userSession = obj as UserSession;
|
UserSession? userSession = obj as UserSession;
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Govor.Data.Configurations;
|
||||||
|
|
||||||
|
public class OneTimePreKeyConfiguration : IEntityTypeConfiguration<OneTimePreKey>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<OneTimePreKey> builder)
|
||||||
|
{
|
||||||
|
// Первичный ключ
|
||||||
|
builder.HasKey(otpk => otpk.Id);
|
||||||
|
|
||||||
|
builder.HasOne(otpk => otpk.UserCryptoSession)
|
||||||
|
.WithMany(ucs => ucs.OneTimePreKeys)
|
||||||
|
.HasForeignKey(otpk => otpk.UserCryptoSessionId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.Property(otpk => otpk.PublicKey)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
builder.Property(otpk => otpk.IsUsed)
|
||||||
|
.IsRequired()
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
|
builder.Property(otpk => otpk.UploadedAt)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
builder.HasIndex(otpk => new { otpk.UserCryptoSessionId, otpk.IsUsed });
|
||||||
|
builder.HasIndex(otpk => otpk.UploadedAt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Govor.Data.Configurations;
|
||||||
|
|
||||||
|
public class SignedPreKeyConfiguration : IEntityTypeConfiguration<SignedPreKey>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<SignedPreKey> builder)
|
||||||
|
{
|
||||||
|
builder.HasKey(spk => spk.Id);
|
||||||
|
|
||||||
|
builder.HasOne(spk => spk.UserCryptoSession)
|
||||||
|
.WithOne(ucs => ucs.SignedPreKey)
|
||||||
|
.HasForeignKey<SignedPreKey>(spk => spk.UserCryptoSessionId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.Property(spk => spk.PublicSignedPreKey)
|
||||||
|
.IsRequired();
|
||||||
|
builder.Property(spk => spk.SignedPreKeySignature)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
builder.HasIndex(spk => spk.UserCryptoSessionId)
|
||||||
|
.IsUnique();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Govor.Data.Configurations;
|
||||||
|
|
||||||
|
public class UserCryptoSessionConfiguration : IEntityTypeConfiguration<UserCryptoSession>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<UserCryptoSession> builder)
|
||||||
|
{
|
||||||
|
builder.HasKey(ucs => ucs.Id);
|
||||||
|
|
||||||
|
builder.HasOne(ucs => ucs.UserSession)
|
||||||
|
.WithOne(us => us.CryptoSession)
|
||||||
|
.HasForeignKey<UserCryptoSession>(ucs => ucs.UserSessionId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.HasOne(ucs => ucs.SignedPreKey)
|
||||||
|
.WithOne(spk => spk.UserCryptoSession)
|
||||||
|
.HasForeignKey<SignedPreKey>(spk => spk.UserCryptoSessionId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.HasMany(ucs => ucs.OneTimePreKeys)
|
||||||
|
.WithOne(otpk => otpk.UserCryptoSession)
|
||||||
|
.HasForeignKey(otpk => otpk.UserCryptoSessionId)
|
||||||
|
.IsRequired()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.HasIndex(ucs => ucs.UserSessionId)
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
builder.Property(ucs => ucs.PublicIdentityKey)
|
||||||
|
.IsRequired();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Govor.Core.Models;
|
using Govor.Core.Models;
|
||||||
using Govor.Core.Models.Users;
|
using Govor.Core.Models.Users;
|
||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
@@ -15,11 +16,16 @@ public class UserSessionConfiguration : IEntityTypeConfiguration<UserSession>
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
builder.Property(us => us.DeviceInfo)
|
builder.Property(us => us.DeviceInfo)
|
||||||
.HasMaxLength(200);
|
.HasMaxLength(256);
|
||||||
|
|
||||||
builder.HasOne<User>()
|
builder.HasOne<User>()
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey(us => us.UserId)
|
.HasForeignKey(us => us.UserId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
builder.HasOne(e => e.CryptoSession)
|
||||||
|
.WithOne(e => e.UserSession)
|
||||||
|
.HasForeignKey<UserCryptoSession>(e => e.UserSessionId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Govor.Core.Models;
|
using Govor.Core.Models;
|
||||||
using Govor.Core.Models.Messages;
|
using Govor.Core.Models.Messages;
|
||||||
using Govor.Core.Models.Users;
|
using Govor.Core.Models.Users;
|
||||||
|
using Govor.Core.Models.Users.Crypto;
|
||||||
using Govor.Data.Configurations;
|
using Govor.Data.Configurations;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -10,6 +11,9 @@ public class GovorDbContext(DbContextOptions<GovorDbContext> options) : DbContex
|
|||||||
{
|
{
|
||||||
public virtual DbSet<User> Users { get; set; }
|
public virtual DbSet<User> Users { get; set; }
|
||||||
public virtual DbSet<UserSession> UserSessions { get; set; }
|
public virtual DbSet<UserSession> UserSessions { get; set; }
|
||||||
|
public virtual DbSet<UserCryptoSession> UserCryptoSessions { get; set; }
|
||||||
|
public virtual DbSet<SignedPreKey> SignedPreKeys { get; set; }
|
||||||
|
public virtual DbSet<OneTimePreKey> OneTimePreKeys { get; set; }
|
||||||
public virtual DbSet<Friendship> Friendships { get; set; }
|
public virtual DbSet<Friendship> Friendships { get; set; }
|
||||||
public virtual DbSet<PrivateChat> PrivateChats { get; set; }
|
public virtual DbSet<PrivateChat> PrivateChats { get; set; }
|
||||||
public virtual DbSet<Admin> Admins { get; set; }
|
public virtual DbSet<Admin> Admins { get; set; }
|
||||||
@@ -44,6 +48,9 @@ public class GovorDbContext(DbContextOptions<GovorDbContext> options) : DbContex
|
|||||||
modelBuilder.ApplyConfiguration(new GroupMembershipConfiguration());
|
modelBuilder.ApplyConfiguration(new GroupMembershipConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new GroupAdminsConfiguration());
|
modelBuilder.ApplyConfiguration(new GroupAdminsConfiguration());
|
||||||
|
|
||||||
|
modelBuilder.ApplyConfiguration(new OneTimePreKeyConfiguration());
|
||||||
|
modelBuilder.ApplyConfiguration(new UserCryptoSessionConfiguration());
|
||||||
|
modelBuilder.ApplyConfiguration(new SignedPreKeyConfiguration());
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,763 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Govor.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Govor.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(GovorDbContext))]
|
||||||
|
[Migration("20250730142221_CryptKeys")]
|
||||||
|
partial class CryptKeys
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.6")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.ChatGroup", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("varchar(500)");
|
||||||
|
|
||||||
|
b.Property<Guid>("ImageId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsChannel")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsPrivate")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ChatGroups");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("AddresseeId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RequesterId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AddresseeId");
|
||||||
|
|
||||||
|
b.HasIndex("RequesterId");
|
||||||
|
|
||||||
|
b.ToTable("Friendships");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("GroupId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GroupId");
|
||||||
|
|
||||||
|
b.ToTable("GroupAdmins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("varchar(500)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("EndDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("GroupId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("InvitationCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)");
|
||||||
|
|
||||||
|
b.Property<int>("MaxParticipants")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserMakerId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GroupId");
|
||||||
|
|
||||||
|
b.HasIndex("UserMakerId");
|
||||||
|
|
||||||
|
b.ToTable("GroupInvitations");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.GroupMembership", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("GroupId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("InvitationId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsBanned")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("MemberSince")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GroupId");
|
||||||
|
|
||||||
|
b.HasIndex("InvitationId");
|
||||||
|
|
||||||
|
b.ToTable("GroupMemberships");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Invitation", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateCreated")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("EndDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAdmin")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<int>("MaxParticipants")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Invitations");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.MediaFile", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateCreated")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("MineType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UploaderId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("MediaFiles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("MediaFileId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("MessageId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaFileId");
|
||||||
|
|
||||||
|
b.HasIndex("MessageId");
|
||||||
|
|
||||||
|
b.ToTable("MediaAttachments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.Message", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ChatGroupId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EditedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptedContent")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEdited")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("PrivateChatId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RecipientId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<int>("RecipientType")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<Guid?>("ReplyToMessageId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SenderId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("SentAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ChatGroupId");
|
||||||
|
|
||||||
|
b.HasIndex("PrivateChatId");
|
||||||
|
|
||||||
|
b.ToTable("Messages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("MessageId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReactedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("ReactionCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.HasIndex("MessageId", "UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("MessageReactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("MessageId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ViewedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MessageId", "UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("MessageViews");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserAId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserBId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PrivateChats");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Admins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.OneTimePreKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsUsed")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
|
b.Property<byte[]>("PublicKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UploadedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserCryptoSessionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UploadedAt");
|
||||||
|
|
||||||
|
b.HasIndex("UserCryptoSessionId", "IsUsed");
|
||||||
|
|
||||||
|
b.ToTable("OneTimePreKeys");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.SignedPreKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<byte[]>("PublicSignedPreKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<byte[]>("SignedPreKeySignature")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserCryptoSessionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserCryptoSessionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SignedPreKeys");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<byte[]>("PublicIdentityKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserSessionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserSessionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("UserCryptoSessions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("CreatedOn")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid>("IconId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("InviteId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("WasOnline")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InviteId");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("DeviceInfo")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsRevoked")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSessions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", "Addressee")
|
||||||
|
.WithMany("ReceivedFriendRequests")
|
||||||
|
.HasForeignKey("AddresseeId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", "Requester")
|
||||||
|
.WithMany("SentFriendRequests")
|
||||||
|
.HasForeignKey("RequesterId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Addressee");
|
||||||
|
|
||||||
|
b.Navigation("Requester");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.GroupAdmins", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.ChatGroup", null)
|
||||||
|
.WithMany("Admins")
|
||||||
|
.HasForeignKey("GroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.GroupInvitation", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.ChatGroup", null)
|
||||||
|
.WithMany("InviteCodes")
|
||||||
|
.HasForeignKey("GroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", "UserMaker")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserMakerId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserMaker");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.GroupMembership", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.ChatGroup", null)
|
||||||
|
.WithMany("Members")
|
||||||
|
.HasForeignKey("GroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Govor.Core.Models.GroupInvitation", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("InvitationId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.MediaFile", "MediaFile")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("MediaFileId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Govor.Core.Models.Messages.Message", "Message")
|
||||||
|
.WithMany("MediaAttachments")
|
||||||
|
.HasForeignKey("MessageId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("MediaFile");
|
||||||
|
|
||||||
|
b.Navigation("Message");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.Message", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.ChatGroup", null)
|
||||||
|
.WithMany("Messages")
|
||||||
|
.HasForeignKey("ChatGroupId");
|
||||||
|
|
||||||
|
b.HasOne("Govor.Core.Models.PrivateChat", null)
|
||||||
|
.WithMany("Messages")
|
||||||
|
.HasForeignKey("PrivateChatId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.MessageReaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Messages.Message", "Message")
|
||||||
|
.WithMany("Reactions")
|
||||||
|
.HasForeignKey("MessageId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Message");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.MessageView", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Messages.Message", null)
|
||||||
|
.WithMany("MessageViews")
|
||||||
|
.HasForeignKey("MessageId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", "User")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("Govor.Core.Models.Users.Admin", "UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.OneTimePreKey", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserCryptoSession")
|
||||||
|
.WithMany("OneTimePreKeys")
|
||||||
|
.HasForeignKey("UserCryptoSessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserCryptoSession");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.SignedPreKey", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserCryptoSession")
|
||||||
|
.WithOne("SignedPreKey")
|
||||||
|
.HasForeignKey("Govor.Core.Models.Users.Crypto.SignedPreKey", "UserCryptoSessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserCryptoSession");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.UserSession", "UserSession")
|
||||||
|
.WithOne("CryptoSession")
|
||||||
|
.HasForeignKey("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserSessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserSession");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Invitation", "Invite")
|
||||||
|
.WithMany("Users")
|
||||||
|
.HasForeignKey("InviteId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Invite");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.ChatGroup", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Admins");
|
||||||
|
|
||||||
|
b.Navigation("InviteCodes");
|
||||||
|
|
||||||
|
b.Navigation("Members");
|
||||||
|
|
||||||
|
b.Navigation("Messages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Invitation", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Messages.Message", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("MediaAttachments");
|
||||||
|
|
||||||
|
b.Navigation("MessageViews");
|
||||||
|
|
||||||
|
b.Navigation("Reactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.PrivateChat", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Messages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OneTimePreKeys");
|
||||||
|
|
||||||
|
b.Navigation("SignedPreKey")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("ReceivedFriendRequests");
|
||||||
|
|
||||||
|
b.Navigation("SentFriendRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CryptoSession")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Govor.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class CryptKeys : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "DeviceInfo",
|
||||||
|
table: "UserSessions",
|
||||||
|
type: "varchar(256)",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "varchar(200)",
|
||||||
|
oldMaxLength: 200)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4")
|
||||||
|
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "InvitationId",
|
||||||
|
table: "GroupMemberships",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: true,
|
||||||
|
collation: "ascii_general_ci",
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "char(36)")
|
||||||
|
.OldAnnotation("Relational:Collation", "ascii_general_ci");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "MemberSince",
|
||||||
|
table: "GroupMemberships",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserCryptoSessions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
UserSessionId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
PublicIdentityKey = table.Column<byte[]>(type: "longblob", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserCryptoSessions", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_UserCryptoSessions_UserSessions_UserSessionId",
|
||||||
|
column: x => x.UserSessionId,
|
||||||
|
principalTable: "UserSessions",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OneTimePreKeys",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
UserCryptoSessionId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
PublicKey = table.Column<byte[]>(type: "longblob", nullable: false),
|
||||||
|
IsUsed = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValue: false),
|
||||||
|
UploadedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OneTimePreKeys", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_OneTimePreKeys_UserCryptoSessions_UserCryptoSessionId",
|
||||||
|
column: x => x.UserCryptoSessionId,
|
||||||
|
principalTable: "UserCryptoSessions",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SignedPreKeys",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
UserCryptoSessionId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
PublicSignedPreKey = table.Column<byte[]>(type: "longblob", nullable: false),
|
||||||
|
SignedPreKeySignature = table.Column<byte[]>(type: "longblob", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SignedPreKeys", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_SignedPreKeys_UserCryptoSessions_UserCryptoSessionId",
|
||||||
|
column: x => x.UserCryptoSessionId,
|
||||||
|
principalTable: "UserCryptoSessions",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OneTimePreKeys_UploadedAt",
|
||||||
|
table: "OneTimePreKeys",
|
||||||
|
column: "UploadedAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OneTimePreKeys_UserCryptoSessionId_IsUsed",
|
||||||
|
table: "OneTimePreKeys",
|
||||||
|
columns: new[] { "UserCryptoSessionId", "IsUsed" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SignedPreKeys_UserCryptoSessionId",
|
||||||
|
table: "SignedPreKeys",
|
||||||
|
column: "UserCryptoSessionId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserCryptoSessions_UserSessionId",
|
||||||
|
table: "UserCryptoSessions",
|
||||||
|
column: "UserSessionId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OneTimePreKeys");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SignedPreKeys");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserCryptoSessions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MemberSince",
|
||||||
|
table: "GroupMemberships");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "DeviceInfo",
|
||||||
|
table: "UserSessions",
|
||||||
|
type: "varchar(200)",
|
||||||
|
maxLength: 200,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "varchar(256)",
|
||||||
|
oldMaxLength: 256)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4")
|
||||||
|
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<Guid>(
|
||||||
|
name: "InvitationId",
|
||||||
|
table: "GroupMemberships",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
collation: "ascii_general_ci",
|
||||||
|
oldClrType: typeof(Guid),
|
||||||
|
oldType: "char(36)",
|
||||||
|
oldNullable: true)
|
||||||
|
.OldAnnotation("Relational:Collation", "ascii_general_ci");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,12 +145,14 @@ namespace Govor.Data.Migrations
|
|||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
b.Property<Guid?>("InvitationId")
|
b.Property<Guid?>("InvitationId")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
b.Property<bool>("IsBanned")
|
b.Property<bool>("IsBanned")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("MemberSince")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
b.Property<Guid>("UserId")
|
b.Property<Guid>("UserId")
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
@@ -364,40 +366,6 @@ namespace Govor.Data.Migrations
|
|||||||
b.ToTable("PrivateChats");
|
b.ToTable("PrivateChats");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.UserSession", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("char(36)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("DeviceInfo")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsRevoked")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("RefreshToken")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<Guid>("UserId")
|
|
||||||
.HasColumnType("char(36)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("UserSessions");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("UserId")
|
b.Property<Guid>("UserId")
|
||||||
@@ -408,6 +376,82 @@ namespace Govor.Data.Migrations
|
|||||||
b.ToTable("Admins");
|
b.ToTable("Admins");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.OneTimePreKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsUsed")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
|
b.Property<byte[]>("PublicKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UploadedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserCryptoSessionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UploadedAt");
|
||||||
|
|
||||||
|
b.HasIndex("UserCryptoSessionId", "IsUsed");
|
||||||
|
|
||||||
|
b.ToTable("OneTimePreKeys");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.SignedPreKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<byte[]>("PublicSignedPreKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<byte[]>("SignedPreKeySignature")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserCryptoSessionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserCryptoSessionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SignedPreKeys");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<byte[]>("PublicIdentityKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserSessionId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserSessionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("UserCryptoSessions");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -445,6 +489,40 @@ namespace Govor.Data.Migrations
|
|||||||
b.ToTable("Users");
|
b.ToTable("Users");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("DeviceInfo")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsRevoked")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSessions");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
|
modelBuilder.Entity("Govor.Core.Models.Friendship", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Govor.Core.Models.Users.User", "Addressee")
|
b.HasOne("Govor.Core.Models.Users.User", "Addressee")
|
||||||
@@ -501,8 +579,7 @@ namespace Govor.Data.Migrations
|
|||||||
b.HasOne("Govor.Core.Models.GroupInvitation", null)
|
b.HasOne("Govor.Core.Models.GroupInvitation", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("InvitationId")
|
.HasForeignKey("InvitationId")
|
||||||
.OnDelete(DeleteBehavior.SetNull)
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
.IsRequired();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b =>
|
modelBuilder.Entity("Govor.Core.Models.Messages.MediaAttachments", b =>
|
||||||
@@ -563,15 +640,6 @@ namespace Govor.Data.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.UserSession", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Govor.Core.Models.Users.User", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
modelBuilder.Entity("Govor.Core.Models.Users.Admin", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Govor.Core.Models.Users.User", "User")
|
b.HasOne("Govor.Core.Models.Users.User", "User")
|
||||||
@@ -583,6 +651,39 @@ namespace Govor.Data.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.OneTimePreKey", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserCryptoSession")
|
||||||
|
.WithMany("OneTimePreKeys")
|
||||||
|
.HasForeignKey("UserCryptoSessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserCryptoSession");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.SignedPreKey", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserCryptoSession")
|
||||||
|
.WithOne("SignedPreKey")
|
||||||
|
.HasForeignKey("Govor.Core.Models.Users.Crypto.SignedPreKey", "UserCryptoSessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserCryptoSession");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.UserSession", "UserSession")
|
||||||
|
.WithOne("CryptoSession")
|
||||||
|
.HasForeignKey("Govor.Core.Models.Users.Crypto.UserCryptoSession", "UserSessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("UserSession");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Govor.Core.Models.Invitation", "Invite")
|
b.HasOne("Govor.Core.Models.Invitation", "Invite")
|
||||||
@@ -594,6 +695,15 @@ namespace Govor.Data.Migrations
|
|||||||
b.Navigation("Invite");
|
b.Navigation("Invite");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Govor.Core.Models.Users.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.ChatGroup", b =>
|
modelBuilder.Entity("Govor.Core.Models.ChatGroup", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Admins");
|
b.Navigation("Admins");
|
||||||
@@ -624,12 +734,26 @@ namespace Govor.Data.Migrations
|
|||||||
b.Navigation("Messages");
|
b.Navigation("Messages");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.Crypto.UserCryptoSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OneTimePreKeys");
|
||||||
|
|
||||||
|
b.Navigation("SignedPreKey")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
modelBuilder.Entity("Govor.Core.Models.Users.User", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("ReceivedFriendRequests");
|
b.Navigation("ReceivedFriendRequests");
|
||||||
|
|
||||||
b.Navigation("SentFriendRequests");
|
b.Navigation("SentFriendRequests");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Govor.Core.Models.Users.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CryptoSession")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user