Initial commit
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
namespace FictionArchive.Service.NovelService.Constants;
|
||||
|
||||
public static class SystemTags
|
||||
{
|
||||
public const string Nsfw = "Nsfw";
|
||||
}
|
||||
23
FictionArchive.Service.NovelService/Dockerfile
Normal file
23
FictionArchive.Service.NovelService/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||
USER $APP_UID
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
EXPOSE 8081
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["FictionArchive.Service.NovelService/FictionArchive.Service.NovelService.csproj", "FictionArchive.Service.NovelService/"]
|
||||
RUN dotnet restore "FictionArchive.Service.NovelService/FictionArchive.Service.NovelService.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/FictionArchive.Service.NovelService"
|
||||
RUN dotnet build "./FictionArchive.Service.NovelService.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./FictionArchive.Service.NovelService.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "FictionArchive.Service.NovelService.dll"]
|
||||
@@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.Types.Scalars" Version="15.1.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FictionArchive.Common\FictionArchive.Common.csproj" />
|
||||
<ProjectReference Include="..\FictionArchive.Service.Shared\FictionArchive.Service.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\Interfaces\" />
|
||||
<Folder Include="Services\GraphQL\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
107
FictionArchive.Service.NovelService/GraphQL/Mutation.cs
Normal file
107
FictionArchive.Service.NovelService/GraphQL/Mutation.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using FictionArchive.Service.NovelService.Models.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.Localization;
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
using FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using FictionArchive.Service.NovelService.Services.SourceAdapters;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.GraphQL;
|
||||
|
||||
public class Mutation
|
||||
{
|
||||
// TODO Make this kick off a job in the background somehow. Probably want to think of how jobs will work across services
|
||||
// Also of course need to make it a proper 'upsert'
|
||||
public async Task<Novel> ImportNovel(string novelUrl, NovelServiceDbContext dbContext,
|
||||
IEnumerable<ISourceAdapter> adapters)
|
||||
{
|
||||
NovelMetadata? metadata = null;
|
||||
foreach (ISourceAdapter sourceAdapter in adapters)
|
||||
{
|
||||
if (await sourceAdapter.CanProcessNovel(novelUrl))
|
||||
{
|
||||
metadata = await sourceAdapter.GetMetadata(novelUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata == null)
|
||||
{
|
||||
throw new NotSupportedException("The provided novel url is currently unsupported.");
|
||||
}
|
||||
|
||||
var systemTags = metadata.SystemTags.Select(tag => new NovelTag()
|
||||
{
|
||||
Key = tag,
|
||||
DisplayName = LocalizationKey.CreateFromText(tag, metadata.RawLanguage),
|
||||
TagType = TagType.System
|
||||
});
|
||||
var sourceTags = metadata.SourceTags.Select(tag => new NovelTag()
|
||||
{
|
||||
Key = tag,
|
||||
DisplayName = LocalizationKey.CreateFromText(tag, metadata.RawLanguage),
|
||||
TagType = TagType.External
|
||||
});
|
||||
|
||||
var addedNovel = dbContext.Novels.Add(new Novel()
|
||||
{
|
||||
Author = new Person()
|
||||
{
|
||||
Name = metadata.AuthorName,
|
||||
ExternalUrl = metadata.AuthorUrl,
|
||||
},
|
||||
RawLanguage = metadata.RawLanguage,
|
||||
Url = metadata.Url,
|
||||
ExternalId = metadata.ExternalId,
|
||||
Chapters = metadata.Chapters.Select(chapter =>
|
||||
{
|
||||
return new Chapter()
|
||||
{
|
||||
Order = chapter.Order,
|
||||
Url = chapter.Url,
|
||||
Revision = chapter.Revision,
|
||||
Name = LocalizationKey.CreateFromText(chapter.Name, metadata.RawLanguage),
|
||||
Body = new LocalizationKey()
|
||||
{
|
||||
Texts = new List<LocalizationText>()
|
||||
}
|
||||
};
|
||||
}).ToList(),
|
||||
Description = LocalizationKey.CreateFromText(metadata.Description, metadata.RawLanguage),
|
||||
Name = LocalizationKey.CreateFromText(metadata.Name, metadata.RawLanguage),
|
||||
RawStatus = metadata.RawStatus,
|
||||
Tags = sourceTags.Concat(systemTags).ToList(),
|
||||
Source = new Source()
|
||||
{
|
||||
Name = metadata.SourceDescriptor.Name,
|
||||
Url = metadata.SourceDescriptor.Url,
|
||||
Key = metadata.SourceDescriptor.Key,
|
||||
}
|
||||
});
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
return addedNovel.Entity;
|
||||
}
|
||||
|
||||
public async Task<Chapter> FetchChapterContents(uint novelId,
|
||||
uint chapterNumber,
|
||||
NovelServiceDbContext dbContext,
|
||||
IEnumerable<ISourceAdapter> sourceAdapters)
|
||||
{
|
||||
var novel = await dbContext.Novels.Where(novel => novel.Id == novelId)
|
||||
.Include(novel => novel.Chapters)
|
||||
.ThenInclude(chapter => chapter.Body)
|
||||
.ThenInclude(body => body.Texts)
|
||||
.Include(novel => novel.Source)
|
||||
.FirstOrDefaultAsync();
|
||||
var chapter = novel.Chapters.Where(chapter => chapter.Order == chapterNumber).FirstOrDefault();
|
||||
var adapter = sourceAdapters.FirstOrDefault(adapter => adapter.SourceDescriptor.Key == novel.Source.Key);
|
||||
var rawChapter = await adapter.GetRawChapter(chapter.Url);
|
||||
chapter.Body.Texts.Add(new LocalizationText()
|
||||
{
|
||||
Text = rawChapter,
|
||||
Language = novel.RawLanguage
|
||||
});
|
||||
await dbContext.SaveChangesAsync();
|
||||
return chapter;
|
||||
}
|
||||
}
|
||||
16
FictionArchive.Service.NovelService/GraphQL/Query.cs
Normal file
16
FictionArchive.Service.NovelService/GraphQL/Query.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.GraphQL;
|
||||
|
||||
public class Query
|
||||
{
|
||||
[UsePaging]
|
||||
[UseProjection]
|
||||
[UseFiltering]
|
||||
[UseSorting]
|
||||
public IQueryable<Novel> GetNovels(NovelServiceDbContext dbContext)
|
||||
{
|
||||
return dbContext.Novels.AsQueryable();
|
||||
}
|
||||
}
|
||||
409
FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.Designer.cs
generated
Normal file
409
FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.Designer.cs
generated
Normal file
@@ -0,0 +1,409 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelServiceDbContext))]
|
||||
[Migration("20251118021857_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LocalizationKey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Language")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("LocalizationKeyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("TranslationEngineId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LocalizationKeyId");
|
||||
|
||||
b.HasIndex("TranslationEngineId");
|
||||
|
||||
b.ToTable("LocalizationText");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("BodyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("NovelId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Order")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Revision")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BodyId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("NovelId");
|
||||
|
||||
b.ToTable("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("AuthorId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DescriptionId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("RawLanguage")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RawStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("StatusOverride")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("DescriptionId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Novels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DisplayNameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("NovelId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("TagType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DisplayNameId");
|
||||
|
||||
b.HasIndex("NovelId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Tags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Person", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ExternalUrl")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Person");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Sources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TranslationEngines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", null)
|
||||
.WithMany("Texts")
|
||||
.HasForeignKey("LocalizationKeyId");
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", "TranslationEngine")
|
||||
.WithMany()
|
||||
.HasForeignKey("TranslationEngineId");
|
||||
|
||||
b.Navigation("TranslationEngine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Body")
|
||||
.WithMany()
|
||||
.HasForeignKey("BodyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("NovelId");
|
||||
|
||||
b.Navigation("Body");
|
||||
|
||||
b.Navigation("Name");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Person", "Author")
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Description")
|
||||
.WithMany()
|
||||
.HasForeignKey("DescriptionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Description");
|
||||
|
||||
b.Navigation("Name");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "DisplayName")
|
||||
.WithMany()
|
||||
.HasForeignKey("DisplayNameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("NovelId");
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId");
|
||||
|
||||
b.Navigation("DisplayName");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Navigation("Texts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
|
||||
b.Navigation("Tags");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "LocalizationKey",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_LocalizationKey", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Person",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
ExternalUrl = table.Column<string>(type: "text", nullable: true),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Person", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Sources",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Url = table.Column<string>(type: "text", nullable: false),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Sources", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TranslationEngines",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Key = table.Column<string>(type: "text", nullable: false),
|
||||
DisplayName = table.Column<string>(type: "text", nullable: false),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TranslationEngines", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Novels",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
AuthorId = table.Column<long>(type: "bigint", nullable: false),
|
||||
Url = table.Column<string>(type: "text", nullable: false),
|
||||
RawLanguage = table.Column<int>(type: "integer", nullable: false),
|
||||
RawStatus = table.Column<int>(type: "integer", nullable: false),
|
||||
StatusOverride = table.Column<int>(type: "integer", nullable: true),
|
||||
SourceId = table.Column<long>(type: "bigint", nullable: false),
|
||||
ExternalId = table.Column<string>(type: "text", nullable: false),
|
||||
NameId = table.Column<long>(type: "bigint", nullable: false),
|
||||
DescriptionId = table.Column<long>(type: "bigint", nullable: false),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Novels", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Novels_LocalizationKey_DescriptionId",
|
||||
column: x => x.DescriptionId,
|
||||
principalTable: "LocalizationKey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Novels_LocalizationKey_NameId",
|
||||
column: x => x.NameId,
|
||||
principalTable: "LocalizationKey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Novels_Person_AuthorId",
|
||||
column: x => x.AuthorId,
|
||||
principalTable: "Person",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Novels_Sources_SourceId",
|
||||
column: x => x.SourceId,
|
||||
principalTable: "Sources",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "LocalizationText",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Language = table.Column<int>(type: "integer", nullable: false),
|
||||
Text = table.Column<string>(type: "text", nullable: false),
|
||||
TranslationEngineId = table.Column<long>(type: "bigint", nullable: true),
|
||||
LocalizationKeyId = table.Column<long>(type: "bigint", nullable: true),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_LocalizationText", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_LocalizationText_LocalizationKey_LocalizationKeyId",
|
||||
column: x => x.LocalizationKeyId,
|
||||
principalTable: "LocalizationKey",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_LocalizationText_TranslationEngines_TranslationEngineId",
|
||||
column: x => x.TranslationEngineId,
|
||||
principalTable: "TranslationEngines",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Chapter",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Revision = table.Column<long>(type: "bigint", nullable: false),
|
||||
Order = table.Column<long>(type: "bigint", nullable: false),
|
||||
Url = table.Column<string>(type: "text", nullable: true),
|
||||
NameId = table.Column<long>(type: "bigint", nullable: false),
|
||||
BodyId = table.Column<long>(type: "bigint", nullable: false),
|
||||
NovelId = table.Column<long>(type: "bigint", nullable: true),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Chapter", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Chapter_LocalizationKey_BodyId",
|
||||
column: x => x.BodyId,
|
||||
principalTable: "LocalizationKey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Chapter_LocalizationKey_NameId",
|
||||
column: x => x.NameId,
|
||||
principalTable: "LocalizationKey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Chapter_Novels_NovelId",
|
||||
column: x => x.NovelId,
|
||||
principalTable: "Novels",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Tags",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Key = table.Column<string>(type: "text", nullable: false),
|
||||
DisplayNameId = table.Column<long>(type: "bigint", nullable: false),
|
||||
TagType = table.Column<int>(type: "integer", nullable: false),
|
||||
SourceId = table.Column<long>(type: "bigint", nullable: true),
|
||||
NovelId = table.Column<long>(type: "bigint", nullable: true),
|
||||
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Tags", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Tags_LocalizationKey_DisplayNameId",
|
||||
column: x => x.DisplayNameId,
|
||||
principalTable: "LocalizationKey",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Tags_Novels_NovelId",
|
||||
column: x => x.NovelId,
|
||||
principalTable: "Novels",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Tags_Sources_SourceId",
|
||||
column: x => x.SourceId,
|
||||
principalTable: "Sources",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Chapter_BodyId",
|
||||
table: "Chapter",
|
||||
column: "BodyId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Chapter_NameId",
|
||||
table: "Chapter",
|
||||
column: "NameId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Chapter_NovelId",
|
||||
table: "Chapter",
|
||||
column: "NovelId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LocalizationText_LocalizationKeyId",
|
||||
table: "LocalizationText",
|
||||
column: "LocalizationKeyId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_LocalizationText_TranslationEngineId",
|
||||
table: "LocalizationText",
|
||||
column: "TranslationEngineId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Novels_AuthorId",
|
||||
table: "Novels",
|
||||
column: "AuthorId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Novels_DescriptionId",
|
||||
table: "Novels",
|
||||
column: "DescriptionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Novels_NameId",
|
||||
table: "Novels",
|
||||
column: "NameId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Novels_SourceId",
|
||||
table: "Novels",
|
||||
column: "SourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tags_DisplayNameId",
|
||||
table: "Tags",
|
||||
column: "DisplayNameId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tags_NovelId",
|
||||
table: "Tags",
|
||||
column: "NovelId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tags_SourceId",
|
||||
table: "Tags",
|
||||
column: "SourceId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Chapter");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "LocalizationText");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Tags");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "TranslationEngines");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Novels");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "LocalizationKey");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Person");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Sources");
|
||||
}
|
||||
}
|
||||
}
|
||||
413
FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.Designer.cs
generated
Normal file
413
FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.Designer.cs
generated
Normal file
@@ -0,0 +1,413 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelServiceDbContext))]
|
||||
[Migration("20251118023157_AddSourceKey")]
|
||||
partial class AddSourceKey
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LocalizationKey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Language")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("LocalizationKeyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("TranslationEngineId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LocalizationKeyId");
|
||||
|
||||
b.HasIndex("TranslationEngineId");
|
||||
|
||||
b.ToTable("LocalizationText");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("BodyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("NovelId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Order")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Revision")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BodyId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("NovelId");
|
||||
|
||||
b.ToTable("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("AuthorId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DescriptionId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("RawLanguage")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RawStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("StatusOverride")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("DescriptionId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Novels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DisplayNameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("NovelId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("TagType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DisplayNameId");
|
||||
|
||||
b.HasIndex("NovelId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Tags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Person", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ExternalUrl")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Person");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Sources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TranslationEngines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", null)
|
||||
.WithMany("Texts")
|
||||
.HasForeignKey("LocalizationKeyId");
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", "TranslationEngine")
|
||||
.WithMany()
|
||||
.HasForeignKey("TranslationEngineId");
|
||||
|
||||
b.Navigation("TranslationEngine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Body")
|
||||
.WithMany()
|
||||
.HasForeignKey("BodyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("NovelId");
|
||||
|
||||
b.Navigation("Body");
|
||||
|
||||
b.Navigation("Name");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Person", "Author")
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Description")
|
||||
.WithMany()
|
||||
.HasForeignKey("DescriptionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Description");
|
||||
|
||||
b.Navigation("Name");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "DisplayName")
|
||||
.WithMany()
|
||||
.HasForeignKey("DisplayNameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("NovelId");
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId");
|
||||
|
||||
b.Navigation("DisplayName");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Navigation("Texts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
|
||||
b.Navigation("Tags");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddSourceKey : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Key",
|
||||
table: "Sources",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Key",
|
||||
table: "Sources");
|
||||
}
|
||||
}
|
||||
}
|
||||
432
FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.Designer.cs
generated
Normal file
432
FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.Designer.cs
generated
Normal file
@@ -0,0 +1,432 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelServiceDbContext))]
|
||||
[Migration("20251118030953_FixTagAssociation")]
|
||||
partial class FixTagAssociation
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LocalizationKey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Language")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("LocalizationKeyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("TranslationEngineId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LocalizationKeyId");
|
||||
|
||||
b.HasIndex("TranslationEngineId");
|
||||
|
||||
b.ToTable("LocalizationText");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("BodyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("NovelId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Order")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Revision")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BodyId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("NovelId");
|
||||
|
||||
b.ToTable("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("AuthorId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DescriptionId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("RawLanguage")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RawStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("StatusOverride")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("DescriptionId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Novels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DisplayNameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("TagType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DisplayNameId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Tags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Person", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ExternalUrl")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Person");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Sources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TranslationEngines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelNovelTag", b =>
|
||||
{
|
||||
b.Property<long>("NovelsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TagsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("NovelsId", "TagsId");
|
||||
|
||||
b.HasIndex("TagsId");
|
||||
|
||||
b.ToTable("NovelNovelTag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", null)
|
||||
.WithMany("Texts")
|
||||
.HasForeignKey("LocalizationKeyId");
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", "TranslationEngine")
|
||||
.WithMany()
|
||||
.HasForeignKey("TranslationEngineId");
|
||||
|
||||
b.Navigation("TranslationEngine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Body")
|
||||
.WithMany()
|
||||
.HasForeignKey("BodyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("NovelId");
|
||||
|
||||
b.Navigation("Body");
|
||||
|
||||
b.Navigation("Name");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Person", "Author")
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Description")
|
||||
.WithMany()
|
||||
.HasForeignKey("DescriptionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Description");
|
||||
|
||||
b.Navigation("Name");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "DisplayName")
|
||||
.WithMany()
|
||||
.HasForeignKey("DisplayNameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId");
|
||||
|
||||
b.Navigation("DisplayName");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelNovelTag", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("NovelsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.NovelTag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Navigation("Texts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixTagAssociation : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Tags_Novels_NovelId",
|
||||
table: "Tags");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Tags_NovelId",
|
||||
table: "Tags");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "NovelId",
|
||||
table: "Tags");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "NovelNovelTag",
|
||||
columns: table => new
|
||||
{
|
||||
NovelsId = table.Column<long>(type: "bigint", nullable: false),
|
||||
TagsId = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_NovelNovelTag", x => new { x.NovelsId, x.TagsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_NovelNovelTag_Novels_NovelsId",
|
||||
column: x => x.NovelsId,
|
||||
principalTable: "Novels",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_NovelNovelTag_Tags_TagsId",
|
||||
column: x => x.TagsId,
|
||||
principalTable: "Tags",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_NovelNovelTag_TagsId",
|
||||
table: "NovelNovelTag",
|
||||
column: "TagsId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "NovelNovelTag");
|
||||
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "NovelId",
|
||||
table: "Tags",
|
||||
type: "bigint",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tags_NovelId",
|
||||
table: "Tags",
|
||||
column: "NovelId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Tags_Novels_NovelId",
|
||||
table: "Tags",
|
||||
column: "NovelId",
|
||||
principalTable: "Novels",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Migrations
|
||||
{
|
||||
[DbContext(typeof(NovelServiceDbContext))]
|
||||
partial class NovelServiceDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LocalizationKey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Language")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("LocalizationKeyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("TranslationEngineId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LocalizationKeyId");
|
||||
|
||||
b.HasIndex("TranslationEngineId");
|
||||
|
||||
b.ToTable("LocalizationText");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("BodyId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("NovelId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Order")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("Revision")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BodyId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("NovelId");
|
||||
|
||||
b.ToTable("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<long>("AuthorId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DescriptionId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("NameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("RawLanguage")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RawStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("StatusOverride")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("DescriptionId");
|
||||
|
||||
b.HasIndex("NameId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Novels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("DisplayNameId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("SourceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("TagType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DisplayNameId");
|
||||
|
||||
b.HasIndex("SourceId");
|
||||
|
||||
b.ToTable("Tags");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Person", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ExternalUrl")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Person");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Sources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedUtc")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TranslationEngines");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelNovelTag", b =>
|
||||
{
|
||||
b.Property<long>("NovelsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TagsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("NovelsId", "TagsId");
|
||||
|
||||
b.HasIndex("TagsId");
|
||||
|
||||
b.ToTable("NovelNovelTag");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", null)
|
||||
.WithMany("Texts")
|
||||
.HasForeignKey("LocalizationKeyId");
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", "TranslationEngine")
|
||||
.WithMany()
|
||||
.HasForeignKey("TranslationEngineId");
|
||||
|
||||
b.Navigation("TranslationEngine");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Body")
|
||||
.WithMany()
|
||||
.HasForeignKey("BodyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("NovelId");
|
||||
|
||||
b.Navigation("Body");
|
||||
|
||||
b.Navigation("Name");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Person", "Author")
|
||||
.WithMany()
|
||||
.HasForeignKey("AuthorId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Description")
|
||||
.WithMany()
|
||||
.HasForeignKey("DescriptionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name")
|
||||
.WithMany()
|
||||
.HasForeignKey("NameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Author");
|
||||
|
||||
b.Navigation("Description");
|
||||
|
||||
b.Navigation("Name");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "DisplayName")
|
||||
.WithMany()
|
||||
.HasForeignKey("DisplayNameId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("SourceId");
|
||||
|
||||
b.Navigation("DisplayName");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NovelNovelTag", b =>
|
||||
{
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("NovelsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("FictionArchive.Service.NovelService.Models.Novels.NovelTag", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("TagsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b =>
|
||||
{
|
||||
b.Navigation("Texts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.Enums;
|
||||
|
||||
public enum NovelStatus
|
||||
{
|
||||
Unknown,
|
||||
InProgress,
|
||||
Completed,
|
||||
Hiatus,
|
||||
Abandoned
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.Enums;
|
||||
|
||||
public enum TagType
|
||||
{
|
||||
System,
|
||||
External,
|
||||
UserDefined,
|
||||
Genre,
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using FictionArchive.Common.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models.Localization;
|
||||
|
||||
public class LocalizationKey : BaseEntity<uint>
|
||||
{
|
||||
public List<LocalizationText> Texts { get; set; }
|
||||
|
||||
public static LocalizationKey CreateFromText(string text, Language language)
|
||||
{
|
||||
return new LocalizationKey()
|
||||
{
|
||||
Texts = new List<LocalizationText>()
|
||||
{
|
||||
new LocalizationText()
|
||||
{
|
||||
Language = language,
|
||||
Text = text
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using FictionArchive.Common.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models.Localization;
|
||||
|
||||
public class LocalizationText : BaseEntity<uint>
|
||||
{
|
||||
public Language Language { get; set; }
|
||||
public string Text { get; set; }
|
||||
public TranslationEngine? TranslationEngine { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public abstract class BaseEntity<TKey>
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
public DateTime CreatedUtc { get; set; }
|
||||
public DateTime UpdatedUtc { get; set; }
|
||||
}
|
||||
13
FictionArchive.Service.NovelService/Models/Novels/Chapter.cs
Normal file
13
FictionArchive.Service.NovelService/Models/Novels/Chapter.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using FictionArchive.Service.NovelService.Models.Localization;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public class Chapter : BaseEntity<uint>
|
||||
{
|
||||
public uint Revision { get; set; }
|
||||
public uint Order { get; set; }
|
||||
public string? Url { get; set; }
|
||||
|
||||
public LocalizationKey Name { get; set; }
|
||||
public LocalizationKey Body { get; set; }
|
||||
}
|
||||
24
FictionArchive.Service.NovelService/Models/Novels/Novel.cs
Normal file
24
FictionArchive.Service.NovelService/Models/Novels/Novel.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using FictionArchive.Common.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.Localization;
|
||||
using NovelStatus = FictionArchive.Service.NovelService.Models.Enums.NovelStatus;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public class Novel : BaseEntity<uint>
|
||||
{
|
||||
public Person Author { get; set; }
|
||||
public string Url { get; set; }
|
||||
public Language RawLanguage { get; set; }
|
||||
|
||||
public NovelStatus RawStatus { get; set; }
|
||||
public NovelStatus? StatusOverride { get; set; }
|
||||
|
||||
public Source Source { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
|
||||
public LocalizationKey Name { get; set; }
|
||||
public LocalizationKey Description { get; set; }
|
||||
|
||||
public List<Chapter> Chapters { get; set; }
|
||||
public List<NovelTag> Tags { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FictionArchive.Service.NovelService.Models.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.Localization;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public class NovelTag : BaseEntity<uint>
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public LocalizationKey DisplayName { get; set; }
|
||||
public TagType TagType { get; set; }
|
||||
|
||||
public Source? Source { get; set; }
|
||||
public List<Novel> Novels { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public class Person : BaseEntity<uint>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string? ExternalUrl { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public class Source : BaseEntity<uint>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models;
|
||||
|
||||
public class SourceConfiguration : BaseEntity<uint>
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
[Column(TypeName = "jsonb")]
|
||||
public string Configuration { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.Novels;
|
||||
|
||||
public class TranslationEngine : BaseEntity<uint>
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||
|
||||
public class ChapterMetadata
|
||||
{
|
||||
public uint Revision { get; set; }
|
||||
public uint Order { get; set; }
|
||||
public string? Url { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using FictionArchive.Common.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.Enums;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||
|
||||
public class NovelMetadata
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string AuthorName { get; set; }
|
||||
public string AuthorUrl { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
|
||||
public Language RawLanguage { get; set; }
|
||||
public NovelStatus RawStatus { get; set; }
|
||||
|
||||
public List<ChapterMetadata> Chapters { get; set; }
|
||||
public List<string> SourceTags { get; set; }
|
||||
public List<string> SystemTags { get; set; }
|
||||
public SourceDescriptor SourceDescriptor { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||
|
||||
public class SourceDescriptor
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
74
FictionArchive.Service.NovelService/Program.cs
Normal file
74
FictionArchive.Service.NovelService/Program.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using FictionArchive.Service.NovelService.GraphQL;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using FictionArchive.Service.NovelService.Services.SourceAdapters;
|
||||
using FictionArchive.Service.NovelService.Services.SourceAdapters.Novelpia;
|
||||
using FictionArchive.Service.Shared.Services.GraphQL;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FictionArchive.Service.NovelService;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddMemoryCache();
|
||||
|
||||
#region GraphQL
|
||||
|
||||
builder.Services.AddGraphQLServer()
|
||||
.AddQueryType<Query>()
|
||||
.AddMutationType<Mutation>()
|
||||
.AddType<UnsignedIntType>()
|
||||
.AddMutationConventions(applyToAllMutations: true)
|
||||
.AddFiltering(opt => opt.AddDefaults().BindRuntimeType<uint, UnsignedIntOperationFilterInputType>())
|
||||
.AddSorting()
|
||||
.AddProjections();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Database
|
||||
|
||||
builder.Services.AddDbContext<NovelServiceDbContext>(opt =>
|
||||
{
|
||||
opt.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region Source Adapters
|
||||
|
||||
builder.Services.Configure<NovelpiaConfiguration>(builder.Configuration.GetSection("Novelpia"));
|
||||
builder.Services.AddHttpClient<NovelpiaAuthMessageHandler>(client =>
|
||||
{
|
||||
client.BaseAddress = new Uri("https://novelpia.com");
|
||||
});
|
||||
builder.Services.AddHttpClient<ISourceAdapter, NovelpiaAdapter>(client =>
|
||||
{
|
||||
client.BaseAddress = new Uri("https://novelpia.com");
|
||||
})
|
||||
.AddHttpMessageHandler<NovelpiaAuthMessageHandler>();
|
||||
|
||||
#endregion
|
||||
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Update database
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<NovelServiceDbContext>();
|
||||
dbContext.UpdateDatabase();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.MapHealthChecks("/healthz");
|
||||
|
||||
app.MapGraphQL();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:32130",
|
||||
"sslPort": 44387
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5101",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "graphql",
|
||||
"applicationUrl": "https://localhost:7208;http://localhost:5101",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Services;
|
||||
|
||||
public class NovelServiceDbContext(DbContextOptions options, ILogger<NovelServiceDbContext> logger)
|
||||
: DbContext(options)
|
||||
{
|
||||
public DbSet<Novel> Novels { get; set; }
|
||||
public DbSet<Source> Sources { get; set; }
|
||||
public DbSet<TranslationEngine> TranslationEngines { get; set; }
|
||||
public DbSet<NovelTag> Tags { get; set; }
|
||||
|
||||
private readonly ILogger _logger = logger;
|
||||
|
||||
public void UpdateDatabase()
|
||||
{
|
||||
IEnumerable<string> pendingMigrations = Database.GetPendingMigrations();
|
||||
if (!pendingMigrations.Any())
|
||||
{
|
||||
_logger.LogDebug("No pending migrations found, continuing.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string migration in pendingMigrations)
|
||||
{
|
||||
_logger.LogInformation("Found pending migration with name {migrationName}.", migration);
|
||||
}
|
||||
_logger.LogInformation("Attempting to apply pending migrations...");
|
||||
Database.Migrate();
|
||||
_logger.LogInformation("Migrations applied.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
using FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Services.SourceAdapters;
|
||||
|
||||
public interface ISourceAdapter
|
||||
{
|
||||
public SourceDescriptor SourceDescriptor { get; }
|
||||
public Task<bool> CanProcessNovel(string url);
|
||||
public Task<NovelMetadata> GetMetadata(string novelUrl);
|
||||
public Task<string> GetRawChapter(string chapterUrl);
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using FictionArchive.Common.Enums;
|
||||
using FictionArchive.Service.NovelService.Constants;
|
||||
using FictionArchive.Service.NovelService.Models.Enums;
|
||||
using FictionArchive.Service.NovelService.Models.SourceAdapters;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Services.SourceAdapters.Novelpia;
|
||||
|
||||
public class NovelpiaAdapter : ISourceAdapter
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private const string NovelIdRegex = @"novelpia.com\/novel\/(\d+)";
|
||||
private const string ChapterIdRegex = @"novelpia.com\/viewer\/(\d+)";
|
||||
private const string EpisodeListEndpoint = "/proc/episode_list";
|
||||
private const string ChapterDownloadEndpoint = "/proc/viewer_data/";
|
||||
|
||||
private const string SourceKey = "novelpia";
|
||||
private const string SourceName = "Novelpia";
|
||||
private const string SourceUrl = "https://novelpia.com";
|
||||
|
||||
private const string ChapterDownloadFailedMessage = "본인인증";
|
||||
|
||||
public NovelpiaAdapter(HttpClient httpClient, ILogger<NovelpiaAdapter> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public SourceDescriptor SourceDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return new SourceDescriptor()
|
||||
{
|
||||
Name = SourceName,
|
||||
Key = SourceKey,
|
||||
Url = SourceUrl
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> CanProcessNovel(string url)
|
||||
{
|
||||
return Regex.IsMatch(url, @"https://novelpia.com/novel/(\d+)");
|
||||
}
|
||||
|
||||
public async Task<NovelMetadata> GetMetadata(string novelUrl)
|
||||
{
|
||||
// PROCESS
|
||||
// Get novelurl
|
||||
// Title is <div class="ep-info-line epnew-novel-title">따먹히는 순애 금태양</div>
|
||||
// Author is <a class="writer-name" href="/user/579482">구다수 </a>
|
||||
// Chapters are gotten from the episode_list proc
|
||||
|
||||
uint novelId = uint.Parse(Regex.Match(novelUrl, NovelIdRegex).Groups[1].Value);
|
||||
|
||||
NovelMetadata novel = new NovelMetadata()
|
||||
{
|
||||
Url = novelUrl,
|
||||
RawLanguage = Language.Kr,
|
||||
ExternalId = novelId.ToString(),
|
||||
SystemTags = new List<string>(),
|
||||
SourceTags = new List<string>(),
|
||||
Chapters = new List<ChapterMetadata>(),
|
||||
SourceDescriptor = SourceDescriptor
|
||||
};
|
||||
|
||||
// Novel metadata
|
||||
var novelData = await _httpClient.GetStringAsync(novelUrl);
|
||||
var novelNameMatch = Regex.Match(novelData, @"<div class=""ep-info-line epnew-novel-title"">(.+)<\/div>");
|
||||
var authorMatch = Regex.Match(novelData, @"(?s)<a\s+class=""writer-name""\s+href=""([^""]+)"">\s*(.*?)\s*<\/a>");
|
||||
var descriptionMatch = Regex.Match(novelData, @"(?s)<div\s+class=""synopsis"">\s*(.*?)\s*<\/div>");
|
||||
|
||||
novel.Name = novelNameMatch.Groups[1].Value;
|
||||
novel.Description = descriptionMatch.Groups[1].Value;
|
||||
novel.AuthorName = authorMatch.Groups[2].Value;
|
||||
novel.AuthorUrl = authorMatch.Groups[2].Value;
|
||||
|
||||
// Some badge info
|
||||
var badgeSet = Regex.Match(novelData, @"(?s)<p\s+class=""in-badge"">(.*?)<\/p>");
|
||||
var badgeMatches = Regex.Matches(badgeSet.Groups[1].Value, @"<span[^>]*>(.*?)<\/span>");
|
||||
foreach (Match badge in badgeMatches)
|
||||
{
|
||||
var innerText = badge.Groups[1].Value;
|
||||
if (innerText == "19")
|
||||
{
|
||||
novel.SystemTags.Add(SystemTags.Nsfw);
|
||||
}
|
||||
|
||||
if (innerText == "완결")
|
||||
{
|
||||
novel.RawStatus = NovelStatus.Completed;
|
||||
}
|
||||
else
|
||||
{
|
||||
novel.RawStatus = NovelStatus.InProgress;
|
||||
}
|
||||
}
|
||||
|
||||
// Novel tags
|
||||
HashSet<string> tags = new HashSet<string>();
|
||||
var tagSetMatch = Regex.Match(novelData, @"(?s)<p\s+class=""writer-tag"">(.*?)<\/p>");
|
||||
var tagMatches =
|
||||
Regex.Matches(tagSetMatch.Groups[1].Value, @"<span[^>]*>#(.*?)<\/span>");
|
||||
foreach (Match tagMatch in tagMatches)
|
||||
{
|
||||
var tagText = tagMatch.Groups[1].Value;
|
||||
tags.Add(tagText);
|
||||
}
|
||||
|
||||
foreach (string tag in tags)
|
||||
{
|
||||
novel.SourceTags.Add(tag);
|
||||
}
|
||||
|
||||
// Chapters
|
||||
uint page = 0;
|
||||
List<ChapterMetadata> chapters = new List<ChapterMetadata>();
|
||||
List<uint> seenChapterIds = new List<uint>();
|
||||
uint chapterOrder = 0;
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
_logger.LogInformation("Next chapter batch");
|
||||
var response = await _httpClient.PostAsync(EpisodeListEndpoint, new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
{"novel_no", novelId.ToString()},
|
||||
{"sort", "DOWN"},
|
||||
{"page", page.ToString()}
|
||||
}));
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var capturedChapters = Regex.Matches(responseContent, @"id=""bookmark_(\d+)""></i>(.+?)</b>");
|
||||
if (seenChapterIds.Contains(uint.Parse(capturedChapters[0].Groups[1].Value)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
foreach (Match chapter in capturedChapters)
|
||||
{
|
||||
string chapterId = chapter.Groups[1].Value;
|
||||
string chapterName = chapter.Groups[2].Value;
|
||||
chapters.Add(new ChapterMetadata
|
||||
{
|
||||
Revision = 0,
|
||||
Order = chapterOrder,
|
||||
Url = $"https://novelpia.com/viewer/{chapterId}",
|
||||
Name = chapterName
|
||||
});
|
||||
seenChapterIds.Add(uint.Parse(chapterId));
|
||||
chapterOrder++;
|
||||
}
|
||||
page++;
|
||||
}
|
||||
novel.Chapters = chapters;
|
||||
|
||||
return novel;
|
||||
}
|
||||
|
||||
public async Task<string> GetRawChapter(string chapterUrl)
|
||||
{
|
||||
var chapterId = uint.Parse(Regex.Match(chapterUrl, ChapterIdRegex).Groups[1].Value);
|
||||
var endpoint = ChapterDownloadEndpoint + chapterId;
|
||||
var result = await _httpClient.PostAsync(endpoint, null);
|
||||
var responseContent = await result.Content.ReadAsStringAsync();
|
||||
|
||||
if (string.IsNullOrEmpty(responseContent) || responseContent.Contains(ChapterDownloadFailedMessage))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
using var doc = JsonDocument.Parse(responseContent);
|
||||
JsonElement root = doc.RootElement;
|
||||
|
||||
// Get the "s" array
|
||||
JsonElement sArray = root.GetProperty("s");
|
||||
|
||||
foreach (JsonElement item in sArray.EnumerateArray())
|
||||
{
|
||||
string text = item.GetProperty("text").GetString();
|
||||
if (text.Contains("cover-wrapper"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (text.Contains("opacity: 0"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Append(WebUtility.HtmlDecode(text));
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace FictionArchive.Service.NovelService.Services.SourceAdapters.Novelpia;
|
||||
|
||||
public class NovelpiaAuthMessageHandler : DelegatingHandler
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly NovelpiaConfiguration _configuration;
|
||||
private const string CacheKey = "novelpia_loginkey";
|
||||
private const string LoginUrl = "/proc/login";
|
||||
private const string LoginSuccessMessage = "감사합니다";
|
||||
private const string UserAgent =
|
||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36";
|
||||
|
||||
public NovelpiaAuthMessageHandler(HttpClient httpClient, IOptions<NovelpiaConfiguration> configuration, IMemoryCache cache)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_configuration = configuration.Value;
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
string loginKey = await GetLoginKey();
|
||||
request.Headers.Add("cookie", $"LOGINKEY={loginKey}");
|
||||
request.Headers.UserAgent.ParseAdd(UserAgent);
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<string> GetLoginKey()
|
||||
{
|
||||
if (!_cache.TryGetValue(CacheKey, out string? loginKey))
|
||||
{
|
||||
var random = new Random();
|
||||
var characters = "0123456789abcdef";
|
||||
var firstPart = new string(Enumerable.Range(0, 32).Select(_ => characters[random.Next(characters.Length)]).ToArray());
|
||||
var secondPart = new string(Enumerable.Range(0, 32).Select(_ => characters[random.Next(characters.Length)]).ToArray());
|
||||
loginKey = firstPart + "_" + secondPart;
|
||||
|
||||
HttpRequestMessage loginMessage = new HttpRequestMessage(HttpMethod.Post, LoginUrl);
|
||||
loginMessage.Headers.Add("cookie", $"LOGINKEY={loginKey}");
|
||||
loginMessage.Headers.UserAgent.ParseAdd(UserAgent);
|
||||
loginMessage.Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
{ "redirecturl", string.Empty },
|
||||
{ "email", _configuration.Username },
|
||||
{ "wd", _configuration.Password }
|
||||
});
|
||||
|
||||
var response = await _httpClient.SendAsync(loginMessage);
|
||||
using (var streamReader = new StreamReader(response.Content.ReadAsStream()))
|
||||
{
|
||||
if (streamReader.ReadToEnd().Contains(LoginSuccessMessage))
|
||||
{
|
||||
_cache.Set(CacheKey, loginKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loginKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FictionArchive.Service.NovelService.Services.SourceAdapters.Novelpia;
|
||||
|
||||
public class NovelpiaConfiguration
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
16
FictionArchive.Service.NovelService/appsettings.json
Normal file
16
FictionArchive.Service.NovelService/appsettings.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Novelpia": {
|
||||
"Username": "REPLACE_ME",
|
||||
"Password": "REPLACE_ME"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Host=localhost;Database=FictionArchive_NovelService;Username=postgres;password=postgres"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user