Introduces accounts (ASP.NET Identity + cookie auth), four global roles (Admin/Writer/Editor/Reviewer), per-novel ownership and grants via ProjectMember, and a service-API-key principal for the MCP server and background import jobs. Enforcement lives in the application services (not endpoint filters) so the embedded agent and MCP tools, which call the same services directly, can't bypass it. Web client gets a login page, session-aware routing, and a People section for managing per-novel access. Also includes prior in-flight changes from this branch (CLAUDE.md compliance pass, dev-deploy docker-compose setup) that were uncommitted when this feature work started.
56 lines
1.8 KiB
C#
56 lines
1.8 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
using Novelly.Api.Projects;
|
|
|
|
namespace Novelly.Api.Agent;
|
|
|
|
public class AgentConversation
|
|
{
|
|
public Guid Id { get; init; } = Guid.NewGuid();
|
|
public Guid ProjectId { get; init; }
|
|
public Project? Project { get; init; }
|
|
|
|
public string Title { get; init; } = "New conversation";
|
|
|
|
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
|
|
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
|
|
|
|
public List<AgentMessage> Messages { get; init; } = [];
|
|
}
|
|
|
|
public class AgentMessage
|
|
{
|
|
public Guid Id { get; init; } = Guid.NewGuid();
|
|
public Guid ConversationId { get; init; }
|
|
public AgentConversation? Conversation { get; init; }
|
|
|
|
public AgentRole Role { get; init; }
|
|
|
|
public int Sequence { get; init; }
|
|
|
|
public string Content { get; init; } = string.Empty;
|
|
|
|
public string? ToolCallsJson { get; init; }
|
|
|
|
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
|
|
}
|
|
|
|
public class AgentConversationEntityTypeConfiguration : IEntityTypeConfiguration<AgentConversation>
|
|
{
|
|
public void Configure(EntityTypeBuilder<AgentConversation> entity)
|
|
{
|
|
entity.Property(c => c.Title).IsRequired().HasMaxLength(200);
|
|
entity.HasMany(c => c.Messages).WithOne(m => m.Conversation!)
|
|
.HasForeignKey(m => m.ConversationId).OnDelete(DeleteBehavior.Cascade);
|
|
}
|
|
}
|
|
|
|
public class AgentMessageEntityTypeConfiguration : IEntityTypeConfiguration<AgentMessage>
|
|
{
|
|
public void Configure(EntityTypeBuilder<AgentMessage> entity)
|
|
{
|
|
entity.Property(m => m.Role).HasConversion<string>().HasMaxLength(16);
|
|
entity.HasIndex(m => new { m.ConversationId, m.Sequence }).IsUnique();
|
|
}
|
|
}
|