mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 00:26:07 +02:00
- Added EncryptionSalt and WrappedRoomKey properties to Channel model. - Introduced RoomCrypto class for client-side encryption and decryption. - Updated ChannelService to handle encrypted channels, including creation and rekeying. - Modified ChannelsController to expose crypto metadata and rekey functionality. - Enhanced IrcCommandHandler to block joining encrypted channels over IRC. - Updated database schema with migration for new encryption fields. - Refactored file validation and image processing services to accommodate encrypted channels. - Added unit tests for RoomCrypto functionality and updated existing tests for channel services.
116 lines
4.7 KiB
C#
116 lines
4.7 KiB
C#
using EchoHub.Core.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
|
|
namespace EchoHub.Server.Data;
|
|
|
|
public class EchoHubDbContext : DbContext
|
|
{
|
|
public EchoHubDbContext(DbContextOptions<EchoHubDbContext> options) : base(options) { }
|
|
public DbSet<User> Users => Set<User>();
|
|
public DbSet<Channel> Channels => Set<Channel>();
|
|
public DbSet<Message> Messages => Set<Message>();
|
|
public DbSet<RefreshToken> RefreshTokens => Set<RefreshToken>();
|
|
public DbSet<ChannelMembership> ChannelMemberships => Set<ChannelMembership>();
|
|
|
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
|
{
|
|
if (!optionsBuilder.IsConfigured)
|
|
{
|
|
var dbPath = Path.Combine(AppContext.BaseDirectory, "echohub.db");
|
|
optionsBuilder.UseSqlite($"Data Source={dbPath}");
|
|
}
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<User>(entity =>
|
|
{
|
|
entity.HasKey(u => u.Id);
|
|
entity.HasIndex(u => u.Username).IsUnique();
|
|
entity.Property(u => u.Username).IsRequired().HasMaxLength(50);
|
|
entity.Property(u => u.PasswordHash).IsRequired();
|
|
entity.Property(u => u.DisplayName).HasMaxLength(100);
|
|
entity.Property(u => u.Bio).HasMaxLength(500);
|
|
entity.Property(u => u.NicknameColor).HasMaxLength(7);
|
|
entity.Property(u => u.AvatarAscii).HasMaxLength(10000);
|
|
entity.Property(u => u.StatusMessage).HasMaxLength(100);
|
|
entity.Property(u => u.Role).HasConversion<int>();
|
|
});
|
|
|
|
modelBuilder.Entity<Channel>(entity =>
|
|
{
|
|
entity.HasKey(c => c.Id);
|
|
entity.HasIndex(c => c.Name).IsUnique();
|
|
entity.Property(c => c.Name).IsRequired().HasMaxLength(100);
|
|
entity.Property(c => c.Topic).HasMaxLength(500);
|
|
entity.Property(c => c.PasswordHash).HasMaxLength(100);
|
|
entity.Property(c => c.EncryptionSalt).HasMaxLength(64);
|
|
entity.Property(c => c.WrappedRoomKey).HasMaxLength(200);
|
|
|
|
entity.HasMany(c => c.Messages)
|
|
.WithOne(m => m.Channel)
|
|
.HasForeignKey(m => m.ChannelId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
modelBuilder.Entity<Message>(entity =>
|
|
{
|
|
entity.HasKey(m => m.Id);
|
|
entity.HasIndex(m => m.SentAt);
|
|
entity.Property(m => m.Content).IsRequired().HasMaxLength(16000); // Increased for encrypted content (Base64 overhead)
|
|
entity.Property(m => m.SenderUsername).IsRequired().HasMaxLength(50);
|
|
entity.Property(m => m.AttachmentUrl).HasMaxLength(500);
|
|
entity.Property(m => m.AttachmentFileName).HasMaxLength(255);
|
|
entity.Property(m => m.EmbedJson).HasMaxLength(32000); // Increased for encrypted embed JSON
|
|
});
|
|
|
|
modelBuilder.Entity<ChannelMembership>(entity =>
|
|
{
|
|
entity.HasKey(cm => new { cm.UserId, cm.ChannelId });
|
|
entity.HasIndex(cm => cm.UserId);
|
|
entity.HasIndex(cm => cm.ChannelId);
|
|
|
|
entity.HasOne<Channel>()
|
|
.WithMany()
|
|
.HasForeignKey(cm => cm.ChannelId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity.HasOne<User>()
|
|
.WithMany()
|
|
.HasForeignKey(cm => cm.UserId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
modelBuilder.Entity<RefreshToken>(entity =>
|
|
{
|
|
entity.HasKey(r => r.Id);
|
|
entity.HasIndex(r => r.TokenHash);
|
|
entity.HasIndex(r => r.UserId);
|
|
entity.Property(r => r.TokenHash).IsRequired().HasMaxLength(128);
|
|
|
|
entity.HasOne(r => r.User)
|
|
.WithMany()
|
|
.HasForeignKey(r => r.UserId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
});
|
|
|
|
// SQLite does not support DateTimeOffset in ORDER BY clauses.
|
|
// Convert all DateTimeOffset properties to Unix milliseconds (long) for storage.
|
|
var dateTimeOffsetConverter = new ValueConverter<DateTimeOffset, long>(
|
|
v => v.ToUnixTimeMilliseconds(),
|
|
v => DateTimeOffset.FromUnixTimeMilliseconds(v));
|
|
|
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
|
{
|
|
foreach (var property in entityType.GetProperties())
|
|
{
|
|
if (property.ClrType == typeof(DateTimeOffset) || property.ClrType == typeof(DateTimeOffset?))
|
|
{
|
|
property.SetValueConverter(dateTimeOffsetConverter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|