Reorganise by feature, rename to Novelly, add Aspire and a pre-push hook
The layered split into Domain/Application/Infrastructure/Api was forcing organisation by layer: adding one capability meant touching four projects and four folders that each held a slice of it. Those four projects are now one feature-organised Novelly.Api, where each folder — Projects, Characters, Chapters, Beats, Scenes, Tags, Agent — holds its entity, DTOs, service and endpoints together. Common/ holds what genuinely crosses features (the patch semantics, the two exception types, DraftStatus) and Data/ holds the DbContext and migrations. Six .NET projects become five: the three layer projects are gone, and Novelly.AppHost and Novelly.ServiceDefaults are new. - Namespaces move from NovelSoftware.* to Novelly.*, including the entity type names recorded in the EF model snapshots. The migration ids are untouched, so an existing novel.db still migrates cleanly — verified against a fresh file. - Aspire orchestration mirrors the mic-check setup: the AppHost starts the API on :5080 and the Vite dev server on :5173, and the API picks up OpenTelemetry, health checks and service discovery from ServiceDefaults. /health and /alive now answer in development. - A Husky pre-push hook runs scripts/ci/prepush.sh: build, test, then a web build. The scripts are plain bash so CI can run the same steps. - The MCP server's env var is now NOVELLY_API_URL. Verified beyond the build: 44 tests pass, the web client builds, the API was exercised over curl (project/chapter/beat/tag round trip, tag cross-reference, 503 on the agent without a key while conversation listing still returns 200), the MCP server was driven over stdio JSON-RPC (26 tools, errors still surface the API's own message rather than being flattened), and the AppHost was run to confirm both resources come up and Vite proxies /api through to the API. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S56bfZMGe1hnhpWP4CjjNw
This commit is contained in:
co-authored by
Claude Opus 5
parent
30e0c6926e
commit
725758ccd9
@@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NovelSoftware.Infrastructure\NovelSoftware.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.10">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.OpenApi" Version="2.11.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NovelSoftware.Domain\NovelSoftware.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,31 +0,0 @@
|
||||
namespace NovelSoftware.Domain;
|
||||
|
||||
/// <summary>The role a character plays in the story.</summary>
|
||||
public enum CharacterRole
|
||||
{
|
||||
Protagonist,
|
||||
Antagonist,
|
||||
Deuteragonist,
|
||||
Supporting,
|
||||
Minor,
|
||||
Mentor,
|
||||
LoveInterest,
|
||||
Foil
|
||||
}
|
||||
|
||||
/// <summary>How far along a chapter or scene is in the drafting pipeline.</summary>
|
||||
public enum DraftStatus
|
||||
{
|
||||
Planned,
|
||||
Outlined,
|
||||
Drafted,
|
||||
Revised,
|
||||
Final
|
||||
}
|
||||
|
||||
/// <summary>Who produced a message in an agent conversation.</summary>
|
||||
public enum AgentRole
|
||||
{
|
||||
User,
|
||||
Assistant
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NovelSoftware.Application.Agent;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>A tool the model may call, described in the shape the Messages API expects.</summary>
|
||||
public record AgentToolDefinition(string Name, string Description, JsonElement InputSchema);
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Projects;
|
||||
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>A chat thread between the writer and the embedded agent, scoped to one project.</summary>
|
||||
public class AgentConversation
|
||||
+1
-3
@@ -1,6 +1,4 @@
|
||||
using NovelSoftware.Domain;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
public record ConversationSummaryDto(
|
||||
Guid Id,
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Agent;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
public static class AgentEndpoints
|
||||
{
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>Who produced a message in an agent conversation.</summary>
|
||||
public enum AgentRole
|
||||
{
|
||||
User,
|
||||
Assistant
|
||||
}
|
||||
+3
-4
@@ -1,11 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using Anthropic;
|
||||
using Anthropic.Models.Messages;
|
||||
using Anthropic;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NovelSoftware.Application;
|
||||
using NovelSoftware.Application.Agent;
|
||||
using Novelly.Api.Common;
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Anthropic;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>
|
||||
/// Talks to the Anthropic Messages API. Translates between the application's
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NovelSoftware.Application.Agent;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>
|
||||
/// Small builder for the JSON Schema objects tool definitions need. Hand-writing these
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Projects;
|
||||
|
||||
namespace NovelSoftware.Application.Agent;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>
|
||||
/// The embedded writing agent. Runs the tool-use loop against the model, persists the
|
||||
+8
-4
@@ -1,9 +1,13 @@
|
||||
using System.Text.Json;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
using NovelSoftware.Domain;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Agent;
|
||||
namespace Novelly.Api.Agent;
|
||||
|
||||
/// <summary>The outcome of running a tool: what to hand back to the model, and whether it failed.</summary>
|
||||
public record AgentToolResult(string Content, bool IsError);
|
||||
@@ -1,4 +1,9 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace Novelly.Api.Beats;
|
||||
|
||||
/// <summary>
|
||||
/// One row of a chapter's outline: a short label, who it belongs to, what happened, and
|
||||
@@ -1,6 +1,6 @@
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Beats;
|
||||
|
||||
public record BeatDto(
|
||||
Guid Id,
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Beats;
|
||||
|
||||
public static class BeatEndpoints
|
||||
{
|
||||
+5
-3
@@ -1,8 +1,10 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Services;
|
||||
namespace Novelly.Api.Beats;
|
||||
|
||||
/// <summary>
|
||||
/// Beats are a chapter's outline: a flat, ordered table rather than a tree. Everything
|
||||
@@ -1,4 +1,11 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace Novelly.Api.Chapters;
|
||||
|
||||
/// <summary>A chapter: an ordered container of scenes plus its own planning fields.</summary>
|
||||
public class Chapter
|
||||
+5
-3
@@ -1,7 +1,9 @@
|
||||
using NovelSoftware.Domain;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Chapters;
|
||||
|
||||
public record ChapterSummaryDto(
|
||||
Guid Id,
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Chapters;
|
||||
|
||||
public static class ChapterEndpoints
|
||||
{
|
||||
+5
-3
@@ -1,8 +1,10 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Services;
|
||||
namespace Novelly.Api.Chapters;
|
||||
|
||||
public class ChapterService(INovelDbContext db, TagService tags)
|
||||
{
|
||||
+4
-1
@@ -1,4 +1,7 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace Novelly.Api.Characters;
|
||||
|
||||
/// <summary>
|
||||
/// A character dossier. Every field beyond <see cref="Name"/> is optional so a writer can
|
||||
+2
-3
@@ -1,7 +1,6 @@
|
||||
using NovelSoftware.Domain;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Characters;
|
||||
|
||||
public record CharacterDto(
|
||||
Guid Id,
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Characters;
|
||||
|
||||
public static class CharacterEndpoints
|
||||
{
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Novelly.Api.Characters;
|
||||
|
||||
/// <summary>The role a character plays in the story.</summary>
|
||||
public enum CharacterRole
|
||||
{
|
||||
Protagonist,
|
||||
Antagonist,
|
||||
Deuteragonist,
|
||||
Supporting,
|
||||
Minor,
|
||||
Mentor,
|
||||
LoveInterest,
|
||||
Foil
|
||||
}
|
||||
+5
-3
@@ -1,8 +1,10 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application.Services;
|
||||
namespace Novelly.Api.Characters;
|
||||
|
||||
public class CharacterService(INovelDbContext db, TagService tags)
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace NovelSoftware.Application;
|
||||
namespace Novelly.Api.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Thrown when the agent is asked to run but has no model credentials. This is a
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Novelly.Api.Common;
|
||||
|
||||
/// <summary>How far along a chapter or scene is in the drafting pipeline.</summary>
|
||||
public enum DraftStatus
|
||||
{
|
||||
Planned,
|
||||
Outlined,
|
||||
Drafted,
|
||||
Revised,
|
||||
Final
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace NovelSoftware.Application;
|
||||
namespace Novelly.Api.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Thrown when a service is asked for an entity that does not exist. The API translates
|
||||
+16
-10
@@ -1,17 +1,23 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NovelSoftware.Application;
|
||||
using NovelSoftware.Application.Agent;
|
||||
using NovelSoftware.Application.Services;
|
||||
using NovelSoftware.Infrastructure.Anthropic;
|
||||
using NovelSoftware.Infrastructure.Persistence;
|
||||
using Novelly.Api.Agent;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Infrastructure;
|
||||
namespace Novelly.Api.Common;
|
||||
|
||||
public static class DependencyInjection
|
||||
/// <summary>
|
||||
/// Wires up every feature's services in one place. Endpoints, the embedded agent and the
|
||||
/// MCP server all resolve the same instances, so a capability added here is available to
|
||||
/// all three.
|
||||
/// </summary>
|
||||
public static class NovellyServiceRegistration
|
||||
{
|
||||
public static IServiceCollection AddNovelSoftware(this IServiceCollection services, IConfiguration configuration)
|
||||
public static IServiceCollection AddNovelly(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var connectionString = configuration.GetConnectionString("Novel")
|
||||
?? "Data Source=novel.db";
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Novelly.Api.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Patch semantics shared by every update endpoint: a null value leaves the field
|
||||
/// untouched, an empty string clears it.
|
||||
/// </summary>
|
||||
internal static class Patch
|
||||
{
|
||||
public static string? Apply(string? current, string? incoming) => incoming switch
|
||||
{
|
||||
null => current,
|
||||
"" => null,
|
||||
_ => incoming
|
||||
};
|
||||
}
|
||||
+8
-2
@@ -1,7 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Agent;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Application;
|
||||
namespace Novelly.Api.Data;
|
||||
|
||||
/// <summary>
|
||||
/// The persistence surface the application services depend on. Infrastructure supplies
|
||||
+34
-34
@@ -2,13 +2,13 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Novelly.Api.Data;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NovelSoftware.Infrastructure.Persistence;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
namespace Novelly.Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelDbContext))]
|
||||
[Migration("20260806023249_InitialSchema")]
|
||||
@@ -20,7 +20,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.10");
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -47,7 +47,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Conversations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentMessage", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentMessage", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -82,7 +82,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("AgentMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -134,7 +134,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Chapters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -205,7 +205,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.CharacterRelationship", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.CharacterRelationship", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -234,7 +234,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("CharacterRelationships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.OutlineNode", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.OutlineNode", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -282,7 +282,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("OutlineNodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Project", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Projects.Project", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -322,7 +322,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Scene", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Scenes.Scene", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -383,9 +383,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Scenes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Conversations")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -394,9 +394,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentMessage", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentMessage", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.AgentConversation", "Conversation")
|
||||
b.HasOne("Novelly.Api.Agent.AgentConversation", "Conversation")
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("ConversationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -405,14 +405,14 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Conversation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "PovCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "PovCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("PovCharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -423,9 +423,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -434,15 +434,15 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.CharacterRelationship", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.CharacterRelationship", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "Character")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "Character")
|
||||
.WithMany("Relationships")
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "RelatedCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "RelatedCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("RelatedCharacterId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
@@ -453,19 +453,19 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("RelatedCharacter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.OutlineNode", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.OutlineNode", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", "Chapter")
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", "Chapter")
|
||||
.WithMany()
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.OutlineNode", "Parent")
|
||||
b.HasOne("Novelly.Api.Chapters.OutlineNode", "Parent")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("ParentId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("OutlineNodes")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -478,15 +478,15 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Scene", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Scenes.Scene", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", "Chapter")
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", "Chapter")
|
||||
.WithMany("Scenes")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "PovCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "PovCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("PovCharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
@@ -496,27 +496,27 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("PovCharacter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.Navigation("Scenes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.Navigation("Relationships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.OutlineNode", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.OutlineNode", b =>
|
||||
{
|
||||
b.Navigation("Children");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Project", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Projects.Project", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
namespace Novelly.Api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialSchema : Migration
|
||||
+42
-42
@@ -2,13 +2,13 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Novelly.Api.Data;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NovelSoftware.Infrastructure.Persistence;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
namespace Novelly.Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelDbContext))]
|
||||
[Migration("20260806031243_ReplaceOutlineWithBeatsAndTags")]
|
||||
@@ -65,7 +65,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("CharacterTags", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -92,7 +92,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Conversations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentMessage", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentMessage", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -127,7 +127,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("AgentMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Beat", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Beats.Beat", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -173,7 +173,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Beats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -225,7 +225,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Chapters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -296,7 +296,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.CharacterRelationship", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.CharacterRelationship", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -325,7 +325,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("CharacterRelationships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Project", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Projects.Project", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -365,7 +365,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Scene", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Scenes.Scene", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -426,7 +426,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Scenes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Tag", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Tags.Tag", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -457,13 +457,13 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
|
||||
modelBuilder.Entity("BeatTag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Beat", null)
|
||||
b.HasOne("Novelly.Api.Beats.Beat", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("BeatsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Tag", null)
|
||||
b.HasOne("Novelly.Api.Tags.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -472,13 +472,13 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
|
||||
modelBuilder.Entity("ChapterTag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", null)
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ChaptersId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Tag", null)
|
||||
b.HasOne("Novelly.Api.Tags.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -487,22 +487,22 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
|
||||
modelBuilder.Entity("CharacterTag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", null)
|
||||
b.HasOne("Novelly.Api.Characters.Character", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("CharactersId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Tag", null)
|
||||
b.HasOne("Novelly.Api.Tags.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Conversations")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -511,9 +511,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentMessage", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentMessage", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.AgentConversation", "Conversation")
|
||||
b.HasOne("Novelly.Api.Agent.AgentConversation", "Conversation")
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("ConversationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -522,20 +522,20 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Conversation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Beat", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Beats.Beat", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", "Chapter")
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", "Chapter")
|
||||
.WithMany("Beats")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "Character")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "Character")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Scene", "Scene")
|
||||
b.HasOne("Novelly.Api.Scenes.Scene", "Scene")
|
||||
.WithMany()
|
||||
.HasForeignKey("SceneId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
@@ -547,14 +547,14 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Scene");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "PovCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "PovCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("PovCharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -565,9 +565,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -576,15 +576,15 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.CharacterRelationship", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.CharacterRelationship", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "Character")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "Character")
|
||||
.WithMany("Relationships")
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "RelatedCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "RelatedCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("RelatedCharacterId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
@@ -595,15 +595,15 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("RelatedCharacter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Scene", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Scenes.Scene", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", "Chapter")
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", "Chapter")
|
||||
.WithMany("Scenes")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "PovCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "PovCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("PovCharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
@@ -613,9 +613,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("PovCharacter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Tag", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Tags.Tag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -624,24 +624,24 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.Navigation("Beats");
|
||||
|
||||
b.Navigation("Scenes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.Navigation("Relationships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Project", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Projects.Project", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
namespace Novelly.Api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ReplaceOutlineWithBeatsAndTags : Migration
|
||||
+42
-42
@@ -2,12 +2,12 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Novelly.Api.Data;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NovelSoftware.Infrastructure.Persistence;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
namespace Novelly.Api.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelDbContext))]
|
||||
partial class NovelDbContextModelSnapshot : ModelSnapshot
|
||||
@@ -62,7 +62,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("CharacterTags", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -89,7 +89,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Conversations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentMessage", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentMessage", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -124,7 +124,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("AgentMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Beat", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Beats.Beat", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -170,7 +170,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Beats");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -222,7 +222,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Chapters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -293,7 +293,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.CharacterRelationship", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.CharacterRelationship", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -322,7 +322,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("CharacterRelationships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Project", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Projects.Project", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -362,7 +362,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Projects");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Scene", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Scenes.Scene", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -423,7 +423,7 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.ToTable("Scenes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Tag", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Tags.Tag", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -454,13 +454,13 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
|
||||
modelBuilder.Entity("BeatTag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Beat", null)
|
||||
b.HasOne("Novelly.Api.Beats.Beat", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("BeatsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Tag", null)
|
||||
b.HasOne("Novelly.Api.Tags.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -469,13 +469,13 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
|
||||
modelBuilder.Entity("ChapterTag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", null)
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ChaptersId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Tag", null)
|
||||
b.HasOne("Novelly.Api.Tags.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -484,22 +484,22 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
|
||||
modelBuilder.Entity("CharacterTag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", null)
|
||||
b.HasOne("Novelly.Api.Characters.Character", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("CharactersId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Tag", null)
|
||||
b.HasOne("Novelly.Api.Tags.Tag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Conversations")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -508,9 +508,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentMessage", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentMessage", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.AgentConversation", "Conversation")
|
||||
b.HasOne("Novelly.Api.Agent.AgentConversation", "Conversation")
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("ConversationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -519,20 +519,20 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Conversation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Beat", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Beats.Beat", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", "Chapter")
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", "Chapter")
|
||||
.WithMany("Beats")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "Character")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "Character")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Scene", "Scene")
|
||||
b.HasOne("Novelly.Api.Scenes.Scene", "Scene")
|
||||
.WithMany()
|
||||
.HasForeignKey("SceneId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
@@ -544,14 +544,14 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Scene");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "PovCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "PovCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("PovCharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -562,9 +562,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -573,15 +573,15 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.CharacterRelationship", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.CharacterRelationship", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "Character")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "Character")
|
||||
.WithMany("Relationships")
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "RelatedCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "RelatedCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("RelatedCharacterId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
@@ -592,15 +592,15 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("RelatedCharacter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Scene", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Scenes.Scene", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Chapter", "Chapter")
|
||||
b.HasOne("Novelly.Api.Chapters.Chapter", "Chapter")
|
||||
.WithMany("Scenes")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Character", "PovCharacter")
|
||||
b.HasOne("Novelly.Api.Characters.Character", "PovCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("PovCharacterId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
@@ -610,9 +610,9 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("PovCharacter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Tag", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Tags.Tag", b =>
|
||||
{
|
||||
b.HasOne("NovelSoftware.Domain.Entities.Project", "Project")
|
||||
b.HasOne("Novelly.Api.Projects.Project", "Project")
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -621,24 +621,24 @@ namespace NovelSoftware.Infrastructure.Persistence.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.AgentConversation", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Agent.AgentConversation", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Chapter", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Chapters.Chapter", b =>
|
||||
{
|
||||
b.Navigation("Beats");
|
||||
|
||||
b.Navigation("Scenes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Character", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Characters.Character", b =>
|
||||
{
|
||||
b.Navigation("Relationships");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelSoftware.Domain.Entities.Project", b =>
|
||||
modelBuilder.Entity("Novelly.Api.Projects.Project", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
|
||||
+9
-4
@@ -1,9 +1,14 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NovelSoftware.Application;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Novelly.Api.Agent;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace NovelSoftware.Infrastructure.Persistence;
|
||||
namespace Novelly.Api.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a <see cref="DateTimeOffset"/> as UTC ticks. SQLite has no native type for it
|
||||
+5
-4
@@ -1,25 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NovelSoftware.Application\NovelSoftware.Application.csproj" />
|
||||
<ProjectReference Include="..\Novelly.ServiceDefaults\Novelly.ServiceDefaults.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Anthropic" Version="12.39.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.10">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.OpenApi" Version="2.11.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,14 +1,20 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Api.Endpoints;
|
||||
using NovelSoftware.Application;
|
||||
using NovelSoftware.Infrastructure;
|
||||
using NovelSoftware.Infrastructure.Persistence;
|
||||
using Novelly.Api.Agent;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Projects;
|
||||
using Novelly.Api.Scenes;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddNovelSoftware(builder.Configuration);
|
||||
builder.AddServiceDefaults();
|
||||
builder.Services.AddNovelly(builder.Configuration);
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddProblemDetails();
|
||||
|
||||
@@ -63,6 +69,8 @@ if (app.Environment.IsDevelopment())
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.MapDefaultEndpoints();
|
||||
|
||||
app.MapGet("/api/health", () => Results.Ok(new { status = "ok" })).WithTags("Health");
|
||||
|
||||
app.MapProjectEndpoints()
|
||||
@@ -1,4 +1,9 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Agent;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Tags;
|
||||
|
||||
namespace Novelly.Api.Projects;
|
||||
|
||||
/// <summary>A single novel and everything that belongs to it.</summary>
|
||||
public class Project
|
||||
+1
-3
@@ -1,6 +1,4 @@
|
||||
using NovelSoftware.Domain.Entities;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Projects;
|
||||
|
||||
public record ProjectSummaryDto(
|
||||
Guid Id,
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Projects;
|
||||
|
||||
public static class ProjectEndpoints
|
||||
{
|
||||
+3
-17
@@ -1,8 +1,8 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
|
||||
namespace NovelSoftware.Application.Services;
|
||||
namespace Novelly.Api.Projects;
|
||||
|
||||
public class ProjectService(INovelDbContext db)
|
||||
{
|
||||
@@ -71,17 +71,3 @@ public class ProjectService(INovelDbContext db)
|
||||
await db.Projects.FirstOrDefaultAsync(p => p.Id == id, ct)
|
||||
?? throw new NotFoundException(nameof(Project), id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Patch semantics shared by every update endpoint: a null value leaves the field
|
||||
/// untouched, an empty string clears it.
|
||||
/// </summary>
|
||||
internal static class Patch
|
||||
{
|
||||
public static string? Apply(string? current, string? incoming) => incoming switch
|
||||
{
|
||||
null => current,
|
||||
"" => null,
|
||||
_ => incoming
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Common;
|
||||
|
||||
namespace Novelly.Api.Scenes;
|
||||
|
||||
/// <summary>
|
||||
/// A scene inside a chapter. The goal/conflict/outcome trio is the unit the agent
|
||||
+2
-3
@@ -1,7 +1,6 @@
|
||||
using NovelSoftware.Domain;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Common;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Scenes;
|
||||
|
||||
public record SceneDto(
|
||||
Guid Id,
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Scenes;
|
||||
|
||||
public static class SceneEndpoints
|
||||
{
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
|
||||
namespace NovelSoftware.Application.Services;
|
||||
namespace Novelly.Api.Scenes;
|
||||
|
||||
public class SceneService(INovelDbContext db)
|
||||
{
|
||||
@@ -1,4 +1,9 @@
|
||||
namespace NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Beats;
|
||||
using Novelly.Api.Chapters;
|
||||
using Novelly.Api.Characters;
|
||||
using Novelly.Api.Projects;
|
||||
|
||||
namespace Novelly.Api.Tags;
|
||||
|
||||
/// <summary>
|
||||
/// A free-form label scoped to one project. Tags are the cross-reference mechanism:
|
||||
@@ -1,6 +1,4 @@
|
||||
using NovelSoftware.Domain.Entities;
|
||||
|
||||
namespace NovelSoftware.Application.Dtos;
|
||||
namespace Novelly.Api.Tags;
|
||||
|
||||
public record TagDto(Guid Id, string Name, string? Color);
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Application.Services;
|
||||
|
||||
namespace NovelSoftware.Api.Endpoints;
|
||||
namespace Novelly.Api.Tags;
|
||||
|
||||
public static class TagEndpoints
|
||||
{
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NovelSoftware.Application.Dtos;
|
||||
using NovelSoftware.Domain.Entities;
|
||||
using Novelly.Api.Common;
|
||||
using Novelly.Api.Data;
|
||||
using Novelly.Api.Projects;
|
||||
|
||||
namespace NovelSoftware.Application.Services;
|
||||
namespace Novelly.Api.Tags;
|
||||
|
||||
public class TagService(INovelDbContext db)
|
||||
{
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"NovelSoftware": "Debug",
|
||||
"Novelly": "Debug",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
// Port 5080 is pinned to match src/Novelly.Web's Vite proxy default and the curl-based
|
||||
// smoke checks in CLAUDE.md, so the API sits at the same address whether it is started
|
||||
// on its own with `dotnet run` or through this AppHost.
|
||||
var api = builder.AddProject<Projects.Novelly_Api>("api")
|
||||
.WithHttpEndpoint(port: 5080, name: "http");
|
||||
|
||||
builder.AddViteApp("web", "../Novelly.Web", "dev")
|
||||
.WithReference(api)
|
||||
.WaitFor(api)
|
||||
.WithHttpEndpoint(port: 5173, name: "http")
|
||||
.WithExternalHttpEndpoints();
|
||||
|
||||
builder.Build().Run();
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Aspire.AppHost.Sdk/13.4.6">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Novelly.Api\Novelly.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.JavaScript" Version="13.4.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<UserSecretsId>4f9d2a13-6c85-4b0e-9a7c-2d3f18b6e0c4</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:17244;http://localhost:15050",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21229",
|
||||
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22051"
|
||||
}
|
||||
},
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:15050",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19033",
|
||||
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20209"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Dashboard": {
|
||||
"Frontend": {
|
||||
"AuthMode": "Unsecured"
|
||||
},
|
||||
"Otlp": {
|
||||
"AuthMode": "Unsecured"
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Aspire.Hosting.Dcp": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"appHost": {
|
||||
"path": "Novelly.AppHost.csproj"
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@ using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using ModelContextProtocol.Protocol;
|
||||
|
||||
namespace NovelSoftware.Mcp;
|
||||
namespace Novelly.Mcp;
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper over the NovelSoftware REST API. The MCP server deliberately owns no
|
||||
/// Thin wrapper over the Novelly REST API. The MCP server deliberately owns no
|
||||
/// domain logic of its own — it is a second front end onto the same API the web client
|
||||
/// uses, so an edit made from Claude Code and one made in the browser are the same edit.
|
||||
/// </summary>
|
||||
@@ -51,7 +51,7 @@ public class NovelApiClient(HttpClient http)
|
||||
{
|
||||
// The API not being up is the most common failure here, and a bare connection
|
||||
// exception tells the model nothing actionable.
|
||||
return Error($"Could not reach the NovelSoftware API at {http.BaseAddress}. Is it running? ({ex.Message})");
|
||||
return Error($"Could not reach the Novelly API at {http.BaseAddress}. Is it running? ({ex.Message})");
|
||||
}
|
||||
|
||||
var body = await response.Content.ReadAsStringAsync(ct);
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NovelSoftware.Mcp;
|
||||
using Novelly.Mcp;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
@@ -11,7 +11,7 @@ builder.Logging.ClearProviders();
|
||||
builder.Logging.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace);
|
||||
builder.Logging.SetMinimumLevel(LogLevel.Warning);
|
||||
|
||||
var apiBaseUrl = builder.Configuration["NOVELSOFTWARE_API_URL"] ?? "http://localhost:5080";
|
||||
var apiBaseUrl = builder.Configuration["NOVELLY_API_URL"] ?? "http://localhost:5080";
|
||||
|
||||
builder.Services.AddHttpClient<NovelApiClient>(client =>
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using System.ComponentModel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace NovelSoftware.Mcp.Tools;
|
||||
namespace Novelly.Mcp.Tools;
|
||||
|
||||
[McpServerToolType]
|
||||
public static class BeatTools
|
||||
+1
-1
@@ -2,7 +2,7 @@ using System.ComponentModel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace NovelSoftware.Mcp.Tools;
|
||||
namespace Novelly.Mcp.Tools;
|
||||
|
||||
[McpServerToolType]
|
||||
public static class CharacterTools
|
||||
+1
-1
@@ -2,7 +2,7 @@ using System.ComponentModel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace NovelSoftware.Mcp.Tools;
|
||||
namespace Novelly.Mcp.Tools;
|
||||
|
||||
[McpServerToolType]
|
||||
public static class ManuscriptTools
|
||||
@@ -2,7 +2,7 @@ using System.ComponentModel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace NovelSoftware.Mcp.Tools;
|
||||
namespace Novelly.Mcp.Tools;
|
||||
|
||||
[McpServerToolType]
|
||||
public static class ProjectTools
|
||||
@@ -2,7 +2,7 @@ using System.ComponentModel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace NovelSoftware.Mcp.Tools;
|
||||
namespace Novelly.Mcp.Tools;
|
||||
|
||||
[McpServerToolType]
|
||||
public static class TagTools
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ServiceDiscovery;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace Microsoft.Extensions.Hosting;
|
||||
|
||||
// Adds common Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
|
||||
// This project should be referenced by each service project in your solution.
|
||||
// To learn more about using this project, see https://aka.ms/aspire/service-defaults
|
||||
public static class Extensions
|
||||
{
|
||||
private const string HealthEndpointPath = "/health";
|
||||
private const string AlivenessEndpointPath = "/alive";
|
||||
|
||||
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
||||
{
|
||||
builder.ConfigureOpenTelemetry();
|
||||
|
||||
builder.AddDefaultHealthChecks();
|
||||
|
||||
builder.Services.AddServiceDiscovery();
|
||||
|
||||
builder.Services.ConfigureHttpClientDefaults(http =>
|
||||
{
|
||||
// Turn on resilience by default
|
||||
http.AddStandardResilienceHandler();
|
||||
|
||||
// Turn on service discovery by default
|
||||
http.AddServiceDiscovery();
|
||||
});
|
||||
|
||||
// Uncomment the following to restrict the allowed schemes for service discovery.
|
||||
// builder.Services.Configure<ServiceDiscoveryOptions>(options =>
|
||||
// {
|
||||
// options.AllowedSchemes = ["https"];
|
||||
// });
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
||||
{
|
||||
builder.Logging.AddOpenTelemetry(logging =>
|
||||
{
|
||||
logging.IncludeFormattedMessage = true;
|
||||
logging.IncludeScopes = true;
|
||||
});
|
||||
|
||||
builder.Services.AddOpenTelemetry()
|
||||
.WithMetrics(metrics =>
|
||||
{
|
||||
metrics.AddAspNetCoreInstrumentation()
|
||||
.AddHttpClientInstrumentation()
|
||||
.AddRuntimeInstrumentation();
|
||||
})
|
||||
.WithTracing(tracing =>
|
||||
{
|
||||
tracing.AddSource(builder.Environment.ApplicationName)
|
||||
.AddAspNetCoreInstrumentation(tracing =>
|
||||
// Exclude health check requests from tracing
|
||||
tracing.Filter = context =>
|
||||
!context.Request.Path.StartsWithSegments(HealthEndpointPath)
|
||||
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)
|
||||
)
|
||||
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
|
||||
//.AddGrpcClientInstrumentation()
|
||||
.AddHttpClientInstrumentation();
|
||||
});
|
||||
|
||||
builder.AddOpenTelemetryExporters();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
||||
{
|
||||
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
|
||||
|
||||
if (useOtlpExporter)
|
||||
{
|
||||
builder.Services.AddOpenTelemetry().UseOtlpExporter();
|
||||
}
|
||||
|
||||
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
|
||||
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
|
||||
//{
|
||||
// builder.Services.AddOpenTelemetry()
|
||||
// .UseAzureMonitor();
|
||||
//}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
|
||||
{
|
||||
builder.Services.AddHealthChecks()
|
||||
// Add a default liveness check to ensure app is responsive
|
||||
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
||||
{
|
||||
// Adding health checks endpoints to applications in non-development environments has security implications.
|
||||
// See https://aka.ms/aspire/healthchecks for details before enabling these endpoints in non-development environments.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
// All health checks must pass for app to be considered ready to accept traffic after starting
|
||||
app.MapHealthChecks(HealthEndpointPath);
|
||||
|
||||
// Only health checks tagged with the "live" tag must pass for app to be considered alive
|
||||
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Tags.Contains("live")
|
||||
});
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsAspireSharedProject>true</IsAspireSharedProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.8.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "novelsoftware-web",
|
||||
"name": "novelly-web",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "novelsoftware-web",
|
||||
"name": "novelly-web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.101.4",
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "novelsoftware-web",
|
||||
"name": "novelly-web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -1,4 +1,4 @@
|
||||
// Mirrors the DTOs in NovelSoftware.Application.Dtos. Enums travel as their names.
|
||||
// Mirrors the DTOs in the Novelly.Api feature folders. Enums travel as their names.
|
||||
|
||||
export type CharacterRole =
|
||||
| 'Protagonist'
|
||||
Reference in New Issue
Block a user