Feat: Reworked ENTIRE Project for Readability

This commit is contained in:
Youwes09
2026-04-20 00:19:22 -05:00
parent 005680394e
commit 4b97f4a6c9
191 changed files with 19210 additions and 15915 deletions
+75
View File
@@ -0,0 +1,75 @@
import { store } from "@store/state.svelte";
import { fetchAuthenticated } from "../core/auth";
const DEFAULT_URL = "http://127.0.0.1:4567";
function getServerUrl(): string {
const url = store.settings.serverUrl;
return typeof url === "string" && url.trim() ? url.replace(/\/$/, "") : DEFAULT_URL;
}
export function plainThumbUrl(path: string): string {
if (!path) return "";
if (path.startsWith("http")) return path;
return `${getServerUrl()}${path}`;
}
export const thumbUrl = plainThumbUrl;
interface GQLResponse<T> {
data: T;
errors?: { message: string }[];
}
function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
return new Promise((resolve, reject) => {
if (signal?.aborted) { reject(new DOMException("Aborted", "AbortError")); return; }
const timer = setTimeout(resolve, ms);
signal?.addEventListener("abort", () => {
clearTimeout(timer);
reject(new DOMException("Aborted", "AbortError"));
}, { once: true });
});
}
async function fetchWithRetry(
url: string,
init: RequestInit,
signal?: AbortSignal,
retries = 3,
delayMs = 300,
): Promise<Response> {
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
for (let i = 0; i < retries; i++) {
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
try {
const res = await fetchAuthenticated(url, init, signal);
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
return res;
} catch (e: any) {
if (e?.authRequired) throw e;
if (e?.name === "AbortError" || signal?.aborted) throw new DOMException("Aborted", "AbortError");
if (i === retries - 1) throw e;
await abortableSleep(delayMs * Math.pow(1.5, i), signal);
}
}
throw new Error("unreachable");
}
export async function gql<T>(
query: string,
variables?: Record<string, unknown>,
signal?: AbortSignal,
): Promise<T> {
const res = await fetchWithRetry(
`${getServerUrl()}/api/graphql`,
{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }) },
signal,
);
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
if (!res.ok) throw new Error(`Suwayomi HTTP ${res.status}`);
const json: GQLResponse<T> = await res.json();
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
if (json.errors?.length) throw new Error(json.errors[0].message);
return json.data;
}
+11
View File
@@ -0,0 +1,11 @@
export * from "./client";
export * from "./queries/manga";
export * from "./queries/chapters";
export * from "./queries/downloads";
export * from "./queries/extensions";
export * from "./queries/tracking";
export * from "./mutations/manga";
export * from "./mutations/chapters";
export * from "./mutations/downloads";
export * from "./mutations/extensions";
export * from "./mutations/tracking";
+48
View File
@@ -0,0 +1,48 @@
export const FETCH_CHAPTERS = `
mutation FetchChapters($mangaId: Int!) {
fetchChapters(input: { mangaId: $mangaId }) {
chapters {
id name chapterNumber sourceOrder isRead isDownloaded isBookmarked
pageCount mangaId uploadDate realUrl lastPageRead scanlator
}
}
}
`;
export const FETCH_CHAPTER_PAGES = `
mutation FetchChapterPages($chapterId: Int!) {
fetchChapterPages(input: { chapterId: $chapterId }) { pages }
}
`;
export const MARK_CHAPTER_READ = `
mutation MarkChapterRead($id: Int!, $isRead: Boolean!) {
updateChapter(input: { id: $id, patch: { isRead: $isRead } }) {
chapter { id isRead }
}
}
`;
export const MARK_CHAPTERS_READ = `
mutation MarkChaptersRead($ids: [Int!]!, $isRead: Boolean!) {
updateChapters(input: { ids: $ids, patch: { isRead: $isRead } }) {
chapters { id isRead }
}
}
`;
export const UPDATE_CHAPTERS_PROGRESS = `
mutation UpdateChaptersProgress($ids: [Int!]!, $isRead: Boolean, $isBookmarked: Boolean, $lastPageRead: Int) {
updateChapters(input: { ids: $ids, patch: { isRead: $isRead, isBookmarked: $isBookmarked, lastPageRead: $lastPageRead } }) {
chapters { id isRead isBookmarked lastPageRead }
}
}
`;
export const DELETE_DOWNLOADED_CHAPTERS = `
mutation DeleteDownloadedChapters($ids: [Int!]!) {
deleteDownloadedChapters(input: { ids: $ids }) {
chapters { id isDownloaded }
}
}
`;
+83
View File
@@ -0,0 +1,83 @@
const QUEUE_FRAGMENT = `
state
queue {
progress state
chapter {
id name pageCount mangaId
manga { id title thumbnailUrl }
}
}
`;
export const ENQUEUE_DOWNLOAD = `
mutation EnqueueDownload($chapterId: Int!) {
enqueueChapterDownload(input: { id: $chapterId }) {
downloadStatus { ${QUEUE_FRAGMENT} }
}
}
`;
export const ENQUEUE_CHAPTERS_DOWNLOAD = `
mutation EnqueueChaptersDownload($chapterIds: [Int!]!) {
enqueueChapterDownloads(input: { ids: $chapterIds }) {
downloadStatus { state }
}
}
`;
export const DEQUEUE_DOWNLOAD = `
mutation DequeueDownload($chapterId: Int!) {
dequeueChapterDownload(input: { id: $chapterId }) {
downloadStatus { state }
}
}
`;
export const START_DOWNLOADER = `
mutation StartDownloader {
startDownloader(input: {}) {
downloadStatus { ${QUEUE_FRAGMENT} }
}
}
`;
export const STOP_DOWNLOADER = `
mutation StopDownloader {
stopDownloader(input: {}) {
downloadStatus { ${QUEUE_FRAGMENT} }
}
}
`;
export const CLEAR_DOWNLOADER = `
mutation ClearDownloader {
clearDownloader(input: {}) {
downloadStatus { ${QUEUE_FRAGMENT} }
}
}
`;
export const FETCH_SOURCE_MANGA = `
mutation FetchSourceManga($source: LongString!, $type: FetchSourceMangaType!, $page: Int!, $query: String, $filters: [FilterChangeInput!]) {
fetchSourceManga(input: { source: $source, type: $type, page: $page, query: $query, filters: $filters }) {
mangas { id title thumbnailUrl inLibrary }
hasNextPage
}
}
`;
export const SET_DOWNLOADS_PATH = `
mutation SetDownloadsPath($path: String!) {
setSettings(input: { settings: { downloadsPath: $path } }) {
settings { downloadsPath }
}
}
`;
export const SET_LOCAL_SOURCE_PATH = `
mutation SetLocalSourcePath($path: String!) {
setSettings(input: { settings: { localSourcePath: $path } }) {
settings { localSourcePath }
}
}
`;
+89
View File
@@ -0,0 +1,89 @@
export const FETCH_EXTENSIONS = `
mutation FetchExtensions {
fetchExtensions(input: {}) {
extensions {
apkName pkgName name lang versionName
isInstalled isObsolete hasUpdate iconUrl
}
}
}
`;
export const UPDATE_EXTENSION = `
mutation UpdateExtension($id: String!, $install: Boolean, $uninstall: Boolean, $update: Boolean) {
updateExtension(input: { id: $id, patch: { install: $install, uninstall: $uninstall, update: $update } }) {
extension { apkName pkgName name isInstalled hasUpdate }
}
}
`;
export const INSTALL_EXTERNAL_EXTENSION = `
mutation InstallExternalExtension($url: String!) {
installExternalExtension(input: { extensionUrl: $url }) {
extension { apkName pkgName name isInstalled }
}
}
`;
export const SET_EXTENSION_REPOS = `
mutation SetExtensionRepos($repos: [String!]!) {
setSettings(input: { settings: { extensionRepos: $repos } }) {
settings { extensionRepos }
}
}
`;
export const SET_SERVER_AUTH = `
mutation SetServerAuth($authMode: AuthMode!, $authUsername: String!, $authPassword: String!) {
setSettings(input: { settings: { authMode: $authMode, authUsername: $authUsername, authPassword: $authPassword } }) {
settings { authMode authUsername }
}
}
`;
export const SET_SOCKS_PROXY = `
mutation SetSocksProxy(
$socksProxyEnabled: Boolean!
$socksProxyHost: String!
$socksProxyPort: String!
$socksProxyVersion: Int!
$socksProxyUsername: String!
$socksProxyPassword: String!
) {
setSettings(input: { settings: {
socksProxyEnabled: $socksProxyEnabled
socksProxyHost: $socksProxyHost
socksProxyPort: $socksProxyPort
socksProxyVersion: $socksProxyVersion
socksProxyUsername: $socksProxyUsername
socksProxyPassword: $socksProxyPassword
}}) {
settings { socksProxyEnabled socksProxyHost socksProxyPort socksProxyVersion socksProxyUsername }
}
}
`;
export const SET_FLARESOLVERR = `
mutation SetFlareSolverr(
$flareSolverrEnabled: Boolean!
$flareSolverrUrl: String!
$flareSolverrTimeout: Int!
$flareSolverrSessionName: String!
$flareSolverrSessionTtl: Int!
$flareSolverrAsResponseFallback: Boolean!
) {
setSettings(input: { settings: {
flareSolverrEnabled: $flareSolverrEnabled
flareSolverrUrl: $flareSolverrUrl
flareSolverrTimeout: $flareSolverrTimeout
flareSolverrSessionName: $flareSolverrSessionName
flareSolverrSessionTtl: $flareSolverrSessionTtl
flareSolverrAsResponseFallback: $flareSolverrAsResponseFallback
}}) {
settings {
flareSolverrEnabled flareSolverrUrl flareSolverrTimeout
flareSolverrSessionName flareSolverrSessionTtl flareSolverrAsResponseFallback
}
}
}
`;
+5
View File
@@ -0,0 +1,5 @@
export * from "./manga";
export * from "./chapters";
export * from "./downloads";
export * from "./extensions";
export * from "./tracking";
+91
View File
@@ -0,0 +1,91 @@
export const FETCH_MANGA = `
mutation FetchManga($id: Int!) {
fetchManga(input: { id: $id }) {
manga {
id title description thumbnailUrl status author artist genre inLibrary realUrl
source { id name displayName }
}
}
}
`;
export const UPDATE_MANGA = `
mutation UpdateManga($id: Int!, $inLibrary: Boolean) {
updateManga(input: { id: $id, patch: { inLibrary: $inLibrary } }) {
manga { id inLibrary }
}
}
`;
export const UPDATE_MANGAS = `
mutation UpdateMangas($ids: [Int!]!, $inLibrary: Boolean) {
updateMangas(input: { ids: $ids, patch: { inLibrary: $inLibrary } }) {
mangas { id inLibrary }
}
}
`;
export const UPDATE_MANGA_CATEGORIES = `
mutation UpdateMangaCategories($mangaId: Int!, $addTo: [Int!]!, $removeFrom: [Int!]!) {
updateMangaCategories(input: { id: $mangaId, patch: { addToCategories: $addTo, removeFromCategories: $removeFrom } }) {
manga { id }
}
}
`;
export const CREATE_CATEGORY = `
mutation CreateCategory($name: String!) {
createCategory(input: { name: $name }) {
category { id name order default includeInUpdate includeInDownload }
}
}
`;
export const UPDATE_CATEGORY = `
mutation UpdateCategory($id: Int!, $name: String) {
updateCategory(input: { id: $id, patch: { name: $name } }) {
category { id name order }
}
}
`;
export const DELETE_CATEGORY = `
mutation DeleteCategory($id: Int!) {
deleteCategory(input: { categoryId: $id }) {
category { id }
}
}
`;
export const UPDATE_CATEGORY_ORDER = `
mutation UpdateCategoryOrder($id: Int!, $position: Int!) {
updateCategoryOrder(input: { id: $id, position: $position }) {
categories { id name order default includeInUpdate includeInDownload }
}
}
`;
export const UPDATE_LIBRARY = `
mutation UpdateLibrary {
updateLibrary(input: {}) {
updateStatus {
jobsInfo { isRunning finishedJobs totalJobs }
}
}
}
`;
export const CREATE_BACKUP = `
mutation CreateBackup {
createBackup(input: {}) { url }
}
`;
export const RESTORE_BACKUP = `
mutation RestoreBackup($backup: Upload!) {
restoreBackup(input: { backup: $backup }) {
id
status { mangaProgress state totalManga }
}
}
`;
+450
View File
@@ -0,0 +1,450 @@
# Mutations
## Manga (`mutations/manga.ts`)
### `FETCH_MANGA`
Fetches and refreshes manga metadata from its source.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Manga ID |
---
### `UPDATE_MANGA`
Updates a single manga's library membership.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Manga ID |
| `inLibrary` | `Boolean` | Add/remove from library |
---
### `UPDATE_MANGAS`
Bulk-updates library membership for multiple manga.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `ids` | `[Int!]!` | Manga IDs |
| `inLibrary` | `Boolean` | Add/remove from library |
---
### `UPDATE_MANGA_CATEGORIES`
Adds or removes a manga from categories.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `mangaId` | `Int!` | Manga ID |
| `addTo` | `[Int!]!` | Category IDs to add to |
| `removeFrom` | `[Int!]!` | Category IDs to remove from |
---
### `CREATE_CATEGORY`
Creates a new manga category.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `name` | `String!` | Category name |
---
### `UPDATE_CATEGORY`
Updates a category's name.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Category ID |
| `name` | `String` | New name |
---
### `DELETE_CATEGORY`
Deletes a category by ID.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Category ID |
---
### `UPDATE_CATEGORY_ORDER`
Moves a category to a new position.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Category ID |
| `position` | `Int!` | New position index |
---
### `UPDATE_LIBRARY`
Triggers a library-wide metadata refresh and returns job status.
**Variables:** none
---
### `CREATE_BACKUP`
Creates a backup and returns its download URL.
**Variables:** none
---
### `RESTORE_BACKUP`
Restores a backup from an uploaded file and returns restore job status.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `backup` | `Upload!` | Backup file |
---
## Chapters (`mutations/chapters.ts`)
### `FETCH_CHAPTERS`
Fetches/refreshes the chapter list for a manga from its source.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `mangaId` | `Int!` | Manga ID |
---
### `FETCH_CHAPTER_PAGES`
Fetches the page URLs for a specific chapter.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `chapterId` | `Int!` | Chapter ID |
---
### `MARK_CHAPTER_READ`
Marks a single chapter as read or unread.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Chapter ID |
| `isRead` | `Boolean!` | Read state |
---
### `MARK_CHAPTERS_READ`
Bulk-marks multiple chapters as read or unread.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `ids` | `[Int!]!` | Chapter IDs |
| `isRead` | `Boolean!` | Read state |
---
### `UPDATE_CHAPTERS_PROGRESS`
Bulk-updates read state, bookmark state, and last page read for multiple chapters.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `ids` | `[Int!]!` | Chapter IDs |
| `isRead` | `Boolean` | Read state |
| `isBookmarked` | `Boolean` | Bookmark state |
| `lastPageRead` | `Int` | Last page index read |
---
### `DELETE_DOWNLOADED_CHAPTERS`
Deletes downloaded chapter files for the given chapter IDs.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `ids` | `[Int!]!` | Chapter IDs |
---
## Downloads (`mutations/downloads.ts`)
### `ENQUEUE_DOWNLOAD`
Adds a single chapter to the download queue.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `chapterId` | `Int!` | Chapter ID |
---
### `ENQUEUE_CHAPTERS_DOWNLOAD`
Adds multiple chapters to the download queue.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `chapterIds` | `[Int!]!` | Chapter IDs |
---
### `DEQUEUE_DOWNLOAD`
Removes a chapter from the download queue.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `chapterId` | `Int!` | Chapter ID |
---
### `START_DOWNLOADER`
Starts the downloader and returns the current queue state.
**Variables:** none
---
### `STOP_DOWNLOADER`
Stops the downloader and returns the current queue state.
**Variables:** none
---
### `CLEAR_DOWNLOADER`
Clears all items from the download queue.
**Variables:** none
---
### `FETCH_SOURCE_MANGA`
Fetches manga from a source (browse/search), with pagination and optional filters.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `source` | `LongString!` | Source ID |
| `type` | `FetchSourceMangaType!` | Browse type (e.g. popular, latest, search) |
| `page` | `Int!` | Page number |
| `query` | `String` | Search query |
| `filters` | `[FilterChangeInput!]` | Source-specific filters |
---
### `SET_DOWNLOADS_PATH`
Sets the downloads directory path in settings.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `path` | `String!` | Filesystem path |
---
### `SET_LOCAL_SOURCE_PATH`
Sets the local source directory path in settings.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `path` | `String!` | Filesystem path |
---
## Extensions (`mutations/extensions.ts`)
### `FETCH_EXTENSIONS`
Fetches the latest extension list from configured repos.
**Variables:** none
---
### `UPDATE_EXTENSION`
Installs, uninstalls, or updates an extension.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `String!` | Extension package name |
| `install` | `Boolean` | Install the extension |
| `uninstall` | `Boolean` | Uninstall the extension |
| `update` | `Boolean` | Update the extension |
---
### `INSTALL_EXTERNAL_EXTENSION`
Installs an extension from an external APK URL.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `url` | `String!` | APK download URL |
---
### `SET_EXTENSION_REPOS`
Sets the list of extension repository URLs.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `repos` | `[String!]!` | Repository URLs |
---
### `SET_SERVER_AUTH`
Configures server authentication mode and credentials.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `authMode` | `AuthMode!` | Auth mode |
| `authUsername` | `String!` | Username |
| `authPassword` | `String!` | Password |
---
### `SET_SOCKS_PROXY`
Configures SOCKS proxy settings.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `socksProxyEnabled` | `Boolean!` | Enable/disable proxy |
| `socksProxyHost` | `String!` | Proxy host |
| `socksProxyPort` | `String!` | Proxy port |
| `socksProxyVersion` | `Int!` | SOCKS version (4 or 5) |
| `socksProxyUsername` | `String!` | Proxy username |
| `socksProxyPassword` | `String!` | Proxy password |
---
### `SET_FLARESOLVERR`
Configures FlareSolverr integration settings.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `flareSolverrEnabled` | `Boolean!` | Enable/disable FlareSolverr |
| `flareSolverrUrl` | `String!` | FlareSolverr URL |
| `flareSolverrTimeout` | `Int!` | Request timeout (ms) |
| `flareSolverrSessionName` | `String!` | Session name |
| `flareSolverrSessionTtl` | `Int!` | Session TTL (seconds) |
| `flareSolverrAsResponseFallback` | `Boolean!` | Use as fallback only |
---
## Tracking (`mutations/tracking.ts`)
### `BIND_TRACK`
Binds a manga to a remote tracker entry.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `mangaId` | `Int!` | Manga ID |
| `trackerId` | `Int!` | Tracker ID |
| `remoteId` | `LongString!` | Remote entry ID on the tracker |
---
### `UPDATE_TRACK`
Updates tracking progress, status, score, and dates for a track record.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `recordId` | `Int!` | Track record ID |
| `status` | `Int` | Reading status |
| `lastChapterRead` | `Float` | Last chapter read |
| `scoreString` | `String` | Score in tracker's format |
| `startDate` | `LongString` | Start date |
| `finishDate` | `LongString` | Finish date |
| `private` | `Boolean` | Mark as private |
---
### `UNBIND_TRACK`
Unbinds a manga from a tracker record.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `recordId` | `Int!` | Track record ID |
---
### `FETCH_TRACK`
Refreshes a track record from the remote tracker.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `recordId` | `Int!` | Track record ID |
---
### `LOGIN_TRACKER_OAUTH`
Initiates OAuth login for a tracker using a callback URL.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `trackerId` | `Int!` | Tracker ID |
| `callbackUrl` | `String!` | OAuth callback URL |
---
### `LOGIN_TRACKER_CREDENTIALS`
Logs into a tracker using username and password.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `trackerId` | `Int!` | Tracker ID |
| `username` | `String!` | Username |
| `password` | `String!` | Password |
---
### `LOGOUT_TRACKER`
Logs out of a tracker.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `trackerId` | `Int!` | Tracker ID |
---
### `LOGIN_USER`
Authenticates a user and returns access and refresh tokens.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `username` | `String!` | Username |
| `password` | `String!` | Password |
---
### `REFRESH_TOKEN`
Refreshes the current access token.
**Variables:** none
+80
View File
@@ -0,0 +1,80 @@
const TRACK_RECORD_FRAGMENT = `
id trackerId remoteId title status score displayScore
lastChapterRead totalChapters remoteUrl startDate finishDate private
`;
export const BIND_TRACK = `
mutation BindTrack($mangaId: Int!, $trackerId: Int!, $remoteId: LongString!) {
bindTrack(input: { mangaId: $mangaId, trackerId: $trackerId, remoteId: $remoteId }) {
trackRecord { ${TRACK_RECORD_FRAGMENT} }
}
}
`;
export const UPDATE_TRACK = `
mutation UpdateTrack($recordId: Int!, $status: Int, $lastChapterRead: Float, $scoreString: String, $startDate: LongString, $finishDate: LongString, $private: Boolean) {
updateTrack(input: { recordId: $recordId, status: $status, lastChapterRead: $lastChapterRead, scoreString: $scoreString, startDate: $startDate, finishDate: $finishDate, private: $private }) {
trackRecord {
id trackerId status score displayScore lastChapterRead totalChapters startDate finishDate private
}
}
}
`;
export const UNBIND_TRACK = `
mutation UnbindTrack($recordId: Int!) {
unbindTrack(input: { recordId: $recordId }) {
trackRecord { id }
}
}
`;
export const FETCH_TRACK = `
mutation FetchTrack($recordId: Int!) {
fetchTrack(input: { recordId: $recordId }) {
trackRecord {
id trackerId status score displayScore lastChapterRead totalChapters startDate finishDate
}
}
}
`;
export const LOGIN_TRACKER_OAUTH = `
mutation LoginTrackerOAuth($trackerId: Int!, $callbackUrl: String!) {
loginTrackerOAuth(input: { trackerId: $trackerId, callbackUrl: $callbackUrl }) {
isLoggedIn
tracker { id name isLoggedIn authUrl }
}
}
`;
export const LOGIN_TRACKER_CREDENTIALS = `
mutation LoginTrackerCredentials($trackerId: Int!, $username: String!, $password: String!) {
loginTrackerCredentials(input: { trackerId: $trackerId, username: $username, password: $password }) {
isLoggedIn
tracker { id name isLoggedIn authUrl }
}
}
`;
export const LOGOUT_TRACKER = `
mutation LogoutTracker($trackerId: Int!) {
logoutTracker(input: { trackerId: $trackerId }) {
tracker { id name isLoggedIn authUrl }
}
}
`;
export const LOGIN_USER = `
mutation Login($username: String!, $password: String!) {
login(input: { username: $username, password: $password }) {
accessToken refreshToken
}
}
`;
export const REFRESH_TOKEN = `
mutation RefreshToken {
refreshToken { accessToken }
}
`;
+10
View File
@@ -0,0 +1,10 @@
export const GET_CHAPTERS = `
query GetChapters($mangaId: Int!) {
chapters(condition: { mangaId: $mangaId }) {
nodes {
id name chapterNumber sourceOrder isRead isDownloaded isBookmarked
pageCount mangaId uploadDate realUrl lastPageRead scanlator
}
}
}
`;
+14
View File
@@ -0,0 +1,14 @@
export const GET_DOWNLOAD_STATUS = `
query GetDownloadStatus {
downloadStatus {
state
queue {
progress state
chapter {
id name pageCount mangaId
manga { id title thumbnailUrl }
}
}
}
}
`;
+35
View File
@@ -0,0 +1,35 @@
export const GET_EXTENSIONS = `
query GetExtensions {
extensions {
nodes {
apkName pkgName name lang versionName
isInstalled isObsolete hasUpdate iconUrl
}
}
}
`;
export const GET_SOURCES = `
query GetSources {
sources {
nodes { id name lang displayName iconUrl isNsfw }
}
}
`;
export const GET_SETTINGS = `
query GetSettings {
settings { extensionRepos }
}
`;
export const GET_SERVER_SECURITY = `
query GetServerSecurity {
settings {
authMode authUsername
socksProxyEnabled socksProxyHost socksProxyPort socksProxyVersion socksProxyUsername
flareSolverrEnabled flareSolverrUrl flareSolverrTimeout
flareSolverrSessionName flareSolverrSessionTtl flareSolverrAsResponseFallback
}
}
`;
+5
View File
@@ -0,0 +1,5 @@
export * from "./manga";
export * from "./chapters";
export * from "./downloads";
export * from "./extensions";
export * from "./tracking";
+96
View File
@@ -0,0 +1,96 @@
export const GET_LIBRARY = `
query GetLibrary {
mangas(condition: { inLibrary: true }) {
nodes {
id title thumbnailUrl inLibrary downloadCount unreadCount
description status author artist genre
source { id name displayName }
chapters { totalCount }
}
}
}
`;
export const GET_ALL_MANGA = `
query GetAllManga {
mangas {
nodes { id title thumbnailUrl inLibrary downloadCount }
}
}
`;
export const GET_MANGA = `
query GetManga($id: Int!) {
manga(id: $id) {
id title description thumbnailUrl status author artist genre inLibrary realUrl
source { id name displayName }
}
}
`;
export const GET_CATEGORIES = `
query GetCategories {
categories {
nodes {
id name order default includeInUpdate includeInDownload
mangas {
nodes { id title thumbnailUrl inLibrary downloadCount unreadCount }
}
}
}
}
`;
export const GET_DOWNLOADED_CHAPTERS_PAGES = `
query GetDownloadedChaptersPages {
chapters(condition: { isDownloaded: true }) {
nodes { pageCount }
}
}
`;
export const GET_DOWNLOADS_PATH = `
query GetDownloadsPath {
settings { downloadsPath localSourcePath }
}
`;
export const LIBRARY_UPDATE_STATUS = `
query LibraryUpdateStatus {
libraryUpdateStatus {
jobsInfo { isRunning finishedJobs totalJobs skippedMangasCount }
mangaUpdates {
status
manga { id title thumbnailUrl unreadCount }
}
}
}
`;
export const GET_RESTORE_STATUS = `
query GetRestoreStatus($id: String!) {
restoreStatus(id: $id) { mangaProgress state totalManga }
}
`;
export const VALIDATE_BACKUP = `
query ValidateBackup($backup: Upload!) {
validateBackup(input: { backup: $backup }) {
missingSources { id name }
missingTrackers { name }
}
}
`;
export const MANGAS_BY_GENRE = `
query MangasByGenre($filter: MangaFilterInput, $first: Int, $offset: Int) {
mangas(filter: $filter, first: $first, offset: $offset, orderBy: IN_LIBRARY_AT, orderByType: DESC) {
nodes {
id title thumbnailUrl inLibrary genre status
source { id displayName }
}
pageInfo { hasNextPage }
totalCount
}
}
`;
+171
View File
@@ -0,0 +1,171 @@
# Queries
## Manga (`queries/manga.ts`)
### `GET_LIBRARY`
Fetches all manga marked as in-library, including metadata, source info, chapter count, download count, and unread count.
**Variables:** none
---
### `GET_ALL_MANGA`
Fetches all manga (library and non-library) with minimal fields.
**Variables:** none
---
### `GET_MANGA`
Fetches a single manga by ID with full metadata and source info.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `Int!` | Manga ID |
---
### `GET_CATEGORIES`
Fetches all categories with their order, settings, and the manga assigned to each.
**Variables:** none
---
### `GET_DOWNLOADED_CHAPTERS_PAGES`
Fetches page counts for all downloaded chapters.
**Variables:** none
---
### `GET_DOWNLOADS_PATH`
Fetches the configured downloads path and local source path from settings.
**Variables:** none
---
### `LIBRARY_UPDATE_STATUS`
Fetches the current library update job status, including progress and any manga with new chapters.
**Variables:** none
---
### `GET_RESTORE_STATUS`
Fetches the status of a backup restore operation by its job ID.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `id` | `String!` | Restore job ID |
---
### `VALIDATE_BACKUP`
Validates a backup file and returns any missing sources or trackers.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `backup` | `Upload!` | Backup file |
---
## Chapters (`queries/chapters.ts`)
### `GET_CHAPTERS`
Fetches all chapters for a given manga, including read/download/bookmark state and page info.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `mangaId` | `Int!` | Manga ID |
---
## Downloads (`queries/downloads.ts`)
### `GET_DOWNLOAD_STATUS`
Fetches the current downloader state and full queue with chapter and manga info.
**Variables:** none
---
## Extensions (`queries/extensions.ts`)
### `GET_EXTENSIONS`
Fetches all extensions with install status, update availability, and metadata.
**Variables:** none
---
### `GET_SOURCES`
Fetches all available sources with language and NSFW flags.
**Variables:** none
---
### `GET_SETTINGS`
Fetches extension repository settings.
**Variables:** none
---
### `GET_SERVER_SECURITY`
Fetches all server security settings including auth mode, SOCKS proxy config, and FlareSolverr config.
**Variables:** none
---
## Tracking (`queries/tracking.ts`)
### `GET_TRACKERS`
Fetches all trackers with login status, supported scores, statuses, and auth info.
**Variables:** none
---
### `GET_MANGA_TRACK_RECORDS`
Fetches all tracking records for a specific manga across all trackers.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `mangaId` | `Int!` | Manga ID |
---
### `SEARCH_TRACKER`
Searches a tracker for manga by query string.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `trackerId` | `Int!` | Tracker ID |
| `query` | `String!` | Search query |
---
### `GET_ALL_TRACKER_RECORDS`
Fetches all trackers and their full track records, including associated manga info.
**Variables:** none
---
### `GET_TRACKER_RECORDS`
Fetches track records for a specific tracker.
**Variables:**
| Name | Type | Description |
|------|------|-------------|
| `trackerId` | `Int!` | Tracker ID |
+69
View File
@@ -0,0 +1,69 @@
export const GET_TRACKERS = `
query GetTrackers {
trackers {
nodes {
id name icon isLoggedIn authUrl supportsPrivateTracking scores
statuses { value name }
}
}
}
`;
export const GET_MANGA_TRACK_RECORDS = `
query GetMangaTrackRecords($mangaId: Int!) {
manga(id: $mangaId) {
trackRecords {
nodes {
id trackerId remoteId title status score displayScore
lastChapterRead totalChapters remoteUrl startDate finishDate private
}
}
}
}
`;
export const SEARCH_TRACKER = `
query SearchTracker($trackerId: Int!, $query: String!) {
searchTracker(input: { trackerId: $trackerId, query: $query }) {
trackSearches {
id trackerId remoteId title coverUrl summary
publishingStatus publishingType startDate totalChapters trackingUrl
}
}
}
`;
export const GET_ALL_TRACKER_RECORDS = `
query GetAllTrackerRecords {
trackers {
nodes {
id name icon isLoggedIn scores
statuses { value name }
trackRecords {
nodes {
id trackerId title status displayScore lastChapterRead
totalChapters remoteUrl private
manga { id title thumbnailUrl inLibrary }
}
}
}
}
}
`;
export const GET_TRACKER_RECORDS = `
query GetTrackerRecords($trackerId: Int!) {
trackers(condition: { id: $trackerId }) {
nodes {
id name
statuses { value name }
trackRecords {
nodes {
id title status displayScore lastChapterRead totalChapters remoteUrl
manga { id title thumbnailUrl }
}
}
}
}
}
`;