Compare commits

..

5 Commits

Author SHA1 Message Date
gamer147
02525d611a [FA-misc] Add delete button
Some checks failed
CI / build-backend (pull_request) Successful in 2m35s
CI / build-frontend (pull_request) Failing after 27s
2025-12-11 15:00:55 -05:00
gamer147
bbc0b5ec7d [FA-misc] Fix an oversight in the update process
All checks were successful
CI / build-backend (pull_request) Successful in 1m28s
CI / build-frontend (pull_request) Successful in 53s
2025-12-11 14:16:21 -05:00
gamer147
1e374e6eeb [FA-misc] Adds standard Polly Resiliency to Novelpia Http Clients
All checks were successful
CI / build-backend (pull_request) Successful in 1m28s
CI / build-frontend (pull_request) Successful in 46s
2025-12-11 09:53:54 -05:00
c710f14257 Merge pull request '[FA-misc] Page title updates' (#49) from feature/FA-misc_UIUpdates into master
All checks were successful
CI / build-backend (push) Successful in 1m2s
CI / build-frontend (push) Successful in 40s
Build Gateway / build-subgraphs (map[name:novel-service project:FictionArchive.Service.NovelService subgraph:Novel]) (push) Successful in 48s
Build Gateway / build-subgraphs (map[name:scheduler-service project:FictionArchive.Service.SchedulerService subgraph:Scheduler]) (push) Successful in 45s
Build Gateway / build-subgraphs (map[name:translation-service project:FictionArchive.Service.TranslationService subgraph:Translation]) (push) Successful in 47s
Build Gateway / build-subgraphs (map[name:user-service project:FictionArchive.Service.UserService subgraph:User]) (push) Successful in 43s
Release / build-and-push (map[dockerfile:FictionArchive.Service.AuthenticationService/Dockerfile name:authentication-service]) (push) Successful in 2m17s
Release / build-and-push (map[dockerfile:FictionArchive.Service.FileService/Dockerfile name:file-service]) (push) Successful in 2m10s
Release / build-and-push (map[dockerfile:FictionArchive.Service.NovelService/Dockerfile name:novel-service]) (push) Successful in 1m56s
Release / build-and-push (map[dockerfile:FictionArchive.Service.SchedulerService/Dockerfile name:scheduler-service]) (push) Successful in 1m52s
Release / build-and-push (map[dockerfile:FictionArchive.Service.TranslationService/Dockerfile name:translation-service]) (push) Successful in 1m54s
Release / build-and-push (map[dockerfile:FictionArchive.Service.UserService/Dockerfile name:user-service]) (push) Successful in 1m47s
Release / build-frontend (push) Successful in 1m41s
Build Gateway / build-gateway (push) Successful in 3m41s
Reviewed-on: #49
2025-12-11 12:42:33 +00:00
fecb3e6f43 Merge pull request '[FA-misc] Various UI updates' (#48) from feature/FA-misc_UIUpdates into master
All checks were successful
CI / build-backend (push) Successful in 1m0s
CI / build-frontend (push) Successful in 37s
Build Gateway / build-subgraphs (map[name:novel-service project:FictionArchive.Service.NovelService subgraph:Novel]) (push) Successful in 46s
Build Gateway / build-subgraphs (map[name:scheduler-service project:FictionArchive.Service.SchedulerService subgraph:Scheduler]) (push) Successful in 43s
Build Gateway / build-subgraphs (map[name:translation-service project:FictionArchive.Service.TranslationService subgraph:Translation]) (push) Successful in 46s
Build Gateway / build-subgraphs (map[name:user-service project:FictionArchive.Service.UserService subgraph:User]) (push) Successful in 44s
Release / build-and-push (map[dockerfile:FictionArchive.Service.AuthenticationService/Dockerfile name:authentication-service]) (push) Successful in 2m11s
Release / build-and-push (map[dockerfile:FictionArchive.Service.FileService/Dockerfile name:file-service]) (push) Successful in 2m2s
Release / build-and-push (map[dockerfile:FictionArchive.Service.NovelService/Dockerfile name:novel-service]) (push) Successful in 1m48s
Release / build-and-push (map[dockerfile:FictionArchive.Service.SchedulerService/Dockerfile name:scheduler-service]) (push) Successful in 1m57s
Release / build-and-push (map[dockerfile:FictionArchive.Service.TranslationService/Dockerfile name:translation-service]) (push) Successful in 1m50s
Release / build-and-push (map[dockerfile:FictionArchive.Service.UserService/Dockerfile name:user-service]) (push) Successful in 1m40s
Release / build-frontend (push) Successful in 1m37s
Build Gateway / build-gateway (push) Successful in 3m56s
Reviewed-on: #48
2025-12-11 01:37:42 +00:00
7 changed files with 211 additions and 15 deletions

View File

@@ -7,6 +7,7 @@ using FictionArchive.Service.NovelService.Services;
using FictionArchive.Service.NovelService.Services.SourceAdapters; using FictionArchive.Service.NovelService.Services.SourceAdapters;
using FictionArchive.Service.Shared.Services.EventBus; using FictionArchive.Service.Shared.Services.EventBus;
using HotChocolate.Authorization; using HotChocolate.Authorization;
using HotChocolate.Types;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace FictionArchive.Service.NovelService.GraphQL; namespace FictionArchive.Service.NovelService.GraphQL;
@@ -26,4 +27,12 @@ public class Mutation
{ {
return await service.QueueChapterPull(novelId, chapterNumber); return await service.QueueChapterPull(novelId, chapterNumber);
} }
[Error<KeyNotFoundException>]
[Authorize]
public async Task<bool> DeleteNovel(uint novelId, NovelUpdateService service)
{
await service.DeleteNovel(novelId);
return true;
}
} }

View File

@@ -62,12 +62,14 @@ public class Program
builder.Services.AddHttpClient<NovelpiaAuthMessageHandler>(client => builder.Services.AddHttpClient<NovelpiaAuthMessageHandler>(client =>
{ {
client.BaseAddress = new Uri("https://novelpia.com"); client.BaseAddress = new Uri("https://novelpia.com");
}); })
.AddStandardResilienceHandler();
builder.Services.AddHttpClient<ISourceAdapter, NovelpiaAdapter>(client => builder.Services.AddHttpClient<ISourceAdapter, NovelpiaAdapter>(client =>
{ {
client.BaseAddress = new Uri("https://novelpia.com"); client.BaseAddress = new Uri("https://novelpia.com");
}) })
.AddHttpMessageHandler<NovelpiaAuthMessageHandler>(); .AddHttpMessageHandler<NovelpiaAuthMessageHandler>()
.AddStandardResilienceHandler();
builder.Services.Configure<NovelUpdateServiceConfiguration>(builder.Configuration.GetSection("UpdateService")); builder.Services.Configure<NovelUpdateServiceConfiguration>(builder.Configuration.GetSection("UpdateService"));
builder.Services.AddTransient<NovelUpdateService>(); builder.Services.AddTransient<NovelUpdateService>();

View File

@@ -281,15 +281,16 @@ public class NovelUpdateService
// Step 3: Check for existing novel by ExternalId + Source.Key // Step 3: Check for existing novel by ExternalId + Source.Key
var existingNovel = await _dbContext.Novels var existingNovel = await _dbContext.Novels
.Include(n => n.Author) .Include(n => n.Author)
.ThenInclude(a => a.Name) .ThenInclude(a => a.Name)
.ThenInclude(lk => lk.Texts) .ThenInclude(lk => lk.Texts)
.Include(n => n.Source) .Include(n => n.Source)
.Include(n => n.Name) .Include(n => n.Name)
.ThenInclude(lk => lk.Texts) .ThenInclude(lk => lk.Texts)
.Include(n => n.Description) .Include(n => n.Description)
.ThenInclude(lk => lk.Texts) .ThenInclude(lk => lk.Texts)
.Include(n => n.Tags) .Include(n => n.Tags)
.Include(n => n.Chapters) .Include(n => n.Chapters).ThenInclude(chapter => chapter.Body)
.ThenInclude(localizationKey => localizationKey.Texts)
.Include(n => n.CoverImage) .Include(n => n.CoverImage)
.FirstOrDefaultAsync(n => .FirstOrDefaultAsync(n =>
n.ExternalId == metadata.ExternalId && n.ExternalId == metadata.ExternalId &&
@@ -378,12 +379,23 @@ public class NovelUpdateService
var chapter = novel.Chapters.Where(chapter => chapter.Order == chapterNumber).FirstOrDefault(); var chapter = novel.Chapters.Where(chapter => chapter.Order == chapterNumber).FirstOrDefault();
var adapter = _sourceAdapters.FirstOrDefault(adapter => adapter.SourceDescriptor.Key == novel.Source.Key); var adapter = _sourceAdapters.FirstOrDefault(adapter => adapter.SourceDescriptor.Key == novel.Source.Key);
var rawChapter = await adapter.GetRawChapter(chapter.Url); var rawChapter = await adapter.GetRawChapter(chapter.Url);
var localizationText = new LocalizationText()
// If we already have the raw for this, overwrite it for now. Revisions will come later.
var localizationText = chapter.Body.Texts.FirstOrDefault(text => text.Language == novel.RawLanguage);
if (localizationText == null)
{ {
Text = rawChapter.Text, localizationText = new LocalizationText()
Language = novel.RawLanguage {
}; Text = rawChapter.Text,
chapter.Body.Texts.Add(localizationText); Language = novel.RawLanguage
};
chapter.Body.Texts.Add(localizationText);
}
else
{
localizationText.Text = rawChapter.Text;
}
chapter.Images = rawChapter.ImageData.Select(img => new Image() chapter.Images = rawChapter.ImageData.Select(img => new Image()
{ {
OriginalPath = img.Url OriginalPath = img.Url
@@ -476,4 +488,49 @@ public class NovelUpdateService
await _eventBus.Publish(chapterPullEvent); await _eventBus.Publish(chapterPullEvent);
return chapterPullEvent; return chapterPullEvent;
} }
public async Task DeleteNovel(uint novelId)
{
var novel = await _dbContext.Novels
.Include(n => n.CoverImage)
.Include(n => n.Name).ThenInclude(k => k.Texts)
.Include(n => n.Description).ThenInclude(k => k.Texts)
.Include(n => n.Chapters).ThenInclude(c => c.Images)
.Include(n => n.Chapters).ThenInclude(c => c.Name).ThenInclude(k => k.Texts)
.Include(n => n.Chapters).ThenInclude(c => c.Body).ThenInclude(k => k.Texts)
.FirstOrDefaultAsync(n => n.Id == novelId);
if (novel == null)
throw new KeyNotFoundException($"Novel with ID '{novelId}' not found");
// Collect all LocalizationKey IDs for cleanup
var locKeyIds = new List<Guid> { novel.Name.Id, novel.Description.Id };
locKeyIds.AddRange(novel.Chapters.Select(c => c.Name.Id));
locKeyIds.AddRange(novel.Chapters.Select(c => c.Body.Id));
// 1. Remove LocalizationRequests referencing these keys
var locRequests = await _dbContext.LocalizationRequests
.Where(r => locKeyIds.Contains(r.KeyRequestedForTranslation.Id))
.ToListAsync();
_dbContext.LocalizationRequests.RemoveRange(locRequests);
// 2. Remove LocalizationTexts (NO ACTION FK - won't cascade)
_dbContext.RemoveRange(novel.Name.Texts);
_dbContext.RemoveRange(novel.Description.Texts);
foreach (var chapter in novel.Chapters)
{
_dbContext.RemoveRange(chapter.Name.Texts);
_dbContext.RemoveRange(chapter.Body.Texts);
}
// 3. Remove Images (NO ACTION FK - won't cascade)
if (novel.CoverImage != null)
_dbContext.Images.Remove(novel.CoverImage);
foreach (var chapter in novel.Chapters)
_dbContext.Images.RemoveRange(chapter.Images);
// 4. Remove novel - cascades: chapters, localization keys, tag mappings
_dbContext.Novels.Remove(novel);
await _dbContext.SaveChangesAsync();
}
} }

View File

@@ -25,10 +25,12 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.2.0" /> <PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4" />
<PackageReference Include="Polly" Version="8.6.5" />
<PackageReference Include="RabbitMQ.Client" Version="7.2.0" /> <PackageReference Include="RabbitMQ.Client" Version="7.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
</ItemGroup> </ItemGroup>

View File

@@ -32,7 +32,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { client } from '$lib/graphql/client'; import { client } from '$lib/graphql/client';
import { NovelDocument, ImportNovelDocument } from '$lib/graphql/__generated__/graphql'; import { NovelDocument, ImportNovelDocument, DeleteNovelDocument } from '$lib/graphql/__generated__/graphql';
import { isAuthenticated } from '$lib/auth/authStore'; import { isAuthenticated } from '$lib/auth/authStore';
import { Card, CardContent, CardHeader } from '$lib/components/ui/card'; import { Card, CardContent, CardHeader } from '$lib/components/ui/card';
import { Badge } from '$lib/components/ui/badge'; import { Badge } from '$lib/components/ui/badge';
@@ -53,6 +53,7 @@
import ChevronDown from '@lucide/svelte/icons/chevron-down'; import ChevronDown from '@lucide/svelte/icons/chevron-down';
import ChevronUp from '@lucide/svelte/icons/chevron-up'; import ChevronUp from '@lucide/svelte/icons/chevron-up';
import RefreshCw from '@lucide/svelte/icons/refresh-cw'; import RefreshCw from '@lucide/svelte/icons/refresh-cw';
import Trash2 from '@lucide/svelte/icons/trash-2';
import X from '@lucide/svelte/icons/x'; import X from '@lucide/svelte/icons/x';
import ChevronLeft from '@lucide/svelte/icons/chevron-left'; import ChevronLeft from '@lucide/svelte/icons/chevron-left';
import ChevronRight from '@lucide/svelte/icons/chevron-right'; import ChevronRight from '@lucide/svelte/icons/chevron-right';
@@ -71,6 +72,11 @@
let refreshError: string | null = $state(null); let refreshError: string | null = $state(null);
let refreshSuccess = $state(false); let refreshSuccess = $state(false);
// Delete state
let showDeleteConfirm = $state(false);
let deleting = $state(false);
let deleteError: string | null = $state(null);
// Image viewer state // Image viewer state
type GalleryImage = { type GalleryImage = {
src: string; src: string;
@@ -119,7 +125,6 @@
const isNsfw = $derived(novel?.tags?.some((tag) => tag.key === SystemTags.Nsfw) ?? false); const isNsfw = $derived(novel?.tags?.some((tag) => tag.key === SystemTags.Nsfw) ?? false);
const canRefresh = $derived(() => { const canRefresh = $derived(() => {
if (status === 'COMPLETED') return false;
if (!lastUpdated) return true; if (!lastUpdated) return true;
const sixHoursAgo = Date.now() - 6 * 60 * 60 * 1000; const sixHoursAgo = Date.now() - 6 * 60 * 60 * 1000;
return lastUpdated.getTime() < sixHoursAgo; return lastUpdated.getTime() < sixHoursAgo;
@@ -251,6 +256,32 @@
} }
} }
async function deleteNovel() {
if (!novel) return;
deleting = true;
deleteError = null;
try {
const result = await client
.mutation(DeleteNovelDocument, { input: { novelId: novel.id } })
.toPromise();
if (result.error) {
deleteError = result.error.message;
} else if (result.data?.deleteNovel?.errors?.length) {
deleteError = result.data.deleteNovel.errors[0].message;
} else {
// Successfully deleted - redirect to novels list
window.location.href = '/novels';
}
} catch (e) {
deleteError = e instanceof Error ? e.message : 'Failed to delete';
} finally {
deleting = false;
}
}
onMount(() => { onMount(() => {
fetchNovel(); fetchNovel();
}); });
@@ -359,11 +390,20 @@
</TooltipTrigger> </TooltipTrigger>
{#if !canRefresh()} {#if !canRefresh()}
<TooltipContent> <TooltipContent>
{status === 'COMPLETED' ? 'Cannot refresh completed novels' : 'Updated less than 6 hours ago'} Updated less than 6 hours ago
</TooltipContent> </TooltipContent>
{/if} {/if}
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
<Button
variant="destructive"
size="sm"
onclick={() => (showDeleteConfirm = true)}
class="gap-1.5 h-6 text-xs"
>
<Trash2 class="h-3 w-3" />
Delete
</Button>
{/if} {/if}
{#if refreshSuccess} {#if refreshSuccess}
<Badge variant="outline" class="bg-green-500/10 text-green-600 border-green-500/30"> <Badge variant="outline" class="bg-green-500/10 text-green-600 border-green-500/30">
@@ -622,3 +662,54 @@
</div> </div>
</div> </div>
{/if} {/if}
<!-- Delete Confirmation Modal -->
{#if showDeleteConfirm && novel}
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<div
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
onclick={() => !deleting && (showDeleteConfirm = false)}
onkeydown={(e) => e.key === 'Escape' && !deleting && (showDeleteConfirm = false)}
role="dialog"
aria-modal="true"
aria-labelledby="delete-modal-title"
tabindex="-1"
>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div onclick={(e: MouseEvent) => e.stopPropagation()}>
<Card class="w-full max-w-md mx-4 shadow-xl">
<CardHeader>
<h2 id="delete-modal-title" class="text-lg font-semibold">Delete Novel</h2>
</CardHeader>
<CardContent class="space-y-4">
<p class="text-muted-foreground">
Are you sure you want to delete <strong class="text-foreground">{novel.name}</strong>?
</p>
<p class="text-sm text-muted-foreground">
This will permanently delete the novel, all chapters, images, and translations. This action cannot be undone.
</p>
{#if deleteError}
<p class="text-sm text-destructive">{deleteError}</p>
{/if}
<div class="flex justify-end gap-2 pt-2">
<Button
variant="outline"
onclick={() => (showDeleteConfirm = false)}
disabled={deleting}
>
Cancel
</Button>
<Button
variant="destructive"
onclick={deleteNovel}
disabled={deleting}
>
{deleting ? 'Deleting...' : 'Delete'}
</Button>
</div>
</CardContent>
</Card>
</div>
</div>
{/if}

View File

@@ -88,6 +88,17 @@ export type DeleteJobPayload = {
errors: Maybe<Array<DeleteJobError>>; errors: Maybe<Array<DeleteJobError>>;
}; };
export type DeleteNovelError = KeyNotFoundError;
export type DeleteNovelInput = {
novelId: Scalars['UnsignedInt']['input'];
};
export type DeleteNovelPayload = {
boolean: Maybe<Scalars['Boolean']['output']>;
errors: Maybe<Array<DeleteNovelError>>;
};
export type DuplicateNameError = Error & { export type DuplicateNameError = Error & {
message: Scalars['String']['output']; message: Scalars['String']['output'];
}; };
@@ -201,6 +212,7 @@ export type ListFilterInputTypeOfNovelTagDtoFilterInput = {
export type Mutation = { export type Mutation = {
deleteJob: DeleteJobPayload; deleteJob: DeleteJobPayload;
deleteNovel: DeleteNovelPayload;
fetchChapterContents: FetchChapterContentsPayload; fetchChapterContents: FetchChapterContentsPayload;
importNovel: ImportNovelPayload; importNovel: ImportNovelPayload;
registerUser: RegisterUserPayload; registerUser: RegisterUserPayload;
@@ -215,6 +227,11 @@ export type MutationDeleteJobArgs = {
}; };
export type MutationDeleteNovelArgs = {
input: DeleteNovelInput;
};
export type MutationFetchChapterContentsArgs = { export type MutationFetchChapterContentsArgs = {
input: FetchChapterContentsInput; input: FetchChapterContentsInput;
}; };
@@ -707,6 +724,13 @@ export type UuidOperationFilterInput = {
nlte?: InputMaybe<Scalars['UUID']['input']>; nlte?: InputMaybe<Scalars['UUID']['input']>;
}; };
export type DeleteNovelMutationVariables = Exact<{
input: DeleteNovelInput;
}>;
export type DeleteNovelMutation = { deleteNovel: { boolean: boolean | null, errors: Array<{ message: string }> | null } };
export type ImportNovelMutationVariables = Exact<{ export type ImportNovelMutationVariables = Exact<{
input: ImportNovelInput; input: ImportNovelInput;
}>; }>;
@@ -740,6 +764,7 @@ export type NovelsQueryVariables = Exact<{
export type NovelsQuery = { novels: { edges: Array<{ cursor: string, node: { id: any, url: string, name: string, description: string, rawStatus: NovelStatus, lastUpdatedTime: any, coverImage: { newPath: string | null } | null, chapters: Array<{ order: any, name: string }>, tags: Array<{ key: string, displayName: string, tagType: TagType }> } }> | null, pageInfo: { hasNextPage: boolean, endCursor: string | null } } | null }; export type NovelsQuery = { novels: { edges: Array<{ cursor: string, node: { id: any, url: string, name: string, description: string, rawStatus: NovelStatus, lastUpdatedTime: any, coverImage: { newPath: string | null } | null, chapters: Array<{ order: any, name: string }>, tags: Array<{ key: string, displayName: string, tagType: TagType }> } }> | null, pageInfo: { hasNextPage: boolean, endCursor: string | null } } | null };
export const DeleteNovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteNovel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteNovelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteNovel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"boolean"}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Error"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"message"}}]}}]}}]}}]}}]} as unknown as DocumentNode<DeleteNovelMutation, DeleteNovelMutationVariables>;
export const ImportNovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ImportNovel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ImportNovelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"importNovel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelUpdateRequestedEvent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelUrl"}}]}}]}}]}}]} as unknown as DocumentNode<ImportNovelMutation, ImportNovelMutationVariables>; export const ImportNovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ImportNovel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ImportNovelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"importNovel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelUpdateRequestedEvent"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novelUrl"}}]}}]}}]}}]} as unknown as DocumentNode<ImportNovelMutation, ImportNovelMutationVariables>;
export const GetChapterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChapter"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"chapterOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"revision"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"novelName"}},{"kind":"Field","name":{"kind":"Name","value":"totalChapters"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterOrder"}}]}}]}}]} as unknown as DocumentNode<GetChapterQuery, GetChapterQueryVariables>; export const GetChapterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetChapter"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"chapter"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"novelId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"novelId"}}},{"kind":"Argument","name":{"kind":"Name","value":"chapterOrder"},"value":{"kind":"Variable","name":{"kind":"Name","value":"chapterOrder"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"revision"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"novelId"}},{"kind":"Field","name":{"kind":"Name","value":"novelName"}},{"kind":"Field","name":{"kind":"Name","value":"totalChapters"}},{"kind":"Field","name":{"kind":"Name","value":"prevChapterOrder"}},{"kind":"Field","name":{"kind":"Name","value":"nextChapterOrder"}}]}}]}}]} as unknown as DocumentNode<GetChapterQuery, GetChapterQueryVariables>;
export const NovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rawLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"statusOverride"}},{"kind":"Field","name":{"kind":"Name","value":"externalId"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"externalUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"source"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<NovelQuery, NovelQueryVariables>; export const NovelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UnsignedInt"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"rawLanguage"}},{"kind":"Field","name":{"kind":"Name","value":"rawStatus"}},{"kind":"Field","name":{"kind":"Name","value":"statusOverride"}},{"kind":"Field","name":{"kind":"Name","value":"externalId"}},{"kind":"Field","name":{"kind":"Name","value":"createdTime"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"author"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"externalUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"source"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tags"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"tagType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"chapters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"lastUpdatedTime"}},{"kind":"Field","name":{"kind":"Name","value":"images"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<NovelQuery, NovelQueryVariables>;

View File

@@ -0,0 +1,10 @@
mutation DeleteNovel($input: DeleteNovelInput!) {
deleteNovel(input: $input) {
boolean
errors {
... on Error {
message
}
}
}
}