From f19390f52defad1aab85ef0c10186ac419704f88 Mon Sep 17 00:00:00 2001 From: glax Date: Mon, 21 Jul 2025 20:38:25 +0200 Subject: [PATCH] Cleanup --- tranga-website/package-lock.json | 817 ++++++++++ tranga-website/package.json | 1 + tranga-website/src/App.tsx | 122 +- tranga-website/src/Components/Chapter.tsx | 50 - tranga-website/src/Components/Jobs.tsx | 163 -- tranga-website/src/Components/Manga.tsx | 119 -- tranga-website/src/Components/MangaList.tsx | 77 - tranga-website/src/Components/MangaPopup.tsx | 186 --- .../src/Components/Mangas/MangaCard.css | 21 + .../src/Components/Mangas/MangaCard.tsx | 102 ++ .../Components/Mangas/MangaConnectorBadge.tsx | 21 + .../src/Components/Mangas/MangaList.css | 3 + .../src/Components/Mangas/MangaList.tsx | 24 + tranga-website/src/Components/Search.tsx | 202 --- .../Components/Settings/AprilFoolsMode.tsx | 59 - .../Settings/ChapterNamingScheme.tsx | 74 - .../src/Components/Settings/FlareSolverr.tsx | 72 - .../Components/Settings/ImageProcessing.tsx | 102 -- .../src/Components/Settings/RequestLimits.tsx | 81 - .../{ => Components/Settings}/Settings.css | 0 .../src/Components/Settings/Settings.tsx | 74 + .../src/Components/Settings/UserAgent.tsx | 60 - tranga-website/src/Settings.tsx | 127 -- tranga-website/src/api/BackendSettings.tsx | 92 -- tranga-website/src/api/Chapter.tsx | 8 - .../src/api/Contexts/ChapterContext.tsx | 9 - .../api/Contexts/MangaConnectorContext.tsx | 4 - .../src/api/Contexts/MangaContext.tsx | 9 - tranga-website/src/api/Job.tsx | 97 -- tranga-website/src/api/LocalLibrary.tsx | 31 - tranga-website/src/api/Manga.tsx | 95 -- tranga-website/src/api/MangaConnector.tsx | 26 - .../src/api/NotificationConnector.tsx | 45 - tranga-website/src/api/Query.tsx | 15 - tranga-website/src/api/Search.tsx | 22 - tranga-website/src/api/fetchApi.tsx | 89 -- .../src/api/types/EnumLibraryType.ts | 0 .../src/api/types/EnumMangaReleaseStatus.ts | 24 - .../src/api/types/EnumRequestLimitType.ts | 8 - tranga-website/src/api/types/IAuthor.ts | 4 - .../src/api/types/IBackendSettings.ts | 19 - tranga-website/src/api/types/IChapter.ts | 11 - .../src/api/types/IFrontendSettings.ts | 4 - .../src/api/types/ILibraryConnector.ts | 11 - tranga-website/src/api/types/ILink.ts | 5 - tranga-website/src/api/types/ILocalLibrary.ts | 5 - tranga-website/src/api/types/IManga.ts | 45 - .../src/api/types/IMangaAltTitle.ts | 5 - .../src/api/types/IMangaConnector.ts | 7 - tranga-website/src/api/types/IMangaTag.ts | 3 - .../src/api/types/INotificationConnector.ts | 7 - .../src/api/types/IRequestLimits.ts | 8 - .../Jobs/IDownloadAvailableChaptersJob.ts | 5 - .../api/types/Jobs/IDownloadMangaCoverJob.ts | 5 - .../types/Jobs/IDownloadSingleChapterJob.ts | 5 - tranga-website/src/api/types/Jobs/IJob.ts | 36 - .../src/api/types/Jobs/IJobWithChapterId.tsx | 5 - .../src/api/types/Jobs/IJobWithMangaId.ts | 5 - .../api/types/Jobs/IMoveFileOrFolderJob.ts | 6 - .../api/types/Jobs/IMoveMangaLibraryJob.ts | 5 - .../api/types/Jobs/IRetrieveChaptersJob.ts | 5 - .../Jobs/IUpdateChaptersDownloadedJob.ts | 5 - .../src/api/types/Jobs/IUpdateCoverJob.ts | 5 - .../IDownloadAvailableChaptersJobRecord.ts | 5 - .../src/api/types/records/IGotifyRecord.ts | 5 - .../src/api/types/records/IModifyJobRecord.ts | 4 - .../api/types/records/INewLibraryRecord.ts | 12 - .../src/api/types/records/INtfyRecord.ts | 7 - .../src/api/types/records/IPushoverRecord.ts | 4 - tranga-website/src/apiClient/ApiContext.tsx | 4 + tranga-website/src/apiClient/MangaContext.tsx | 34 + .../src/apiClient/SettingsContext.tsx | 31 + tranga-website/src/apiClient/V2.ts | 1308 +++++++++++++++++ .../src/apiClient/data-contracts.ts | 335 +++++ tranga-website/src/apiClient/http-client.ts | 260 ++++ 75 files changed, 3068 insertions(+), 2293 deletions(-) delete mode 100644 tranga-website/src/Components/Chapter.tsx delete mode 100644 tranga-website/src/Components/Jobs.tsx delete mode 100644 tranga-website/src/Components/Manga.tsx delete mode 100644 tranga-website/src/Components/MangaList.tsx delete mode 100644 tranga-website/src/Components/MangaPopup.tsx create mode 100644 tranga-website/src/Components/Mangas/MangaCard.css create mode 100644 tranga-website/src/Components/Mangas/MangaCard.tsx create mode 100644 tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx create mode 100644 tranga-website/src/Components/Mangas/MangaList.css create mode 100644 tranga-website/src/Components/Mangas/MangaList.tsx delete mode 100644 tranga-website/src/Components/Search.tsx delete mode 100644 tranga-website/src/Components/Settings/AprilFoolsMode.tsx delete mode 100644 tranga-website/src/Components/Settings/ChapterNamingScheme.tsx delete mode 100644 tranga-website/src/Components/Settings/FlareSolverr.tsx delete mode 100644 tranga-website/src/Components/Settings/ImageProcessing.tsx delete mode 100644 tranga-website/src/Components/Settings/RequestLimits.tsx rename tranga-website/src/{ => Components/Settings}/Settings.css (100%) create mode 100644 tranga-website/src/Components/Settings/Settings.tsx delete mode 100644 tranga-website/src/Components/Settings/UserAgent.tsx delete mode 100644 tranga-website/src/Settings.tsx delete mode 100644 tranga-website/src/api/BackendSettings.tsx delete mode 100644 tranga-website/src/api/Chapter.tsx delete mode 100644 tranga-website/src/api/Contexts/ChapterContext.tsx delete mode 100644 tranga-website/src/api/Contexts/MangaConnectorContext.tsx delete mode 100644 tranga-website/src/api/Contexts/MangaContext.tsx delete mode 100644 tranga-website/src/api/Job.tsx delete mode 100644 tranga-website/src/api/LocalLibrary.tsx delete mode 100644 tranga-website/src/api/Manga.tsx delete mode 100644 tranga-website/src/api/MangaConnector.tsx delete mode 100644 tranga-website/src/api/NotificationConnector.tsx delete mode 100644 tranga-website/src/api/Query.tsx delete mode 100644 tranga-website/src/api/Search.tsx delete mode 100644 tranga-website/src/api/fetchApi.tsx delete mode 100644 tranga-website/src/api/types/EnumLibraryType.ts delete mode 100644 tranga-website/src/api/types/EnumMangaReleaseStatus.ts delete mode 100644 tranga-website/src/api/types/EnumRequestLimitType.ts delete mode 100644 tranga-website/src/api/types/IAuthor.ts delete mode 100644 tranga-website/src/api/types/IBackendSettings.ts delete mode 100644 tranga-website/src/api/types/IChapter.ts delete mode 100644 tranga-website/src/api/types/IFrontendSettings.ts delete mode 100644 tranga-website/src/api/types/ILibraryConnector.ts delete mode 100644 tranga-website/src/api/types/ILink.ts delete mode 100644 tranga-website/src/api/types/ILocalLibrary.ts delete mode 100644 tranga-website/src/api/types/IManga.ts delete mode 100644 tranga-website/src/api/types/IMangaAltTitle.ts delete mode 100644 tranga-website/src/api/types/IMangaConnector.ts delete mode 100644 tranga-website/src/api/types/IMangaTag.ts delete mode 100644 tranga-website/src/api/types/INotificationConnector.ts delete mode 100644 tranga-website/src/api/types/IRequestLimits.ts delete mode 100644 tranga-website/src/api/types/Jobs/IDownloadAvailableChaptersJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IDownloadMangaCoverJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IDownloadSingleChapterJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IJobWithChapterId.tsx delete mode 100644 tranga-website/src/api/types/Jobs/IJobWithMangaId.ts delete mode 100644 tranga-website/src/api/types/Jobs/IMoveFileOrFolderJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IMoveMangaLibraryJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IRetrieveChaptersJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IUpdateChaptersDownloadedJob.ts delete mode 100644 tranga-website/src/api/types/Jobs/IUpdateCoverJob.ts delete mode 100644 tranga-website/src/api/types/records/IDownloadAvailableChaptersJobRecord.ts delete mode 100644 tranga-website/src/api/types/records/IGotifyRecord.ts delete mode 100644 tranga-website/src/api/types/records/IModifyJobRecord.ts delete mode 100644 tranga-website/src/api/types/records/INewLibraryRecord.ts delete mode 100644 tranga-website/src/api/types/records/INtfyRecord.ts delete mode 100644 tranga-website/src/api/types/records/IPushoverRecord.ts create mode 100644 tranga-website/src/apiClient/ApiContext.tsx create mode 100644 tranga-website/src/apiClient/MangaContext.tsx create mode 100644 tranga-website/src/apiClient/SettingsContext.tsx create mode 100644 tranga-website/src/apiClient/V2.ts create mode 100644 tranga-website/src/apiClient/data-contracts.ts create mode 100644 tranga-website/src/apiClient/http-client.ts diff --git a/tranga-website/package-lock.json b/tranga-website/package-lock.json index 483188c..d647d92 100644 --- a/tranga-website/package-lock.json +++ b/tranga-website/package-lock.json @@ -26,6 +26,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "swagger-typescript-api": "^13.2.7", "typescript": "~5.7.2", "typescript-eslint": "^8.24.1", "vite": "^6.2.0" @@ -329,6 +330,36 @@ "node": ">=6.9.0" } }, + "node_modules/@biomejs/js-api": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@biomejs/js-api/-/js-api-1.0.0.tgz", + "integrity": "sha512-69OfQ7+09AtiCIg+k+aU3rEsGit5o/SJWCS3BeBH/2nJYdJGi0cIx+ybka8i1EK69aNcZxYO1y1iAAEmYMq1HA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "peerDependencies": { + "@biomejs/wasm-bundler": "^2.0.0", + "@biomejs/wasm-nodejs": "^2.0.0", + "@biomejs/wasm-web": "^2.0.0" + }, + "peerDependenciesMeta": { + "@biomejs/wasm-bundler": { + "optional": true + }, + "@biomejs/wasm-nodejs": { + "optional": true + }, + "@biomejs/wasm-web": { + "optional": true + } + } + }, + "node_modules/@biomejs/wasm-nodejs": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@biomejs/wasm-nodejs/-/wasm-nodejs-2.0.5.tgz", + "integrity": "sha512-pihpBMylewgDdGFZHRkgmc3OajuGIJPXhvfYuKCNK/CWyJMrYEFmPKs8Iq1kY0sYMmGlTbD4K2udV03KYa+r0Q==", + "dev": true, + "license": "MIT OR Apache-2.0" + }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -1057,6 +1088,13 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", + "dev": true, + "license": "MIT" + }, "node_modules/@floating-ui/core": { "version": "1.6.9", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", @@ -2160,6 +2198,13 @@ "@types/react": "*" } }, + "node_modules/@types/swagger-schema-official": { + "version": "2.0.25", + "resolved": "https://registry.npmjs.org/@types/swagger-schema-official/-/swagger-schema-official-2.0.25.tgz", + "integrity": "sha512-T92Xav+Gf/Ik1uPW581nA+JftmjWPgskw/WBf4TJzxRG/SJ+DfNnNE+WuZ4mrXuzflQMqMkm1LSYjzYW7MB1Cg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2488,6 +2533,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2616,6 +2671,42 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2713,6 +2804,47 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2759,6 +2891,23 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2865,6 +3014,13 @@ "dev": true, "license": "MIT" }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2874,6 +3030,13 @@ "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -2911,6 +3074,19 @@ "csstype": "^3.0.2" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.128", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz", @@ -2918,6 +3094,13 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/entities": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", @@ -2939,6 +3122,13 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -3190,6 +3380,26 @@ "node": ">=0.10.0" } }, + "node_modules/eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true, + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3247,6 +3457,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -3361,6 +3578,34 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -3768,6 +4013,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", + "dev": true, + "license": "MIT" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3875,6 +4127,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3927,6 +4189,16 @@ "dev": true, "license": "ISC" }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4044,6 +4316,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5007,6 +5286,57 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-h2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http2-client": "^1.2.5" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^3.2.1" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -5026,6 +5356,131 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nypm": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", + "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "pathe": "^2.0.3", + "pkg-types": "^2.0.0", + "tinyexec": "^0.3.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/oas-kit-common": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-linter/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-resolver/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", + "dev": true, + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.2.2", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.9", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/oas-validator/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5035,6 +5490,13 @@ "node": ">=0.10.0" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5193,6 +5655,20 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5212,6 +5688,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", + "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -5309,6 +5797,17 @@ ], "license": "MIT" }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", @@ -5389,6 +5888,20 @@ "react-dom": ">=16.6.0" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/refractor": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.9.0.tgz", @@ -5420,6 +5933,16 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", + "dev": true, + "license": "BSD-3-Clause", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -5636,6 +6159,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -5779,6 +6312,66 @@ "node": ">=8" } }, + "node_modules/should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "node_modules/should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.4.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -5808,6 +6401,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -5822,6 +6430,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5884,6 +6505,120 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-schema-official": { + "version": "2.0.0-bab6bed", + "resolved": "https://registry.npmjs.org/swagger-schema-official/-/swagger-schema-official-2.0.0-bab6bed.tgz", + "integrity": "sha512-rCC0NWGKr/IJhtRuPq/t37qvZHI/mH4I4sxflVM+qgVe5Z2uOCivzWaVbuioJaB61kvm5UvB7b49E+oBY0M8jA==", + "dev": true, + "license": "ISC" + }, + "node_modules/swagger-typescript-api": { + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/swagger-typescript-api/-/swagger-typescript-api-13.2.7.tgz", + "integrity": "sha512-rfqqoRFpZJPl477M/snMJPM90EvI8WqhuUHSF5ecC2r/w376T29+QXNJFVPsJmbFu5rBc/8m3vhArtMctjONdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@biomejs/js-api": "1.0.0", + "@biomejs/wasm-nodejs": "2.0.5", + "@types/swagger-schema-official": "^2.0.25", + "c12": "^3.0.4", + "citty": "^0.1.6", + "consola": "^3.4.2", + "eta": "^2.2.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "nanoid": "^5.1.5", + "swagger-schema-official": "2.0.0-bab6bed", + "swagger2openapi": "^7.0.8", + "typescript": "~5.8.3" + }, + "bin": { + "sta": "dist/cli.js", + "swagger-typescript-api": "dist/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/swagger-typescript-api/node_modules/nanoid": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", + "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/swagger-typescript-api/node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/swagger2openapi": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "call-me-maybe": "^1.0.1", + "node-fetch": "^2.6.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^5.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "boast": "boast.js", + "oas-validate": "oas-validate.js", + "swagger2openapi": "swagger2openapi.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/swagger2openapi/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", @@ -5942,6 +6677,13 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -6319,6 +7061,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6345,6 +7105,34 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6367,6 +7155,35 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/tranga-website/package.json b/tranga-website/package.json index 6f55fa6..bec82c8 100644 --- a/tranga-website/package.json +++ b/tranga-website/package.json @@ -28,6 +28,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "swagger-typescript-api": "^13.2.7", "typescript": "~5.7.2", "typescript-eslint": "^8.24.1", "vite": "^6.2.0" diff --git a/tranga-website/src/App.tsx b/tranga-website/src/App.tsx index cd8c1a1..02b3adc 100644 --- a/tranga-website/src/App.tsx +++ b/tranga-website/src/App.tsx @@ -1,107 +1,51 @@ import Sheet from '@mui/joy/Sheet'; import './App.css' -import Settings from "./Settings.tsx"; +import Settings from "./Components/Settings/Settings.tsx"; import Header from "./Header.tsx"; -import {Badge, Button} from "@mui/joy"; -import {useEffect, useState} from "react"; -import {ApiUriContext} from "./api/fetchApi.tsx"; -import Search from './Components/Search.tsx'; -import MangaList from "./Components/MangaList.tsx"; -import {MangaConnectorContext} from "./api/Contexts/MangaConnectorContext.tsx"; -import IMangaConnector from "./api/types/IMangaConnector.ts"; -import {GetAllConnectors} from "./api/MangaConnector.tsx"; -import JobsDrawer from "./Components/Jobs.tsx"; -import {MangaContext} from "./api/Contexts/MangaContext.tsx"; -import IManga from "./api/types/IManga.ts"; -import {GetMangaById} from "./api/Manga.tsx"; -import IChapter from "./api/types/IChapter.ts"; -import {GetChapterFromId} from "./api/Chapter.tsx"; -import {ChapterContext} from "./api/Contexts/ChapterContext.tsx"; +import {createContext, useEffect, useState} from "react"; +import {V2} from "./apiClient/V2.ts"; +import {GetManga, MangaContext } from './apiClient/MangaContext.tsx'; +import { ApiContext } from './apiClient/ApiContext.tsx'; +import MangaList from "./Components/Mangas/MangaList.tsx"; +import {MangaConnector} from "./apiClient/data-contracts.ts"; + +export const MangaConnectorContext = createContext([]); export default function App () { - - const [showSettings, setShowSettings] = useState(false); - const [showSearch, setShowSearch] = useState(false); - const [showJobs, setShowJobs] = useState(false); - const [apiConnected, setApiConnected] = useState(false); - const apiUriStr = localStorage.getItem("apiUri") ?? window.location.href.substring(0, window.location.href.lastIndexOf("/")) + "/api"; - const [apiUri, setApiUri] = useState(apiUriStr); - const [mangas, setMangas] = useState([]); - const [chapters, setChapters] = useState([]); + const [Api, setApi] = useState(new V2()); + const [mangaConnectors, setMangaConnectors] = useState([]); + useEffect(() => { + Api.mangaConnectorList().then(response => { + if (response.ok) + setMangaConnectors(response.data); + }) + }, [Api]); + + useEffect(() => { localStorage.setItem("apiUri", apiUri); + setApi(new V2({ + baseUrl: apiUri + })); }, [apiUri]); - const [mangaPromises, setMangaPromises] = useState(new Map>()); - const GetManga = (mangaId: string) : Promise => { - const promise = mangaPromises.get(mangaId); - if(promise) return promise; - const p = new Promise((resolve, reject) => { - let ret = mangas?.find(m => m.mangaId == mangaId); - if (ret) resolve(ret); - - console.log(`Fetching manga ${mangaId}`); - GetMangaById(apiUri, mangaId).then(manga => { - if(manga && mangas?.find(m => m.mangaId == mangaId) === undefined) - setMangas([...mangas, manga]); - resolve(manga); - }).catch(reject); - }); - setMangaPromises(mangaPromises.set(mangaId, p)); - return p; - } - - const [chapterPromises, setChapterPromises] = useState(new Map>()); - const GetChapter = (chapterId: string) : Promise => { - const promise = chapterPromises.get(chapterId); - if(promise) return promise; - const p = new Promise((resolve, reject) => { - let ret = chapters?.find(c => c.chapterId == chapterId); - if (ret) resolve(ret); - - console.log(`Fetching chapter ${chapterId}`); - GetChapterFromId(apiUri, chapterId).then(chapter => { - if(chapter && chapters?.find(c => c.chapterId == chapterId) === undefined) - setChapters([...chapters, chapter]); - resolve(chapter); - }).catch(reject); - }); - setChapterPromises(chapterPromises.set(chapterId, p)); - return p; - } - - const [mangaConnectors, setMangaConnectors] = useState([]); - - useEffect(() => { - if(!apiConnected) return; - GetAllConnectors(apiUri).then(setMangaConnectors); - }, [apiConnected]); - return ( - - - - - -
- - - - -
- - - - - - + + + + +
+ +
+ + -
+
-
+ ); } diff --git a/tranga-website/src/Components/Chapter.tsx b/tranga-website/src/Components/Chapter.tsx deleted file mode 100644 index cf89e49..0000000 --- a/tranga-website/src/Components/Chapter.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, {ReactElement, useContext, useState} from "react"; -import IChapter from "../api/types/IChapter.ts"; -import {Box, Chip, Link, Stack, Tooltip, Typography} from "@mui/joy"; -import {MangaFromId} from "./Manga.tsx"; -import {ChapterContext} from "../api/Contexts/ChapterContext.tsx"; -import Drawer from "@mui/joy/Drawer"; -import ModalClose from "@mui/joy/ModalClose"; -import {Archive} from "@mui/icons-material"; - -export function ChapterPopupFromId({chapterId, open, setOpen, children}: { chapterId: string | null, open: boolean, setOpen: React.Dispatch>, children?: ReactElement | ReactElement[] | undefined }) { - return ( - setOpen(false)}> - - { - chapterId !== null ? - {children} - : null - } - - ) -} - -export function ChapterFromId({chapterId, children} : { chapterId: string, children?: ReactElement | ReactElement[] | undefined }){ - const chapterContext = useContext(ChapterContext); - - const [chapter, setChapter] = useState(undefined); - chapterContext.GetChapter(chapterId).then(setChapter); - - return ( - chapter === undefined ? - null - : - {children} - ); -} - -export function Chapter({chapter, children} : { chapter: IChapter, children?: ReactElement | ReactElement[] | undefined }){ - return ( - - - - {chapter.title} - Volume {chapter.volumeNumber} - Chapter {chapter.chapterNumber} - - - {children} - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Jobs.tsx b/tranga-website/src/Components/Jobs.tsx deleted file mode 100644 index 987b187..0000000 --- a/tranga-website/src/Components/Jobs.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import { - Button, - DialogContent, DialogTitle, - Drawer, - Input, - Option, - Select, - Stack, - Table, - Typography -} from "@mui/joy"; -import {GetJobsInState, GetJobsOfTypeAndWithState, GetJobsWithType, StartJob} from "../api/Job.tsx"; -import * as React from "react"; -import {useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "../api/fetchApi.tsx"; -import IJob, {JobState, JobStateToString, JobType, JobTypeToString} from "../api/types/Jobs/IJob.ts"; -import ModalClose from "@mui/joy/ModalClose"; -import {MangaPopupFromId} from "./MangaPopup.tsx"; -import IJobWithMangaId from "../api/types/Jobs/IJobWithMangaId.ts"; -import {ChapterPopupFromId} from "./Chapter.tsx"; -import IJobWithChapterId from "../api/types/Jobs/IJobWithChapterId.tsx"; - -export default function JobsDrawer({open, connected, setOpen} : {open: boolean, connected: boolean, setOpen:React.Dispatch>}) { - const apiUri = useContext(ApiUriContext); - - const [allJobs, setAllJobs] = useState([]); - - const [filterState, setFilterState] = useState(null); - const [filterType, setFilterType] = useState(null); - - const pageSize = 10; - const [page, setPage] = useState(1); - - const updateDisplayJobs = useCallback(() => { - if(!connected) - return; - if (filterState === null && filterType === null) - setAllJobs([]); - else if (filterState === null && filterType != null) - GetJobsWithType(apiUri, filterType as unknown as JobType).then(setAllJobs); - else if (filterState != null && filterType === null) - GetJobsInState(apiUri, filterState as unknown as JobState).then(setAllJobs); - else if (filterState != null && filterType != null) - GetJobsOfTypeAndWithState(apiUri, filterType as unknown as JobType, filterState as unknown as JobState).then(setAllJobs); - }, [connected, filterType, filterState]); - - const timerRef = React.useRef>(undefined); - useEffect(() => { - clearTimeout(timerRef.current); - updateDisplayJobs(); - timerRef.current = setInterval(updateDisplayJobs, 2000); - }, [filterState, filterType]); - - useEffect(() => { - if (!open || !connected) - clearTimeout(timerRef.current); - }, [open, connected]); - - const handleChangeState = ( - _: React.SyntheticEvent | null, - newValue: string | null, - ) => { - setFilterState(newValue); - setPage(1); - }; - - const handleChangeType = ( - _: React.SyntheticEvent | null, - newValue: string | null, - ) => { - setFilterType(newValue); - setPage(1); - }; - - const [mangaPopupOpen, setMangaPopupOpen] = React.useState(false); - const [selectedMangaId, setSelectedMangaId] = useState(null); - const OpenMangaPopupDrawer = (mangaId: string) => { - setSelectedMangaId(mangaId); - setMangaPopupOpen(true); - } - - const [chapterPopupOpen, setChapterPopupOpen] = React.useState(false); - const [selectedChapterId, setSelectedChapterId] = React.useState(null); - const OpenChapterPopupDrawer = (chapterId: string) => { - setSelectedChapterId(chapterId); - setChapterPopupOpen(true); - } - - const ReRunJob = useCallback((jobId: string) => { - StartJob(apiUri, jobId, false); - }, [apiUri]); - - return ( - setOpen(false)}> - - Jobs - - - - setPage(parseInt(e.target.value))} - slotProps={{input: { min: 1, max: Math.ceil(allJobs.length / pageSize)}}} - startDecorator={Page} - endDecorator={/{Math.ceil(allJobs.length / pageSize)}}/> - - - - - - - - - - - - - - - {allJobs.slice((page-1)*pageSize, page*pageSize).map((job) => ( - - - - - - - - - ))} - -
TypeStateLast ExecutionNext ExecutionExtra
{JobTypeToString(job.jobType)}{JobStateToString(job.state)}{new Date(job.lastExecution).toLocaleString()}{new Date(job.nextExecution).toLocaleString()}{ExtraContent(job, OpenMangaPopupDrawer, OpenChapterPopupDrawer)}
-
- - -
- ) -} - -function ExtraContent(job: IJob, OpenMangaPopupDrawer: (mangaId: string) => void, OpenChapterPopupDrawer: (IJobWithChapterId: string) => void){ - switch(job.jobType){ - case JobType.DownloadAvailableChaptersJob: - case JobType.DownloadMangaCoverJob: - case JobType.RetrieveChaptersJob: - case JobType.UpdateChaptersDownloadedJob: - case JobType.UpdateCoverJob: - case JobType.MoveMangaLibraryJob: - return - case JobType.DownloadSingleChapterJob: - return - default: - return null; - } -} \ No newline at end of file diff --git a/tranga-website/src/Components/Manga.tsx b/tranga-website/src/Components/Manga.tsx deleted file mode 100644 index d70c21c..0000000 --- a/tranga-website/src/Components/Manga.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import {Badge, Box, Card, CardContent, CardCover, Skeleton, Tooltip, Typography,} from "@mui/joy"; -import IManga from "../api/types/IManga.ts"; -import {CSSProperties, ReactElement, useCallback, useContext, useEffect, useRef, useState} from "react"; -import {GetMangaCoverImageUrl} from "../api/Manga.tsx"; -import {ApiUriContext, getData} from "../api/fetchApi.tsx"; -import {MangaReleaseStatus, ReleaseStatusToPalette} from "../api/types/EnumMangaReleaseStatus.ts"; -import {SxProps} from "@mui/joy/styles/types"; -import MangaPopup from "./MangaPopup.tsx"; -import {MangaConnectorContext} from "../api/Contexts/MangaConnectorContext.tsx"; -import {MangaContext} from "../api/Contexts/MangaContext.tsx"; - -export const CardWidth = 190; -export const CardHeight = 300; - -const coverSx : SxProps = { - height: CardHeight + "px", - width: CardWidth + "px", - position: "relative", -} - -const coverCss : CSSProperties = { - maxHeight: "calc("+CardHeight+"px + 2rem)", - maxWidth: "calc("+CardWidth+"px + 2rem)", - objectFit: "cover", - width: "calc("+CardHeight+"px + 2rem)", - height: "calc("+CardHeight+"px + 2rem)", -} - -export function MangaFromId({mangaId, children} : { mangaId: string, children?: ReactElement | ReactElement[] | undefined }){ - const mangaContext = useContext(MangaContext); - - const [manga, setManga] = useState(undefined); - mangaContext.GetManga(mangaId).then(setManga); - - return ( - manga === undefined ? - } color={ReleaseStatusToPalette(MangaReleaseStatus.Completed)} size={"lg"}> - - - Manga Cover - - - - - - - {mangaId.split("").splice(0,mangaId.length/2).join(" ")} - - - - - - - : - - ); -} - -export function Manga({manga: manga, children} : { manga: IManga, children?: ReactElement | ReactElement[] | undefined}) { - const CoverRef = useRef(null); - - const apiUri = useContext(ApiUriContext); - const mangaConnector = useContext(MangaConnectorContext).find(all => all.name == manga.mangaConnectorName); - - const [expanded, setExpanded] = useState(false); - - useEffect(() => { - LoadMangaCover(); - }, [manga, apiUri]); - - const LoadMangaCover = useCallback(() => { - if(CoverRef.current == null) - return; - const coverUrl = GetMangaCoverImageUrl(apiUri, manga.mangaId, CoverRef.current); - if(CoverRef.current.src == coverUrl) - return; - - //Check if we can fetch the image exists (by fetching it), only then update - getData(coverUrl).then(() => { - if(CoverRef.current) CoverRef.current.src = coverUrl; - }); - }, [manga, apiUri]); - - const interactiveElements = ["button", "input", "textarea", "a", "select", "option", "li"]; - - const maxLength = 50; - const mangaName = manga.name.length > maxLength ? manga.name.substring(0, maxLength-3) + "..." : manga.name; - - return ( - : manga.mangaConnectorName} color={ReleaseStatusToPalette(manga.releaseStatus)} size={"lg"}> - { - const target = e.target as HTMLElement; - if(interactiveElements.find(x => x == target.localName) == undefined) - setExpanded(!expanded)} - }> - - Manga Cover - - - - - - {mangaName} - - - - {children} - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/MangaList.tsx b/tranga-website/src/Components/MangaList.tsx deleted file mode 100644 index b334fd3..0000000 --- a/tranga-website/src/Components/MangaList.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import {Badge, Box, Button, Card, CardContent, CardCover, Stack, Tooltip, Typography} from "@mui/joy"; -import {Dispatch, SetStateAction, useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "../api/fetchApi.tsx"; -import {DeleteJob, GetJobsWithType, StartJob} from "../api/Job.tsx"; -import {JobType} from "../api/types/Jobs/IJob.ts"; -import IDownloadAvailableChaptersJob from "../api/types/Jobs/IDownloadAvailableChaptersJob.ts"; -import {CardHeight, CardWidth, MangaFromId} from "./Manga.tsx"; -import {PlayArrow, Remove} from "@mui/icons-material"; -import * as React from "react"; - -export default function MangaList({connected, setShowSearch}: {connected: boolean, setShowSearch: Dispatch>} ) { - const apiUri = useContext(ApiUriContext); - - const [jobList, setJobList] = useState([]); - - const getJobList = useCallback(() => { - if(!connected) - return; - GetJobsWithType(apiUri, JobType.DownloadAvailableChaptersJob).then((jl) => setJobList(jl as IDownloadAvailableChaptersJob[])); - },[apiUri,connected]); - - const deleteJob = useCallback((jobId: string) => { - DeleteJob(apiUri, jobId).finally(() => getJobList()); - },[apiUri]); - - const startJob = useCallback((jobId: string) => { - StartJob(apiUri, jobId, true).finally(() => getJobList()); - },[apiUri]); - - useEffect(() => { - updateTimer(); - }, [connected, apiUri]); - - const timerRef = React.useRef>(undefined); - const updateTimer = useCallback(() => { - if(!connected){ - clearTimeout(timerRef.current); - return; - }else{ - if(timerRef.current === undefined) { - console.log("Added timer!"); - getJobList(); - timerRef.current = setInterval(getJobList, 2000); - } - } - }, [getJobList, connected, timerRef]); - - return( - - - setShowSearch(true)} sx={{height:"fit-content",width:"fit-content"}}> - - - - - - - Search - - - - - {jobList?.map((job) => ( - - - - - - - ))} - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/MangaPopup.tsx b/tranga-website/src/Components/MangaPopup.tsx deleted file mode 100644 index 2e65fd5..0000000 --- a/tranga-website/src/Components/MangaPopup.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import IManga from "../api/types/IManga.ts"; -import {Badge, Box, Chip, CircularProgress, Drawer, Input, Link, Skeleton, Stack, Typography} from "@mui/joy"; -import React, {ReactElement, useCallback, useContext, useEffect, useRef, useState} from "react"; -import { - GetLatestChapterAvailable, - GetLatestChapterDownloaded, - GetMangaCoverImageUrl, - SetIgnoreThreshold -} from "../api/Manga.tsx"; -import {ApiUriContext, getData} from "../api/fetchApi.tsx"; -import MarkdownPreview from "@uiw/react-markdown-preview"; -import {CardHeight} from "./Manga.tsx"; -import IChapter from "../api/types/IChapter.ts"; -import {MangaReleaseStatus, ReleaseStatusToPalette} from "../api/types/EnumMangaReleaseStatus.ts"; -import {MangaConnectorContext} from "../api/Contexts/MangaConnectorContext.tsx"; -import {MangaContext} from "../api/Contexts/MangaContext.tsx"; -import ModalClose from "@mui/joy/ModalClose"; - - -export function MangaPopupFromId({mangaId, open, setOpen, children} : {mangaId: string | null, open: boolean, setOpen: React.Dispatch>, children?: ReactElement | ReactElement[] | undefined}) { - const mangaContext = useContext(MangaContext); - - const [manga, setManga] = useState(undefined); - - useEffect(() => { - if (!open || mangaId === null) - return; - mangaContext.GetManga(mangaId).then(setManga); - }, [open]); - - return ( - manga === undefined ? - setOpen(false)}> - - - { /* Cover and Description */ } - - - Manga Cover - - - - {mangaId?.split("").splice(0,mangaId.length/2).join(" ")} - - - {mangaId?.split("").filter(x => Number.isNaN(x)).map(_ => - - Wow - - )} - - - - - - { /* Actions */ } - - {children} - - - - : - {children} - ); -} - -export default function MangaPopup({manga, open, setOpen, children} : {manga: IManga | null, open: boolean, setOpen:React.Dispatch>, children?: ReactElement | ReactElement[] | undefined}) { - - const apiUri = useContext(ApiUriContext); - - const CoverRef = useRef(null); - - const LoadMangaCover = useCallback(() => { - if(CoverRef.current == null || manga == null) - return; - if (!open) - return; - const coverUrl = GetMangaCoverImageUrl(apiUri, manga.mangaId, CoverRef.current); - if(CoverRef.current.src == coverUrl) - return; - - //Check if we can fetch the image exists (by fetching it), only then update - getData(coverUrl).then(() => { - if(CoverRef.current) CoverRef.current.src = coverUrl; - }); - }, [manga, apiUri, open]) - - useEffect(() => { - if(!open) - return; - LoadMaxChapter(); - LoadDownloadedChapter(); - LoadMangaCover(); - }, [open]); - - const [mangaMaxChapter, setMangaMaxChapter] = useState(); - const [maxChapterLoading, setMaxChapterLoading] = useState(true); - const LoadMaxChapter = useCallback(() => { - if(manga == null) - return; - setMaxChapterLoading(true); - GetLatestChapterAvailable(apiUri, manga.mangaId) - .then(setMangaMaxChapter) - .finally(() => setMaxChapterLoading(false)); - }, [manga, apiUri]); - - const [mangaDownloadedChapter, setMangaDownloadedChapter] = useState(); - const [downloadedChapterLoading, setDownloadedChapterLoading] = useState(true); - const LoadDownloadedChapter = useCallback(() => { - if(manga == null) - return; - setDownloadedChapterLoading(true); - GetLatestChapterDownloaded(apiUri, manga.mangaId) - .then(setMangaDownloadedChapter) - .finally(() => setDownloadedChapterLoading(false)); - }, [manga, apiUri]); - - const [updatingThreshold, setUpdatingThreshold] = useState(false); - const updateIgnoreThreshold = useCallback((value: number) => { - if(manga == null) - return; - setUpdatingThreshold(true); - SetIgnoreThreshold(apiUri, manga.mangaId, value).finally(() => setUpdatingThreshold(false)); - },[manga, apiUri]) - - const mangaConnector = useContext(MangaConnectorContext).find(all => all.name == manga?.mangaConnectorName); - - return ( - setOpen(false)}> - - - { /* Cover and Description */ } - - : manga?.mangaConnectorName} color={ReleaseStatusToPalette(manga?.releaseStatus??MangaReleaseStatus.Unreleased)} size={"lg"}> - Manga Cover - - - - {manga?.name} - - - {manga?.authors?.map(author => {author.authorName})} - {manga?.mangaTags?.map(tag => {tag.tag})} - {manga?.links?.map(link => - - {link?.linkProvider??"Load Failed"} - - )} - - - - - - { /* Actions */ } - - - { - updatingThreshold ? - - : Ch. - } - - } - endDecorator={ - - - /{mangaMaxChapter?.chapterNumber??"-"} - - - } - sx={{width:"min-content"}} - size={"md"} - onChange={(e) => updateIgnoreThreshold(e.currentTarget.valueAsNumber)} - /> - {children} - - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Mangas/MangaCard.css b/tranga-website/src/Components/Mangas/MangaCard.css new file mode 100644 index 0000000..a359ea0 --- /dev/null +++ b/tranga-website/src/Components/Mangas/MangaCard.css @@ -0,0 +1,21 @@ +.manga-card { + width: 220px; + height: 300px; +} + +.manga-cover-blur { + background: linear-gradient(to bottom, rgba(0,0,0,0.8), rgba(0,0,0,0.2) 75%); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(9px); + -webkit-backdrop-filter: blur(9px); +} + +.manga-card-badge-icon { + width: 25px; + height: 25px; +} + +.manga-modal { + width: 90%; + margin: auto; +} \ No newline at end of file diff --git a/tranga-website/src/Components/Mangas/MangaCard.tsx b/tranga-website/src/Components/Mangas/MangaCard.tsx new file mode 100644 index 0000000..cc5c312 --- /dev/null +++ b/tranga-website/src/Components/Mangas/MangaCard.tsx @@ -0,0 +1,102 @@ +import { + Badge, + Box, + Card, + CardContent, + CardCover, + Chip, + Link, + Modal, + ModalDialog, + Stack, Tooltip, + Typography +} from "@mui/joy"; +import {Manga} from "../../apiClient/data-contracts.ts"; +import {Dispatch, SetStateAction, useContext, useState} from "react"; +import {MangaContext} from "../../apiClient/MangaContext.tsx"; +import "./MangaCard.css"; +import MangaConnectorBadge from "./MangaConnectorBadge.tsx"; +import ModalClose from "@mui/joy/ModalClose"; +import {ApiContext} from "../../apiClient/ApiContext.tsx"; +import MarkdownPreview from '@uiw/react-markdown-preview'; + +export function MangaCardFromId({mangaId} : {mangaId: string}) { + const Mangas = useContext(MangaContext); + const [manga, setManga] = useState(undefined); + + Mangas.GetManga(mangaId).then(setManga); + + return +} + +export function MangaCard({manga} : {manga: Manga | undefined}) { + if (manga === undefined) + return PlaceHolderCard(); + + const [open, setOpen] = useState(false); + + return ( + + setOpen(true)}> + + + + + + {manga?.name} + + + + + ); +} + +function MangaModal({manga, open, setOpen}: {manga: Manga | undefined, open: boolean, setOpen: Dispatch>}) { + + return ( + setOpen(false)} className={"manga-modal"}> + + + {manga?.altTitles?.map(title => {title.title})}}> + {manga?.name} + + + + + + + + {manga?.mangaTags?.map((tag) => {tag.tag})} + {manga?.links?.map((link) => {link.linkProvider})} + + + + + + + + + ); +} + +function PlaceHolderCard(){ + return ( + + + + + + + + + ); +} + +function MangaCover({manga}: {manga: Manga | undefined}) { + const api = useContext(ApiContext); + const uri = manga ? `${api.baseUrl}/v2/Manga/${manga?.key}/Cover` : "blahaj.png"; + + return ( + + ); +} \ No newline at end of file diff --git a/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx b/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx new file mode 100644 index 0000000..b63d34a --- /dev/null +++ b/tranga-website/src/Components/Mangas/MangaConnectorBadge.tsx @@ -0,0 +1,21 @@ +import { Badge } from "@mui/joy"; +import {Manga, MangaConnector} from "../../apiClient/data-contracts.ts"; +import {ReactElement, useContext, useEffect, useState} from "react"; +import {MangaConnectorContext} from "../../App.tsx"; +import "./MangaCard.css" + +export default function MangaConnectorBadge ({manga, children} : {manga: Manga, children? : ReactElement | ReactElement[] | undefined}) { + const context = useContext(MangaConnectorContext); + const [connectors, setConnectors] = useState([]); + + useEffect(() => { + if (context) + setConnectors(context.filter(con => Object.keys(manga.idsOnMangaConnectors??[]).find(name => con.name == name))); + }, []); + + return ( + )}> + {children} + + ); +} \ No newline at end of file diff --git a/tranga-website/src/Components/Mangas/MangaList.css b/tranga-website/src/Components/Mangas/MangaList.css new file mode 100644 index 0000000..79616c9 --- /dev/null +++ b/tranga-website/src/Components/Mangas/MangaList.css @@ -0,0 +1,3 @@ +.manga-list { + margin: 15px; +} \ No newline at end of file diff --git a/tranga-website/src/Components/Mangas/MangaList.tsx b/tranga-website/src/Components/Mangas/MangaList.tsx new file mode 100644 index 0000000..c3f935c --- /dev/null +++ b/tranga-website/src/Components/Mangas/MangaList.tsx @@ -0,0 +1,24 @@ +import {useContext, useState} from "react"; +import {ApiContext} from "../../apiClient/ApiContext.tsx"; +import {MangaCardFromId} from "./MangaCard.tsx"; +import {Stack} from "@mui/joy"; +import "./MangaList.css"; + +export default function MangaList (){ + const Api = useContext(ApiContext); + + const [mangaIds, setMangaIds] = useState(); + + Api.mangaList().then((response) => { + if (!response.ok) + return; + setMangaIds(response.data); + }); + + return ( + + {mangaIds?.map(id => )} + + ); + +} \ No newline at end of file diff --git a/tranga-website/src/Components/Search.tsx b/tranga-website/src/Components/Search.tsx deleted file mode 100644 index c10ce01..0000000 --- a/tranga-website/src/Components/Search.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import { - Avatar, - Button, - Chip, - CircularProgress, - Drawer, - Input, - ListItemDecorator, - Option, - Select, SelectOption, - Skeleton, - Stack, - Step, - StepIndicator, - Stepper, - Typography -} from "@mui/joy"; -import ModalClose from "@mui/joy/ModalClose"; -import IMangaConnector from "../api/types/IMangaConnector"; -import {useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "../api/fetchApi.tsx"; -import {GetAllConnectors} from "../api/MangaConnector.tsx"; -import IManga from "../api/types/IManga.ts"; -import {SearchNameOnConnector, SearchUrl} from "../api/Search.tsx"; -import {Manga} from "./Manga.tsx"; -import Add from "@mui/icons-material/Add"; -import React from "react"; -import {CreateDownloadAvailableChaptersJob} from "../api/Job.tsx"; -import ILocalLibrary from "../api/types/ILocalLibrary.ts"; -import {GetLibraries} from "../api/LocalLibrary.tsx"; -import { LibraryBooks } from "@mui/icons-material"; - -export default function Search({open, setOpen}:{open:boolean, setOpen:React.Dispatch>}){ - - const [step, setStep] = useState(2); - - const apiUri = useContext(ApiUriContext); - const [mangaConnectors, setMangaConnectors] = useState(); - const [mangaConnectorsLoading, setMangaConnectorsLoading] = useState(true); - const [selectedMangaConnector, setSelectedMangaConnector] = useState(); - - const loadMangaConnectors = useCallback(() => { - setMangaConnectorsLoading(true); - GetAllConnectors(apiUri).then(setMangaConnectors).finally(() => setMangaConnectorsLoading(false)); - }, [apiUri]); - - const [results, setResults] = useState([]); - const [resultsLoading, setResultsLoading] = useState(false); - - const StartSearch = useCallback((mangaConnector : IMangaConnector | undefined, value: string)=>{ - if(mangaConnector === undefined && !IsValidUrl(value)) - return; - setResults(undefined); - setResultsLoading(true); - setStep(3); - if (IsValidUrl(value)){ - SearchUrl(apiUri, value).then((r) => setResults([r])).finally(() => setResultsLoading(false)); - }else if (mangaConnector != undefined){ - SearchNameOnConnector(apiUri, mangaConnector.name, value).then(setResults).finally(() => setResultsLoading(false)); - } - },[apiUri]) - - function IsValidUrl(str : string) : boolean { - const pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name - '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path - '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string - '(\\#[-a-z\\d_]*)?$','i'); // fragment locator - return !!pattern.test(str); - } - - const [localLibraries, setLocalLibraries] = useState(); - const [localLibrariesLoading, setLocalLibrariesLoading] = useState(true); - const [selectedLibraryId, setSelectedLibraryId] = useState(); - - const loadLocalLibraries = useCallback(() => { - setLocalLibrariesLoading(true); - GetLibraries(apiUri).then(setLocalLibraries).finally(() => setLocalLibrariesLoading(false)); - }, [apiUri]); - - useEffect(() => { - loadMangaConnectors(); - loadLocalLibraries(); - },[apiUri]); - - useEffect(() => { - loadMangaConnectors(); - loadLocalLibraries(); - }, []); - - function renderValue(option: SelectOption | null) { - if (!option) { - return null; - } - - return ( - - - o.name === option.value)?.iconUrl} /> - - {option.label} - - ); - } - - // @ts-ignore - return ( - { - if(step > 2) - setStep(2); - setResults([]); - setOpen(false); - }}> - - - - 1 - }> - - - - - - 2 - }> - { - setStep(2); - setResults(undefined); - if(e.key === "Enter") { - StartSearch(selectedMangaConnector, e.currentTarget.value); - } - }}/> - - - 3 - }> - {results?.length??"-"}}>Results - - - {results?.map((result) => - - - - )} - - - - - - ); -} - -function ConnectorOption(connector: IMangaConnector){ - return ( - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Settings/AprilFoolsMode.tsx b/tranga-website/src/Components/Settings/AprilFoolsMode.tsx deleted file mode 100644 index c678372..0000000 --- a/tranga-website/src/Components/Settings/AprilFoolsMode.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import IBackendSettings from "../../api/types/IBackendSettings.ts"; -import {useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "../../api/fetchApi.tsx"; -import { - Accordion, - AccordionDetails, - AccordionSummary, - ColorPaletteProp, - Switch, - Typography -} from "@mui/joy"; -import * as React from "react"; -import {GetAprilFoolsToggle, UpdateAprilFoolsToggle} from "../../api/BackendSettings.tsx"; - -export default function ImageProcessing({backendSettings}: {backendSettings?: IBackendSettings}) { - const apiUri = useContext(ApiUriContext); - - const [loading, setLoading] = useState(false); - const [color, setColor] = useState("neutral"); - const [value, setValue] = useState(backendSettings?.aprilFoolsMode??false); - - const timerRef = React.useRef>(undefined); - const valueChanged = (e : React.ChangeEvent) => { - setColor("warning"); - clearTimeout(timerRef.current); - timerRef.current = setTimeout(() => { - UpdateAprilFoolsMode(e.target.checked); - }, 1000); - } - - useEffect(() => { - setValue(backendSettings?.aprilFoolsMode??false); - }, [backendSettings]); - - const UpdateAprilFoolsMode = useCallback((val: boolean) => { - UpdateAprilFoolsToggle(apiUri, val) - .then(() => GetAprilFoolsToggle(apiUri)) - .then((val) => setValue(val)) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - }, [apiUri]); - - return ( - - April Fools Mode - - - }> - Toggle - - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx b/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx deleted file mode 100644 index 716901d..0000000 --- a/tranga-website/src/Components/Settings/ChapterNamingScheme.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import IBackendSettings from "../../api/types/IBackendSettings"; -import { - Accordion, - AccordionDetails, - AccordionSummary, Chip, - CircularProgress, - ColorPaletteProp, - Divider, - Input, - Stack, Tooltip, Typography -} from "@mui/joy"; -import {KeyboardEventHandler, useCallback, useContext, useState} from "react"; -import {ApiUriContext} from "../../api/fetchApi.tsx"; -import {UpdateChapterNamingScheme} from "../../api/BackendSettings.tsx"; - -export default function ChapterNamingScheme({backendSettings}: {backendSettings?: IBackendSettings}) { - const apiUri = useContext(ApiUriContext); - const [loading, setLoading] = useState(false); - const [value, setValue] = useState(""); - const [color, setColor] = useState("neutral"); - - const keyDown : KeyboardEventHandler = useCallback((e) => { - if(e.key === "Enter") { - setLoading(true); - UpdateChapterNamingScheme(apiUri, value) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - } - }, [apiUri]) - - return ( - - Chapter Naming Scheme - - setValue(e.target.value)} - color={color} - endDecorator={(loading ? : null)} - /> - Placeholders: - }> - - %M - - - %V - - - %C - - - %T - - - %Y - - - %A - - - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Settings/FlareSolverr.tsx b/tranga-website/src/Components/Settings/FlareSolverr.tsx deleted file mode 100644 index aa62cee..0000000 --- a/tranga-website/src/Components/Settings/FlareSolverr.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import IBackendSettings from "../../api/types/IBackendSettings"; -import { - Accordion, - AccordionDetails, - AccordionSummary, - Button, - ColorPaletteProp, - Input, Stack -} from "@mui/joy"; -import {KeyboardEventHandler, useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "../../api/fetchApi.tsx"; -import { - ResetFlareSolverrUrl, - SetFlareSolverrUrl, TestFlareSolverrUrl, -} from "../../api/BackendSettings.tsx"; - -export default function FlareSolverr({backendSettings}: {backendSettings?: IBackendSettings}) { - const apiUri = useContext(ApiUriContext); - const [loading, setLoading] = useState(false); - const [value, setValue] = useState(backendSettings?.flareSolverrUrl??""); - const [color, setColor] = useState("neutral"); - - const keyDown : KeyboardEventHandler = useCallback((e) => { - if(value === undefined) return; - if(e.key === "Enter") { - setLoading(true); - SetFlareSolverrUrl(apiUri, value) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - } - }, [apiUri, value]) - - const Reset = useCallback(() => { - setLoading(true); - ResetFlareSolverrUrl(apiUri) - .then(() => Test()) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - }, [apiUri]); - - const Test = useCallback(() => { - setLoading(true); - TestFlareSolverrUrl(apiUri) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - }, [apiUri]); - - useEffect(() => { - setValue(backendSettings?.flareSolverrUrl??""); - }, [backendSettings]); - - return ( - - FlareSolverr - - setValue(e.target.value)} - color={color} - endDecorator={ - - - } - /> - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Settings/ImageProcessing.tsx b/tranga-website/src/Components/Settings/ImageProcessing.tsx deleted file mode 100644 index edc8bdb..0000000 --- a/tranga-website/src/Components/Settings/ImageProcessing.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import IBackendSettings from "../../api/types/IBackendSettings.ts"; -import {ChangeEvent, useCallback, useContext, useEffect, useRef, useState} from "react"; -import {ApiUriContext} from "../../api/fetchApi.tsx"; -import { - Accordion, - AccordionDetails, - AccordionSummary, ColorPaletteProp, Input, Stack, Switch, Typography, -} from "@mui/joy"; -import { - GetBWImageToggle, - GetImageCompressionValue, - UpdateBWImageToggle, - UpdateImageCompressionValue -} from "../../api/BackendSettings.tsx"; - -export default function ImageProcessing ({backendSettings}: { backendSettings?: IBackendSettings }) { - const apiUri = useContext(ApiUriContext); - - useEffect(() => { - setBwImages(backendSettings?.bwImages??false); - setCompression(backendSettings?.compression??100); - }, [backendSettings]); - - const [bwImages, setBwImages] = useState(backendSettings?.bwImages??false); - const [bwImagesLoading, setBwImagesLoading] = useState(false); - const [bwImagesColor, setBwImagesColor] = useState("neutral"); - const bwTimerRef = useRef>(undefined); - const bwValueChanged = (e : ChangeEvent) => { - setBwImages(e.target.checked); - setBwImagesColor("warning"); - clearTimeout(bwTimerRef.current); - bwTimerRef.current = setTimeout(() => { - UpdateBwImages(e.target.checked); - }, 1000); - } - const UpdateBwImages = useCallback((val : boolean) => { - setBwImagesLoading(true); - UpdateBWImageToggle(apiUri, val) - .then(() => GetBWImageToggle(apiUri)) - .then(setBwImages) - .then(() => setBwImagesColor("success")) - .catch(() => setBwImagesColor("danger")) - .finally(() => setBwImagesLoading(false)); - },[apiUri]); - - const [compression, setCompression] = useState(backendSettings?.compression??100); - const [compressionLoading, setCompressionLoading] = useState(false); - const [compressionColor, setCompressionColor] = useState("neutral"); - const compressionTimerRef = useRef>(undefined); - const compressionCheckedChanged = (e : ChangeEvent) => { - setCompressionColor("warning"); - if(!e.target.checked) - setCompression(100); - else - setCompression(50); - clearTimeout(compressionTimerRef.current); - bwTimerRef.current = setTimeout(() => { - UpdateImageCompression(e.target.checked ? 50 : 100); - }, 1000); - } - const compressionValueChanged = (e : ChangeEvent) => { - setCompressionColor("warning"); - setCompression(parseInt(e.target.value)); - clearTimeout(compressionTimerRef.current); - bwTimerRef.current = setTimeout(() => { - UpdateImageCompression(parseInt(e.target.value)); - }, 1000); - } - const UpdateImageCompression = useCallback((val : number) => { - setCompressionLoading(true); - UpdateImageCompressionValue(apiUri, val) - .then(() => GetImageCompressionValue(apiUri)) - .then(setCompression) - .then(() => setCompressionColor("success")) - .catch(() => setCompressionColor("danger")) - .finally(() => setCompressionLoading(false)); - },[apiUri]); - - return ( - - Image Processing - - - - }>B/W Images - - } /> - }>Compression - - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Components/Settings/RequestLimits.tsx b/tranga-website/src/Components/Settings/RequestLimits.tsx deleted file mode 100644 index c84e6b0..0000000 --- a/tranga-website/src/Components/Settings/RequestLimits.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import IBackendSettings from "../../api/types/IBackendSettings.ts"; -import {useCallback, useContext, useState} from "react"; -import {ApiUriContext} from "../../api/fetchApi.tsx"; -import { - Accordion, - AccordionDetails, - AccordionSummary, - Button, - ColorPaletteProp, - Input, - Stack, - Typography -} from "@mui/joy"; -import {RequestLimitType} from "../../api/types/EnumRequestLimitType.ts"; -import {ResetRequestLimit, ResetRequestLimits, UpdateRequestLimit} from "../../api/BackendSettings.tsx"; -import {Restore} from "@mui/icons-material"; - -export default function RequestLimits({backendSettings}: {backendSettings?: IBackendSettings}) { - const apiUri = useContext(ApiUriContext); - - const [color, setColor] = useState("neutral"); - const [loading, setLoading] = useState(false); - const Update = useCallback((target: HTMLInputElement, limit: RequestLimitType) => { - setLoading(true); - UpdateRequestLimit(apiUri, limit, Number.parseInt(target.value)) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - },[apiUri]) - - const Reset = useCallback((limit: RequestLimitType) => { - setLoading(true); - ResetRequestLimit(apiUri, limit) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - }, [apiUri]); - - const ResetAll = useCallback(() => { - setLoading(true); - ResetRequestLimits(apiUri) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - }, [apiUri]); - - return ( - - Request Limits - - - - - - - - - - - - ); -} - -function Item({type, color, loading, backendSettings, Reset, Update}: - {type: RequestLimitType, color: ColorPaletteProp, loading: boolean, backendSettings: IBackendSettings | undefined, Reset: (x: RequestLimitType) => void, Update: (a: HTMLInputElement, x: RequestLimitType) => void}) { - return ( - {type}} - endDecorator={} - disabled={loading} type={"number"} - defaultValue={backendSettings?.requestLimits[type]} - placeholder={"Default"} - required - onKeyDown={(e) => { - if(e.key == "Enter") - Update(e.target as HTMLInputElement, type); - }} - /> - ); -} \ No newline at end of file diff --git a/tranga-website/src/Settings.css b/tranga-website/src/Components/Settings/Settings.css similarity index 100% rename from tranga-website/src/Settings.css rename to tranga-website/src/Components/Settings/Settings.css diff --git a/tranga-website/src/Components/Settings/Settings.tsx b/tranga-website/src/Components/Settings/Settings.tsx new file mode 100644 index 0000000..11a12a0 --- /dev/null +++ b/tranga-website/src/Components/Settings/Settings.tsx @@ -0,0 +1,74 @@ +import Drawer from '@mui/joy/Drawer'; +import ModalClose from '@mui/joy/ModalClose'; +import { + Accordion, + AccordionDetails, + AccordionGroup, + AccordionSummary, Button, ColorPaletteProp, + DialogContent, + DialogTitle, Input, + Link, Stack +} from "@mui/joy"; +import './Settings.css'; +import * as React from "react"; +import {createContext, Dispatch, useContext, useEffect, useState} from "react"; +import {Article} from '@mui/icons-material'; +import {TrangaSettings} from "../../apiClient/data-contracts.ts"; +import {ApiContext} from "../../apiClient/ApiContext.tsx"; + +export const SettingsContext = createContext({}); + +export default function Settings({setApiUri} : {setApiUri: Dispatch>}) { + const Api = useContext(ApiContext); + const [settings, setSettings] = useState({}); + + const [open, setOpen] = React.useState(false); + + const [apiUriColor, setApiUriColor] = useState("neutral"); + const timerRef = React.useRef>(undefined); + + const [apiUriAccordionOpen, setApiUriAccordionOpen] = React.useState(true); + + useEffect(() => { + Api.settingsList().then((response) => { + setSettings(response.data) + }); + }, []); + + const apiUriChanged = (e : React.ChangeEvent) => { + clearTimeout(timerRef.current); + setApiUriColor("warning"); + timerRef.current = setTimeout(() => { + setApiUri(e.target.value); + setApiUriColor("success"); + }, 1000); + } + + return ( + + + setOpen(false)}> + + Settings + + + setApiUriAccordionOpen(expanded)}> + ApiUri + + + + + + +
Swagger Doc + + + + + ); +} \ No newline at end of file diff --git a/tranga-website/src/Components/Settings/UserAgent.tsx b/tranga-website/src/Components/Settings/UserAgent.tsx deleted file mode 100644 index 684b789..0000000 --- a/tranga-website/src/Components/Settings/UserAgent.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import IBackendSettings from "../../api/types/IBackendSettings"; -import { - Accordion, - AccordionDetails, - AccordionSummary, - Button, - ColorPaletteProp, - Input -} from "@mui/joy"; -import {KeyboardEventHandler, useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "../../api/fetchApi.tsx"; -import {GetUserAgent, ResetUserAgent, UpdateUserAgent} from "../../api/BackendSettings.tsx"; - -export default function UserAgent({backendSettings}: {backendSettings?: IBackendSettings}) { - const apiUri = useContext(ApiUriContext); - const [loading, setLoading] = useState(false); - const [value, setValue] = useState(backendSettings?.userAgent??""); - const [color, setColor] = useState("neutral"); - - const keyDown : KeyboardEventHandler = useCallback((e) => { - if(value === undefined) return; - if(e.key === "Enter") { - setLoading(true); - UpdateUserAgent(apiUri, value) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - } - }, [apiUri, value]) - - const Reset = useCallback(() => { - setLoading(true); - ResetUserAgent(apiUri) - .then(() => GetUserAgent(apiUri)) - .then((val) => setValue(val)) - .then(() => setColor("success")) - .catch(() => setColor("danger")) - .finally(() => setLoading(false)); - }, [apiUri]); - - useEffect(() => { - setValue(backendSettings?.userAgent??""); - }, [backendSettings]); - - return ( - - UserAgent - - setValue(e.target.value)} - color={color} - endDecorator={} - /> - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/Settings.tsx b/tranga-website/src/Settings.tsx deleted file mode 100644 index 54bf86f..0000000 --- a/tranga-website/src/Settings.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import Drawer from '@mui/joy/Drawer'; -import ModalClose from '@mui/joy/ModalClose'; -import { - Accordion, - AccordionDetails, - AccordionGroup, - AccordionSummary, CircularProgress, ColorPaletteProp, - DialogContent, - DialogTitle, Input, - Link, Stack -} from "@mui/joy"; -import './Settings.css'; -import * as React from "react"; -import {useCallback, useContext, useEffect, useState} from "react"; -import {ApiUriContext} from "./api/fetchApi.tsx"; -import IBackendSettings from "./api/types/IBackendSettings.ts"; -import { GetSettings } from './api/BackendSettings.tsx'; -import UserAgent from "./Components/Settings/UserAgent.tsx"; -import ImageProcessing from "./Components/Settings/ImageProcessing.tsx"; -import ChapterNamingScheme from "./Components/Settings/ChapterNamingScheme.tsx"; -import AprilFoolsMode from './Components/Settings/AprilFoolsMode.tsx'; -import RequestLimits from "./Components/Settings/RequestLimits.tsx"; -import FlareSolverr from "./Components/Settings/FlareSolverr.tsx"; -import {Article} from '@mui/icons-material'; - -const checkConnection = async (apiUri: string): Promise =>{ - return fetch(`${apiUri}/swagger/v2/swagger.json`, - { - method: 'GET', - }) - .then((response) => { - if(!(response.ok && response.status == 200)) - return false; - return response.json().then((json) => (json as {openapi:string}).openapi.match("[0-9]+(?:\.[0-9]+)+")?true:false).catch(() => false); - }) - .catch(() => { - return false; - }); -} - -export default function Settings({open, setOpen, setApiUri, setConnected}:{open:boolean, setOpen:React.Dispatch>, setApiUri:React.Dispatch>, setConnected:React.Dispatch>}) { - - const apiUri = useContext(ApiUriContext); - - const [apiUriColor, setApiUriColor] = useState("neutral"); - const timerRef = React.useRef>(undefined); - - const [apiUriAccordionOpen, setApiUriAccordionOpen] = React.useState(true); - const [checking, setChecking] = useState(false); - - useEffect(() => { - OnCheckConnection(apiUri); - }, []); - - const apiUriChanged = (e : React.ChangeEvent) => { - clearTimeout(timerRef.current); - setApiUriColor("warning"); - timerRef.current = setTimeout(() => { - OnCheckConnection(e.target.value); - }, 1000); - } - - const OnCheckConnection = (uri: string) => { - console.log("Checking connection..."); - setChecking(true); - checkConnection(uri) - .then((result) => { - setConnected(result); - if(result) - console.log("Connected!"); - setApiUriAccordionOpen(!result); - setApiUriColor(result ? "success" : "danger"); - if(result) - setApiUri(uri); - }) - .finally(() => setChecking(false)); - } - - const [backendSettings, setBackendSettings] = useState(); - - const getBackendSettings = useCallback(() => { - GetSettings(apiUri).then(setBackendSettings); - }, [apiUri]); - - useEffect(() => { - getBackendSettings(); - }, [checking]); - - return ( - setOpen(false)}> - - Settings - - - setApiUriAccordionOpen(expanded)}> - ApiUri - - { - if(e.key === "Enter") { - clearTimeout(timerRef.current); - OnCheckConnection(e.currentTarget.value); - } - }} - endDecorator={(checking ? : null)} /> - - - - - - - - - - -
Swagger Doc - - - - ); -} \ No newline at end of file diff --git a/tranga-website/src/api/BackendSettings.tsx b/tranga-website/src/api/BackendSettings.tsx deleted file mode 100644 index 05cfdd0..0000000 --- a/tranga-website/src/api/BackendSettings.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import {deleteData, getData, patchData, postData} from './fetchApi.tsx'; -import IBackendSettings from "./types/IBackendSettings.ts"; -import IRequestLimits from "./types/IRequestLimits.ts"; -import {RequestLimitType} from "./types/EnumRequestLimitType.ts"; - -export const GetSettings = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings`) as Promise; -} - -export const GetUserAgent = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings/UserAgent`) as Promise; -} - -export const UpdateUserAgent = async (apiUri: string, userAgent: string)=> { - if(userAgent === undefined || userAgent === null) - return Promise.reject(`userAgent was not provided`); - return patchData(`${apiUri}/v2/Settings/UserAgent`, userAgent); -} - -export const ResetUserAgent = async (apiUri: string) => { - return deleteData(`${apiUri}/v2/Settings/UserAgent`); -} - -export const GetRequestLimits = async(apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings/RequestLimits`) as Promise; -} - -export const ResetRequestLimits = async (apiUri: string) => { - return deleteData(`${apiUri}/v2/Settings/RequestLimits`); -} - -export const UpdateRequestLimit = async (apiUri: string, requestType: RequestLimitType, value: number) => { - if(requestType === undefined || requestType === null || value === undefined || value === null) - return Promise.reject(); - return patchData(`${apiUri}/v2/Settings/RequestLimits/${requestType}`, value); -} - -export const ResetRequestLimit = async (apiUri: string, requestType: RequestLimitType) => { - if(requestType === undefined || requestType === null) - return Promise.reject("requestType was not provided"); - return deleteData(`${apiUri}/v2/Settings/RequestLimits/${requestType}`); -} - -export const GetImageCompressionValue = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings/ImageCompression`) as Promise; -} - -export const UpdateImageCompressionValue = async (apiUri: string, value: number) => { - if(value === undefined || value === null) - return Promise.reject("value was not provided"); - return patchData(`${apiUri}/v2/Settings/ImageCompression`, value); -} - -export const GetBWImageToggle = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings/BWImages`) as Promise; -} - -export const UpdateBWImageToggle = async (apiUri: string, value: boolean) => { - if(value === undefined || value === null) - return Promise.reject("value was not provided"); - return patchData(`${apiUri}/v2/Settings/BWImages`, value); -} - -export const GetAprilFoolsToggle = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings/AprilFoolsMode`) as Promise; -} - -export const UpdateAprilFoolsToggle = async (apiUri: string, value: boolean) => { - if(value === undefined || value === null) - return Promise.reject("value was not provided"); - return patchData(`${apiUri}/v2/Settings/AprilFoolsMode`, value); -} - -export const GetChapterNamingScheme = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Settings/ChapterNamingScheme`) as Promise; -} - -export const UpdateChapterNamingScheme = async (apiUri: string, value: string) => { - return patchData(`${apiUri}/v2/Settings/ChapterNamingScheme`, value); -} - -export const SetFlareSolverrUrl = async (apiUri: string, value: string) => { - return postData(`${apiUri}/v2/Settings/FlareSolverr/Url`, value); -} - -export const ResetFlareSolverrUrl = async (apiUri: string) => { - return deleteData(`${apiUri}/v2/Settings/FlareSolverr/Url`); -} - -export const TestFlareSolverrUrl = async (apiUri: string) => { - return postData(`${apiUri}/v2/Settings/FlareSolverr/Test`); -} \ No newline at end of file diff --git a/tranga-website/src/api/Chapter.tsx b/tranga-website/src/api/Chapter.tsx deleted file mode 100644 index 73792eb..0000000 --- a/tranga-website/src/api/Chapter.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import {getData} from "./fetchApi.tsx"; -import IChapter from "./types/IChapter.ts"; - -export const GetChapterFromId = async (apiUri: string, chapterId: string): Promise => { - if(chapterId === undefined || chapterId === null) - return Promise.reject(`chapterId was not provided`); - return await getData(`${apiUri}/v2/Query/Chapter/${chapterId}`) as Promise; -} \ No newline at end of file diff --git a/tranga-website/src/api/Contexts/ChapterContext.tsx b/tranga-website/src/api/Contexts/ChapterContext.tsx deleted file mode 100644 index 4023eac..0000000 --- a/tranga-website/src/api/Contexts/ChapterContext.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import {createContext} from "react"; -import IChapter from "../types/IChapter.ts"; - -export const ChapterContext = createContext<{chapters: IChapter[], GetChapter: (chapterId: string) => Promise}>( - { - chapters : [], - GetChapter: _ => Promise.resolve(undefined) - } -); \ No newline at end of file diff --git a/tranga-website/src/api/Contexts/MangaConnectorContext.tsx b/tranga-website/src/api/Contexts/MangaConnectorContext.tsx deleted file mode 100644 index b830365..0000000 --- a/tranga-website/src/api/Contexts/MangaConnectorContext.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import {createContext} from "react"; -import IMangaConnector from "../types/IMangaConnector.ts"; - -export const MangaConnectorContext = createContext([]); \ No newline at end of file diff --git a/tranga-website/src/api/Contexts/MangaContext.tsx b/tranga-website/src/api/Contexts/MangaContext.tsx deleted file mode 100644 index ec7b19e..0000000 --- a/tranga-website/src/api/Contexts/MangaContext.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import {createContext} from "react"; -import IManga, {DefaultManga} from "../types/IManga.ts"; - -export const MangaContext = createContext<{mangas: IManga[], GetManga: (mangaId: string) => Promise}>( - { - mangas : [], - GetManga: _ => Promise.resolve(DefaultManga) - } -); \ No newline at end of file diff --git a/tranga-website/src/api/Job.tsx b/tranga-website/src/api/Job.tsx deleted file mode 100644 index db1874e..0000000 --- a/tranga-website/src/api/Job.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import {deleteData, getData, patchData, postData, putData} from "./fetchApi"; -import IJob, {JobState, JobType} from "./types/Jobs/IJob"; -import IModifyJobRecord from "./types/records/IModifyJobRecord"; -import IDownloadAvailableChaptersJobRecord from "./types/records/IDownloadAvailableChaptersJobRecord.ts"; - -export const GetAllJobs = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Job`) as Promise; -} - -export const GetJobsWithIds = async (apiUri: string, jobIds: string[]) : Promise => { - if(jobIds === null || jobIds === undefined || jobIds.length === 0) - return Promise.reject("jobIds was not provided"); - return await postData(`${apiUri}/v2/Job/WithIDs`, jobIds) as Promise; -} - -export const GetJobsInState = async (apiUri: string, state: JobState) : Promise => { - if(state == null) - return Promise.reject("state was not provided"); - return await getData(`${apiUri}/v2/Job/State/${state}`) as Promise; -} - -export const GetJobsWithType = async (apiUri: string, jobType: JobType) : Promise => { - if(jobType == null) { - return Promise.reject("jobType was not provided"); - } - return await getData(`${apiUri}/v2/Job/Type/${jobType}`) as Promise; -} - -export const GetJobsOfTypeAndWithState = async (apiUri: string, jobType: JobType, state: JobState) : Promise => { - if(jobType == null) - return Promise.reject("jobType was not provided"); - if(state == null) - return Promise.reject("state was not provided"); - return await getData(`${apiUri}/v2/Job/TypeAndState/${jobType}/${state}`) as Promise; -} - -export const GetJob = async (apiUri: string, jobId: string) : Promise => { - if(jobId === undefined || jobId === null || jobId.length < 1) - return Promise.reject("jobId was not provided"); - return await getData(`${apiUri}/v2/Job/${jobId}`) as Promise; -} - -export const DeleteJob = async (apiUri: string, jobId: string) : Promise => { - if(jobId === undefined || jobId === null || jobId.length < 1) - return Promise.reject("jobId was not provided"); - return await deleteData(`${apiUri}/v2/Job/${jobId}`); -} - -export const ModifyJob = async (apiUri: string, jobId: string, modifyData: IModifyJobRecord) : Promise => { - if(jobId === undefined || jobId === null || jobId.length < 1) - return Promise.reject("jobId was not provided"); - if(modifyData === undefined || modifyData === null) - return Promise.reject("modifyData was not provided"); - return await patchData(`${apiUri}/v2/Job/${jobId}`, modifyData) as Promise; -} - -export const CreateDownloadAvailableChaptersJob = async (apiUri: string, mangaId: string, data: IDownloadAvailableChaptersJobRecord) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(data === undefined || data === null) - return Promise.reject("data was not provided"); - return await putData(`${apiUri}/v2/Job/DownloadAvailableChaptersJob/${mangaId}`, data) as Promise; -} - -export const CreateDownloadSingleChapterJob = async (apiUri: string, chapterId: string) : Promise => { - if(chapterId === undefined || chapterId === null || chapterId.length < 1) - return Promise.reject("chapterId was not provided"); - return await putData(`${apiUri}/v2/Job/DownloadSingleChapterJob/${chapterId}`, {}) as Promise; -} - -export const CreateUpdateFilesJob = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - return await putData(`${apiUri}/v2/Job/UpdateFilesJob/${mangaId}`, {}) as Promise; -} - -export const CreateUpdateAllFilesJob = async (apiUri: string) : Promise => { - return await putData(`${apiUri}/v2/Job/UpdateAllFilesJob`, {}) as Promise; -} - -export const CreateUpdateMetadataJob = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - return await putData(`${apiUri}/v2/Job/UpdateMetadataJob/${mangaId}`, {}) as Promise; -} - -export const CreateUpdateAllMetadataJob = async (apiUri: string) : Promise => { - return await putData(`${apiUri}/v2/Job/UpdateAllMetadataJob`, {}) as Promise; -} - -export const StartJob = async (apiUri: string, jobId: string, startDependencies: boolean) : Promise => { - return await postData(`${apiUri}/v2/Job/${jobId}/Start`, startDependencies); -} - -export const StopJob = async (apiUri: string, jobId: string) : Promise => { - return await postData(`${apiUri}/v2/Job/${jobId}/Stop`); -} \ No newline at end of file diff --git a/tranga-website/src/api/LocalLibrary.tsx b/tranga-website/src/api/LocalLibrary.tsx deleted file mode 100644 index 3258972..0000000 --- a/tranga-website/src/api/LocalLibrary.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import ILocalLibrary from "./types/ILocalLibrary.ts"; -import {deleteData, getData, patchData, putData} from "./fetchApi.tsx"; -import INewLibraryRecord from "./types/records/INewLibraryRecord.ts"; - -export const GetLibraries = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/LocalLibraries`) as Promise; -} - -export const GetLibrary = async (apiUri: string, libraryId: string) : Promise => { - return await getData(`${apiUri}/v2/LocalLibraries/${libraryId}`) as Promise; -} - -export const CreateLibrary = async (apiUri: string, data: INewLibraryRecord) : Promise => { - return await putData(`${apiUri}/v2/LocalLibraries`, data) as Promise -} - -export const DeleteLibrary = async (apiUri: string, libraryId: string) : Promise => { - return await deleteData(`${apiUri}/v2/LocalLibraries/${libraryId}`); -} - -export const ChangeLibraryPath = async (apiUri: string, libraryId: string, newPath: string) : Promise => { - return await patchData(`${apiUri}/v2/LocalLibraries/${libraryId}/ChangeBasePath`, newPath); -} - -export const ChangeLibraryName = async (apiUri: string, libraryId: string, newName: string) : Promise => { - return await patchData(`${apiUri}/v2/LocalLibraries/${libraryId}/ChangeName`, newName); -} - -export const UpdateLibrary = async (apiUri: string, libraryId: string, record: INewLibraryRecord) : Promise => { - return await patchData(`${apiUri}/v2/LocalLibraries/${libraryId}`, record); -} \ No newline at end of file diff --git a/tranga-website/src/api/Manga.tsx b/tranga-website/src/api/Manga.tsx deleted file mode 100644 index 92bdc08..0000000 --- a/tranga-website/src/api/Manga.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import {deleteData, getData, patchData, postData} from './fetchApi.tsx'; -import IManga, {DefaultManga} from "./types/IManga.ts"; -import IChapter from "./types/IChapter.ts"; - -export const GetAllManga = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/Manga`) as Promise; -} - -export const GetMangaWithIds = async (apiUri: string, mangaIds: string[]) : Promise => { - if(mangaIds === undefined || mangaIds === null || mangaIds.length < 1) - return Promise.reject("mangaIds was not provided"); - return await postData(`${apiUri}/v2/Manga/WithIds`, mangaIds) as Promise; -} - -export const GetMangaById = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await getData(`${apiUri}/v2/Manga/${mangaId}`) as Promise; -} - -export const DeleteManga = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await deleteData(`${apiUri}/v2/Manga/${mangaId}`); -} - -export const GetMangaCoverImageUrl = (apiUri: string, mangaId: string, ref: HTMLImageElement | undefined | null) : string => { - if(ref == null || mangaId === DefaultManga.mangaId) - return "/blahaj.png"; - return `${apiUri}/v2/Manga/${mangaId}/Cover?width=${ref.clientWidth}&height=${ref.clientHeight}`; -} - -export const GetChapters = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await getData(`${apiUri}/v2/Manga/${mangaId}/Chapters`) as Promise; -} - -export const GetDownloadedChapters = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await getData(`${apiUri}/v2/Manga/${mangaId}/Chapters/Downloaded`) as Promise; -} - -export const GetNotDownloadedChapters = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await getData(`${apiUri}/v2/Manga/${mangaId}/Chapters/NotDownloaded`) as Promise; -} - -export const GetLatestChapterAvailable = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await getData(`${apiUri}/v2/Manga/${mangaId}/Chapter/LatestAvailable`) as Promise; -} - -export const GetLatestChapterDownloaded = async (apiUri: string, mangaId: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await getData(`${apiUri}/v2/Manga/${mangaId}/Chapter/LatestDownloaded`) as Promise; -} - -export const SetIgnoreThreshold = async (apiUri: string, mangaId: string, chapterThreshold: number) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(chapterThreshold === undefined || chapterThreshold === null) - return Promise.reject("chapterThreshold was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await patchData(`${apiUri}/v2/Manga/${mangaId}/IgnoreChaptersBefore`, chapterThreshold); -} - -export const MoveFolder = async (apiUri: string, mangaId: string, newPath: string) : Promise => { - if(mangaId === undefined || mangaId === null || mangaId.length < 1) - return Promise.reject("mangaId was not provided"); - if(newPath === undefined || newPath === null || newPath.length < 1) - return Promise.reject("newPath was not provided"); - if(mangaId === DefaultManga.mangaId) - return Promise.reject("Default Manga was requested"); - return await postData(`${apiUri}/v2/Manga/{MangaId}/MoveFolder`, {newPath}); -} \ No newline at end of file diff --git a/tranga-website/src/api/MangaConnector.tsx b/tranga-website/src/api/MangaConnector.tsx deleted file mode 100644 index d05b43c..0000000 --- a/tranga-website/src/api/MangaConnector.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import {getData, patchData} from './fetchApi.tsx'; -import IMangaConnector from "./types/IMangaConnector.ts"; - -export const GetAllConnectors = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/MangaConnector`) as Promise -} - -export const GetConnector = async (apiUri: string, mangaConnectorName: string) : Promise => { - return await getData(`${apiUri}/v2/MangaConnector/${mangaConnectorName}`) as Promise; -} - -export const GetEnabledConnectors = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/MangaConnector/enabled`) as Promise -} - -export const GetDisabledConnectors = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/MangaConnector/disabled`) as Promise -} - -export const SetConnectorEnabled = async (apiUri: string, connectorName: string, enabled: boolean) : Promise => { - if(connectorName === undefined || connectorName === null || connectorName.length < 1) - return Promise.reject("connectorName was not provided"); - if(enabled === undefined || enabled === null) - return Promise.reject("enabled was not provided"); - return await patchData(`${apiUri}/v2/MangaConnector/${connectorName}/SetEnabled/${enabled}`, {}); -} \ No newline at end of file diff --git a/tranga-website/src/api/NotificationConnector.tsx b/tranga-website/src/api/NotificationConnector.tsx deleted file mode 100644 index 45a054f..0000000 --- a/tranga-website/src/api/NotificationConnector.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import {deleteData, getData, putData} from "./fetchApi.tsx"; -import INotificationConnector from "./types/INotificationConnector.ts"; -import IGotifyRecord from "./types/records/IGotifyRecord.ts"; -import INtfyRecord from "./types/records/INtfyRecord.ts"; -import IPushoverRecord from "./types/records/IPushoverRecord.ts"; - -export const GetNotificationConnectors = async (apiUri: string) : Promise => { - return await getData(`${apiUri}/v2/NotificationConnector`) as Promise -} - -export const CreateNotificationConnector = async (apiUri: string, newConnector: INotificationConnector) : Promise => { - if(newConnector === undefined || newConnector === null) - return Promise.reject("newConnector was not provided"); - return await putData(`${apiUri}/v2/NotificationConnector`, newConnector) as Promise; -} - -export const GetNotificationConnectorWithId = async (apiUri: string, notificationConnectorId: string) : Promise => { - if(notificationConnectorId === undefined || notificationConnectorId === null || notificationConnectorId.length < 1) - return Promise.reject("notificationConnectorId was not provided"); - return await getData(`${apiUri}/v2/NotificationConnector/${notificationConnectorId}`) as Promise; -} - -export const DeleteNotificationConnectorWithId = async (apiUri: string, notificationConnectorId: string) : Promise => { - if(notificationConnectorId === undefined || notificationConnectorId === null || notificationConnectorId.length < 1) - return Promise.reject("notificationConnectorId was not provided"); - return await deleteData(`${apiUri}/v2/NotificationConnector/${notificationConnectorId}`); -} - -export const CreateGotify = async (apiUri: string, gotify: IGotifyRecord) : Promise => { - if(gotify === undefined || gotify === null) - return Promise.reject("gotify was not provided"); - return await putData(`${apiUri}/v2/NotificationConnector/Gotify`, gotify) as Promise; -} - -export const CreateNtfy = async (apiUri: string, ntfy: INtfyRecord) : Promise => { - if(ntfy === undefined || ntfy === null) - return Promise.reject("gotify was not provided"); - return await putData(`${apiUri}/v2/NotificationConnector/Ntfy`, ntfy) as Promise; -} - -export const CreatePushover = async (apiUri: string, pushover: IPushoverRecord) : Promise => { - if(pushover === undefined || pushover === null) - return Promise.reject("pushover was not provided"); - return await putData(`${apiUri}/v2/NotificationConnector/Pushover`, pushover) as Promise; -} \ No newline at end of file diff --git a/tranga-website/src/api/Query.tsx b/tranga-website/src/api/Query.tsx deleted file mode 100644 index d262ac5..0000000 --- a/tranga-website/src/api/Query.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import IAuthor from "./types/IAuthor.ts"; -import {getData} from "./fetchApi.tsx"; -import ILink from "./types/ILink.ts"; - -export const GetAuthor = async (apiUri: string, authorId: string) : Promise => { - if(authorId === undefined || authorId === null || authorId.length < 1) - return Promise.reject("authorId was not provided"); - return await getData(`${apiUri}/v2/Query/Author/${authorId}`) as Promise; -} - -export const GetLink = async (apiUri: string, linkId: string) : Promise => { - if(linkId === undefined || linkId === null || linkId.length < 1) - return Promise.reject("linkId was not provided"); - return await getData(`${apiUri}/v2/Query/Link/${linkId}`) as Promise; -} \ No newline at end of file diff --git a/tranga-website/src/api/Search.tsx b/tranga-website/src/api/Search.tsx deleted file mode 100644 index 4abf20f..0000000 --- a/tranga-website/src/api/Search.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import {getData, postData} from "./fetchApi.tsx"; -import IManga from "./types/IManga.ts"; - -export const SearchName = async (apiUri: string, name: string) : Promise => { - if(name === undefined || name === null || name.length < 1) - return Promise.reject("name was not provided"); - return await postData(`${apiUri}/v2/Search/Name`, name) as Promise; -} - -export const SearchNameOnConnector = async (apiUri: string, connectorName: string, name: string) : Promise => { - if(connectorName === undefined || connectorName === null || connectorName.length < 1) - return Promise.reject("connectorName was not provided"); - if(name === undefined || name === null || name.length < 1) - return Promise.reject("name was not provided"); - return await getData(`${apiUri}/v2/Search/${connectorName}/${name}`) as Promise; -} - -export const SearchUrl = async (apiUri: string, url: string) : Promise => { - if(url === undefined || url === null || url.length < 1) - return Promise.reject("name was not provided"); - return await postData(`${apiUri}/v2/Search/Url`, url) as Promise; -} \ No newline at end of file diff --git a/tranga-website/src/api/fetchApi.tsx b/tranga-website/src/api/fetchApi.tsx deleted file mode 100644 index 5d06f8f..0000000 --- a/tranga-website/src/api/fetchApi.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import {createContext} from "react"; - -export const ApiUriContext = createContext(""); - -export function getData(uri: string) : Promise { - return makeRequestWrapper("GET", uri, null); -} - -export function postData(uri: string, content?: object | string | number | boolean | null) : Promise { - return makeRequestWrapper("POST", uri, content); -} - -export function deleteData(uri: string) : Promise { - return makeRequestWrapper("DELETE", uri, null) as Promise; -} - -export function patchData(uri: string, content: object | string | number | boolean) : Promise { - return makeRequestWrapper("patch", uri, content); -} - -export function putData(uri: string, content: object | string | number | boolean) : Promise { - return makeRequestWrapper("PUT", uri, content); -} - -function makeRequestWrapper(method: string, uri: string, content?: object | string | number | null | boolean) : Promise{ - return makeRequest(method, uri, content) - .then((result) => result as Promise) - .catch((e) => { - console.warn(e); - return Promise.reject(e); - }); -} - -let currentlyRequestedEndpoints: string[] = []; -function makeRequest(method: string, uri: string, content?: object | string | number | null | boolean) : Promise { - const id = method + uri; - if(currentlyRequestedEndpoints.find(x => x == id) != undefined) - return Promise.reject(`DO NOT REPORT! Already requested: ${method} ${uri}`); - currentlyRequestedEndpoints.push(id); - return fetch(uri, - { - method: method, - headers : { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - body: content ? JSON.stringify(content) : null - }) - .then(function(response){ - if(!response.ok){ - if(response.status === 503){ - currentlyRequestedEndpoints.splice(currentlyRequestedEndpoints.indexOf(id), 1) - let retryHeaderVal = response.headers.get("Retry-After"); - let seconds = 10; - if(retryHeaderVal === null){ - return response.text().then(text => { - seconds = parseInt(text); - return new Promise(resolve => setTimeout(resolve, seconds * 1000)) - .then(() => { - return makeRequest(method, uri, content); - }); - }); - }else { - seconds = parseInt(retryHeaderVal); - return new Promise(resolve => setTimeout(resolve, seconds * 1000)) - .then(() => { - return makeRequest(method, uri, content); - }); - } - }else - throw new Error(response.statusText); - } - let json = response.json(); - return json.then((json) => json).catch(() => null); - }) - .catch(function(err : Error){ - console.error(`Error ${method}ing Data ${uri}\n${err}`); - return Promise.reject(); - }).finally(() => currentlyRequestedEndpoints.splice(currentlyRequestedEndpoints.indexOf(id), 1)); -} - -export function isValidUri(uri: string) : boolean{ - try { - new URL(uri); - return true; - } catch (err) { - return false; - } -} \ No newline at end of file diff --git a/tranga-website/src/api/types/EnumLibraryType.ts b/tranga-website/src/api/types/EnumLibraryType.ts deleted file mode 100644 index e69de29..0000000 diff --git a/tranga-website/src/api/types/EnumMangaReleaseStatus.ts b/tranga-website/src/api/types/EnumMangaReleaseStatus.ts deleted file mode 100644 index 586062b..0000000 --- a/tranga-website/src/api/types/EnumMangaReleaseStatus.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {ColorPaletteProp} from "@mui/joy"; - -export enum MangaReleaseStatus { - Continuing = "Continuing", - Completed = "Completed", - OnHiatus = "OnHiatus", - Cancelled = "Cancelled", - Unreleased = "Unreleased", -} - -export function ReleaseStatusToPalette(status: MangaReleaseStatus): ColorPaletteProp { - switch (status) { - case MangaReleaseStatus.Continuing: - return "success"; - case MangaReleaseStatus.Completed: - return "primary"; - case MangaReleaseStatus.Cancelled: - return "danger"; - case MangaReleaseStatus.Unreleased: - return "neutral"; - case MangaReleaseStatus.OnHiatus: - return "warning"; - } -} \ No newline at end of file diff --git a/tranga-website/src/api/types/EnumRequestLimitType.ts b/tranga-website/src/api/types/EnumRequestLimitType.ts deleted file mode 100644 index c742b5f..0000000 --- a/tranga-website/src/api/types/EnumRequestLimitType.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum RequestLimitType { - Default = "Default", - MangaDexFeed = "MangaDexFeed", - MangaImage = "MangaImage", - MangaCover = "MangaCover", - MangaDexImage = "MangaDexImage", - MangaInfo = "MangaInfo" -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IAuthor.ts b/tranga-website/src/api/types/IAuthor.ts deleted file mode 100644 index 2b54e17..0000000 --- a/tranga-website/src/api/types/IAuthor.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface IAuthor { - authorId: string; - authorName: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IBackendSettings.ts b/tranga-website/src/api/types/IBackendSettings.ts deleted file mode 100644 index 56e29e2..0000000 --- a/tranga-website/src/api/types/IBackendSettings.ts +++ /dev/null @@ -1,19 +0,0 @@ -export default interface IBackendSettings { - downloadLocation: string; - workingDirectory: string; - userAgent: string; - aprilFoolsMode: boolean; - requestLimits: { - Default: number, - MangaInfo: number, - MangaDexFeed: number, - MangaDexImage: number, - MangaImage: number, - MangaCover: number, - }; - compression: number; - bwImages: boolean; - startNewJobTimeoutMs: number; - chapterNamingScheme: string; - flareSolverrUrl: string; -} diff --git a/tranga-website/src/api/types/IChapter.ts b/tranga-website/src/api/types/IChapter.ts deleted file mode 100644 index e2f091a..0000000 --- a/tranga-website/src/api/types/IChapter.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default interface IChapter{ - chapterId: string; - parentMangaId: string; - volumeNumber: number | null; - chapterNumber: string; - url: string; - title: string | null; - fileName: string | null; - downloaded: boolean; - fullArchiveFilePath: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IFrontendSettings.ts b/tranga-website/src/api/types/IFrontendSettings.ts deleted file mode 100644 index 2c1c7c5..0000000 --- a/tranga-website/src/api/types/IFrontendSettings.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface IFrontendSettings { - jobInterval: Date; - apiUri: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/ILibraryConnector.ts b/tranga-website/src/api/types/ILibraryConnector.ts deleted file mode 100644 index a477475..0000000 --- a/tranga-website/src/api/types/ILibraryConnector.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default interface ILibraryConnector { - libraryConnectorId: string; - libraryType: LibraryType; - baseUrl: string; - auth: string; -} - -export enum LibraryType { - Komga = "Komga", - Kavita = "Kavita" -} \ No newline at end of file diff --git a/tranga-website/src/api/types/ILink.ts b/tranga-website/src/api/types/ILink.ts deleted file mode 100644 index 9a26546..0000000 --- a/tranga-website/src/api/types/ILink.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface ILink { - linkId: string; - linkProvider: string; - linkUrl: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/ILocalLibrary.ts b/tranga-website/src/api/types/ILocalLibrary.ts deleted file mode 100644 index 11f2bbc..0000000 --- a/tranga-website/src/api/types/ILocalLibrary.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface ILocalLibrary { - localLibraryId: string; - basePath: string; - libraryName: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IManga.ts b/tranga-website/src/api/types/IManga.ts deleted file mode 100644 index d34b3cb..0000000 --- a/tranga-website/src/api/types/IManga.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {MangaReleaseStatus} from "./EnumMangaReleaseStatus"; -import IAuthor from "./IAuthor.ts"; -import IMangaAltTitle from "./IMangaAltTitle.ts"; -import IMangaTag from "./IMangaTag.ts"; -import ILink from "./ILink.ts"; - -export default interface IManga{ - mangaId: string; - idOnConnectorSite: string; - name: string; - description: string; - websiteUrl: string; - releaseStatus: MangaReleaseStatus; - libraryId: string | null; - mangaConnectorName: string; - authors: IAuthor[] | null; - mangaTags: IMangaTag[] | null; - links: ILink[] | null; - altTitles: IMangaAltTitle[] | null; - ignoreChaptersBefore: number; - directoryName: string; - year: number | null; - originalLanguage: string | null; - chapterIds: string[] | null; -} - -export const DefaultManga : IManga = { - mangaId: "Loading", - idOnConnectorSite: "Loading", - name: "Loading", - description: "Loading", - websiteUrl: "", - releaseStatus: MangaReleaseStatus.Continuing, - libraryId: null, - mangaConnectorName: "Loading", - authors: null, - mangaTags: null, - links: null, - altTitles: null, - ignoreChaptersBefore: 0, - directoryName: "", - year: 1999, - originalLanguage: "en", - chapterIds: null -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IMangaAltTitle.ts b/tranga-website/src/api/types/IMangaAltTitle.ts deleted file mode 100644 index 2526df4..0000000 --- a/tranga-website/src/api/types/IMangaAltTitle.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface IMangaAltTitle { - altTitleId: string; - language: string; - title: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IMangaConnector.ts b/tranga-website/src/api/types/IMangaConnector.ts deleted file mode 100644 index 61fb457..0000000 --- a/tranga-website/src/api/types/IMangaConnector.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface IMangaConnector { - name: string; - supportedLanguages: string[]; - iconUrl: string; - baseUris: string[]; - enabled: boolean; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IMangaTag.ts b/tranga-website/src/api/types/IMangaTag.ts deleted file mode 100644 index 64aa37c..0000000 --- a/tranga-website/src/api/types/IMangaTag.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IMangaTag { - tag: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/INotificationConnector.ts b/tranga-website/src/api/types/INotificationConnector.ts deleted file mode 100644 index 8d2b765..0000000 --- a/tranga-website/src/api/types/INotificationConnector.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface INotificationConnector { - name: string; - url: string; - headers: Record[]; - httpMethod: string; - body: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/IRequestLimits.ts b/tranga-website/src/api/types/IRequestLimits.ts deleted file mode 100644 index 54a00ec..0000000 --- a/tranga-website/src/api/types/IRequestLimits.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default interface IRequestLimits { - Default: number; - MangaDexFeed: number; - MangaImage: number; - MangaCover: number; - MangaDexImage: number; - MangaInfo: number; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IDownloadAvailableChaptersJob.ts b/tranga-website/src/api/types/Jobs/IDownloadAvailableChaptersJob.ts deleted file mode 100644 index 9bb3097..0000000 --- a/tranga-website/src/api/types/Jobs/IDownloadAvailableChaptersJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithMangaId from "./IJobWithMangaId.ts"; - -export default interface IDownloadAvailableChaptersJob extends IJobWithMangaId { - -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IDownloadMangaCoverJob.ts b/tranga-website/src/api/types/Jobs/IDownloadMangaCoverJob.ts deleted file mode 100644 index f869988..0000000 --- a/tranga-website/src/api/types/Jobs/IDownloadMangaCoverJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithMangaId from "./IJobWithMangaId.ts"; - -export default interface IDownloadMangaCoverJob extends IJobWithMangaId { - -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IDownloadSingleChapterJob.ts b/tranga-website/src/api/types/Jobs/IDownloadSingleChapterJob.ts deleted file mode 100644 index 9add634..0000000 --- a/tranga-website/src/api/types/Jobs/IDownloadSingleChapterJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithChapterId from "./IJobWithChapterId.tsx"; - -export default interface IDownloadSingleChapterJob extends IJobWithChapterId { - -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IJob.ts b/tranga-website/src/api/types/Jobs/IJob.ts deleted file mode 100644 index e7ad426..0000000 --- a/tranga-website/src/api/types/Jobs/IJob.ts +++ /dev/null @@ -1,36 +0,0 @@ -export default interface IJob{ - jobId: string; - parentJobId: string | null; - jobType: JobType; - recurrenceMs: number; - lastExecution: Date; - nextExecution: Date; - state: JobState; - enabled: boolean; -} - -export enum JobType { - DownloadSingleChapterJob = "DownloadSingleChapterJob", - DownloadAvailableChaptersJob = "DownloadAvailableChaptersJob", - DownloadMangaCoverJob = "DownloadMangaCoverJob", - RetrieveChaptersJob = "RetrieveChaptersJob", - UpdateChaptersDownloadedJob = "UpdateChaptersDownloadedJob", - MoveMangaLibraryJob = "MoveMangaLibraryJob", - UpdateCoverJob = "UpdateCoverJob" -} - -export function JobTypeToString(job: JobType | string): string { - return job.replace(/([A-Z])/g, ' $1').replace("Job", "").trim(); -} - -export enum JobState { - FirstExecution = "FirstExecution", - Running = "Running", - Completed = "Completed", - CompletedWaiting = "CompletedWaiting", - Failed = "Failed" -} - -export function JobStateToString(state: JobState | string): string { - return state.replace(/([A-Z])/g, ' $1').trim(); -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IJobWithChapterId.tsx b/tranga-website/src/api/types/Jobs/IJobWithChapterId.tsx deleted file mode 100644 index a71a9e1..0000000 --- a/tranga-website/src/api/types/Jobs/IJobWithChapterId.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import IJob from "./IJob.ts"; - -export default interface IJobWithChapterId extends IJob { - chapterId: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IJobWithMangaId.ts b/tranga-website/src/api/types/Jobs/IJobWithMangaId.ts deleted file mode 100644 index 1e6147c..0000000 --- a/tranga-website/src/api/types/Jobs/IJobWithMangaId.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJob from "./IJob.ts"; - -export default interface IJobWithMangaId extends IJob { - mangaId: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IMoveFileOrFolderJob.ts b/tranga-website/src/api/types/Jobs/IMoveFileOrFolderJob.ts deleted file mode 100644 index 9daa55c..0000000 --- a/tranga-website/src/api/types/Jobs/IMoveFileOrFolderJob.ts +++ /dev/null @@ -1,6 +0,0 @@ -import IJob from "./IJob"; - -export default interface IMoveFileOrFolderJob extends IJob { - fromLocation: string; - toLocation: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IMoveMangaLibraryJob.ts b/tranga-website/src/api/types/Jobs/IMoveMangaLibraryJob.ts deleted file mode 100644 index d861777..0000000 --- a/tranga-website/src/api/types/Jobs/IMoveMangaLibraryJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithMangaId from "./IJobWithMangaId.ts"; - -export default interface IMoveMangaLibraryJob extends IJobWithMangaId { - ToLibraryId: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IRetrieveChaptersJob.ts b/tranga-website/src/api/types/Jobs/IRetrieveChaptersJob.ts deleted file mode 100644 index 6af1397..0000000 --- a/tranga-website/src/api/types/Jobs/IRetrieveChaptersJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithMangaId from "./IJobWithMangaId.ts"; - -export default interface IRetrieveChaptersJob extends IJobWithMangaId { - -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IUpdateChaptersDownloadedJob.ts b/tranga-website/src/api/types/Jobs/IUpdateChaptersDownloadedJob.ts deleted file mode 100644 index 8f1cff6..0000000 --- a/tranga-website/src/api/types/Jobs/IUpdateChaptersDownloadedJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithMangaId from "./IJobWithMangaId.ts"; - -export default interface IUpdateChaptersDownloadedJob extends IJobWithMangaId { - -} \ No newline at end of file diff --git a/tranga-website/src/api/types/Jobs/IUpdateCoverJob.ts b/tranga-website/src/api/types/Jobs/IUpdateCoverJob.ts deleted file mode 100644 index 296db2a..0000000 --- a/tranga-website/src/api/types/Jobs/IUpdateCoverJob.ts +++ /dev/null @@ -1,5 +0,0 @@ -import IJobWithMangaId from "./IJobWithMangaId.ts"; - -export default interface IUpdateCoverJob extends IJobWithMangaId { - -} \ No newline at end of file diff --git a/tranga-website/src/api/types/records/IDownloadAvailableChaptersJobRecord.ts b/tranga-website/src/api/types/records/IDownloadAvailableChaptersJobRecord.ts deleted file mode 100644 index e41ae9b..0000000 --- a/tranga-website/src/api/types/records/IDownloadAvailableChaptersJobRecord.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface IDownloadAvailableChaptersJobRecord { - language: string; - recurrenceTimeMs: number; - localLibraryId: string; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/records/IGotifyRecord.ts b/tranga-website/src/api/types/records/IGotifyRecord.ts deleted file mode 100644 index ffd66ae..0000000 --- a/tranga-website/src/api/types/records/IGotifyRecord.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface IGotifyRecord { - endpoint: string; - appToken: string; - priority: number; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/records/IModifyJobRecord.ts b/tranga-website/src/api/types/records/IModifyJobRecord.ts deleted file mode 100644 index 3241c9a..0000000 --- a/tranga-website/src/api/types/records/IModifyJobRecord.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface IModifyJobRecord { - recurrenceMs: number; - enabled: boolean; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/records/INewLibraryRecord.ts b/tranga-website/src/api/types/records/INewLibraryRecord.ts deleted file mode 100644 index 59a764b..0000000 --- a/tranga-website/src/api/types/records/INewLibraryRecord.ts +++ /dev/null @@ -1,12 +0,0 @@ -export default interface INewLibraryRecord { - path: string; - name: string; -} - -export function Validate(record: INewLibraryRecord) : boolean { - if(record.path.length < 1) - return false; - if(record.name.length < 1) - return false; - return true; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/records/INtfyRecord.ts b/tranga-website/src/api/types/records/INtfyRecord.ts deleted file mode 100644 index 9c67e0c..0000000 --- a/tranga-website/src/api/types/records/INtfyRecord.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface INtfyRecord { - endpoint: string; - username: string; - password: string; - topic: string; - priority: number; -} \ No newline at end of file diff --git a/tranga-website/src/api/types/records/IPushoverRecord.ts b/tranga-website/src/api/types/records/IPushoverRecord.ts deleted file mode 100644 index 52001f8..0000000 --- a/tranga-website/src/api/types/records/IPushoverRecord.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default interface IPushoverRecord { - apptoken: string; - user: string; -} \ No newline at end of file diff --git a/tranga-website/src/apiClient/ApiContext.tsx b/tranga-website/src/apiClient/ApiContext.tsx new file mode 100644 index 0000000..46ca069 --- /dev/null +++ b/tranga-website/src/apiClient/ApiContext.tsx @@ -0,0 +1,4 @@ +import { createContext } from "react"; +import {V2} from "./V2.ts"; + +export const ApiContext = createContext(new V2()); \ No newline at end of file diff --git a/tranga-website/src/apiClient/MangaContext.tsx b/tranga-website/src/apiClient/MangaContext.tsx new file mode 100644 index 0000000..9170b20 --- /dev/null +++ b/tranga-website/src/apiClient/MangaContext.tsx @@ -0,0 +1,34 @@ +import {createContext, useContext} from "react"; +import {Manga} from "./data-contracts.ts"; +import {ApiContext} from "./ApiContext.tsx"; + +const mangaPromises = new Map>(); +const mangas : Manga[] = []; + +export const GetManga : (id: string) => Promise = (id: string) => { + const API = useContext(ApiContext); + + const promise = mangaPromises.get(id); + if(promise) return promise; + const p = new Promise((resolve, reject) => { + let ret = mangas?.find(m => m.key == id); + if (ret) resolve(ret); + + console.log(`Fetching manga ${id}`); + API.mangaDetail(id) + .then(result => { + if (!result.ok) + throw new Error(`Error fetching manga detail ${id}`); + mangas.push(result.data); + resolve(result.data); + }).catch(reject); + }); + mangaPromises.set(id, p); + return p; +}; + +export const MangaContext = createContext<{ GetManga: (id: string) => Promise }>( + { + GetManga: GetManga + } +); diff --git a/tranga-website/src/apiClient/SettingsContext.tsx b/tranga-website/src/apiClient/SettingsContext.tsx new file mode 100644 index 0000000..dd2097b --- /dev/null +++ b/tranga-website/src/apiClient/SettingsContext.tsx @@ -0,0 +1,31 @@ +import {createContext, useContext, useState} from "react"; +import {TrangaSettings} from "./data-contracts.ts"; +import {ApiContext} from "./ApiContext.tsx"; + +const [settingsPromise, setSettingsPromise] = useState>(); +const [settings, setSettings] = useState(); + +const API = useContext(ApiContext); + +export const SettingsContext = createContext<{ GetSettings: () => Promise }>( + { + GetSettings: () : Promise => { + const promise = settingsPromise; + if(promise) return promise; + const p = new Promise((resolve, reject) => { + if (settings) resolve(settings); + + console.log(`Fetching settings`); + API.settingsList() + .then(result => { + if (!result.ok) + throw new Error(`Error fetching settings`); + setSettings(result.data); + resolve(result.data); + }).catch(reject); + }); + setSettingsPromise(p); + return p; + } + } +); \ No newline at end of file diff --git a/tranga-website/src/apiClient/V2.ts b/tranga-website/src/apiClient/V2.ts new file mode 100644 index 0000000..b6b22a1 --- /dev/null +++ b/tranga-website/src/apiClient/V2.ts @@ -0,0 +1,1308 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import { + Author, + BaseWorker, + Chapter, + FileLibrary, + GotifyRecord, + LibraryConnector, + Manga, + MangaConnector, + MetadataEntry, + MetadataSearchResult, + NotificationConnector, + NtfyRecord, + ProblemDetails, + PushoverRecord, + RequestType, + TrangaSettings, + WorkerExecutionState, +} from "./data-contracts"; +import { ContentType, HttpClient, RequestParams } from "./http-client"; + +export class V2< + SecurityDataType = unknown, +> extends HttpClient { + /** + * No description + * + * @tags FileLibrary + * @name FileLibraryList + * @summary Returns all API.Schema.MangaContext.FileLibrary + * @request GET:/v2/FileLibrary + */ + fileLibraryList = (params: RequestParams = {}) => + this.request({ + path: `/v2/FileLibrary`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags FileLibrary + * @name FileLibraryUpdate + * @summary Creates new !:FileLibraryId + * @request PUT:/v2/FileLibrary + */ + fileLibraryUpdate = (data: FileLibrary, params: RequestParams = {}) => + this.request({ + path: `/v2/FileLibrary`, + method: "PUT", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags FileLibrary + * @name FileLibraryDetail + * @summary Returns API.Schema.MangaContext.FileLibrary with FileLibraryId + * @request GET:/v2/FileLibrary/{FileLibraryId} + */ + fileLibraryDetail = (fileLibraryId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/FileLibrary/${fileLibraryId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags FileLibrary + * @name FileLibraryDelete + * @summary Deletes the !:FileLibraryId.LibraryName with FileLibraryId + * @request DELETE:/v2/FileLibrary/{FileLibraryId} + */ + fileLibraryDelete = (fileLibraryId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/FileLibrary/${fileLibraryId}`, + method: "DELETE", + ...params, + }); + /** + * No description + * + * @tags FileLibrary + * @name FileLibraryChangeBasePathPartialUpdate + * @summary Changes the !:FileLibraryId.BasePath with FileLibraryId + * @request PATCH:/v2/FileLibrary/{FileLibraryId}/ChangeBasePath + */ + fileLibraryChangeBasePathPartialUpdate = ( + fileLibraryId: string, + data: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/FileLibrary/${fileLibraryId}/ChangeBasePath`, + method: "PATCH", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags FileLibrary + * @name FileLibraryChangeNamePartialUpdate + * @summary Changes the !:FileLibraryId.LibraryName with FileLibraryId + * @request PATCH:/v2/FileLibrary/{FileLibraryId}/ChangeName + */ + fileLibraryChangeNamePartialUpdate = ( + fileLibraryId: string, + data: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/FileLibrary/${fileLibraryId}/ChangeName`, + method: "PATCH", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags LibraryConnector + * @name LibraryConnectorList + * @summary Gets all configured API.Schema.LibraryContext.LibraryConnectors.LibraryConnector + * @request GET:/v2/LibraryConnector + */ + libraryConnectorList = (params: RequestParams = {}) => + this.request({ + path: `/v2/LibraryConnector`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags LibraryConnector + * @name LibraryConnectorUpdate + * @summary Creates a new API.Schema.LibraryContext.LibraryConnectors.LibraryConnector + * @request PUT:/v2/LibraryConnector + */ + libraryConnectorUpdate = ( + data: LibraryConnector, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/LibraryConnector`, + method: "PUT", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags LibraryConnector + * @name LibraryConnectorDetail + * @summary Returns API.Schema.LibraryContext.LibraryConnectors.LibraryConnector with LibraryConnectorId + * @request GET:/v2/LibraryConnector/{LibraryConnectorId} + */ + libraryConnectorDetail = ( + libraryConnectorId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/LibraryConnector/${libraryConnectorId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags LibraryConnector + * @name LibraryConnectorDelete + * @request DELETE:/v2/LibraryConnector/{LibraryConnectorId} + */ + libraryConnectorDelete = ( + libraryConnectorId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/LibraryConnector/${libraryConnectorId}`, + method: "DELETE", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaList + * @summary Returns all cached API.Schema.MangaContext.Manga + * @request GET:/v2/Manga + */ + mangaList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Manga`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaWithIDsCreate + * @request POST:/v2/Manga/WithIDs + */ + mangaWithIDsCreate = (data: string[], params: RequestParams = {}) => + this.request({ + path: `/v2/Manga/WithIDs`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaDetail + * @summary Return API.Schema.MangaContext.Manga with MangaId + * @request GET:/v2/Manga/{MangaId} + */ + mangaDetail = (mangaId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Manga/${mangaId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaDelete + * @request DELETE:/v2/Manga/{MangaId} + */ + mangaDelete = (mangaId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Manga/${mangaId}`, + method: "DELETE", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaMergeIntoPartialUpdate + * @summary Merge two API.Schema.MangaContext.Manga into one. THIS IS NOT REVERSIBLE! + * @request PATCH:/v2/Manga/{MangaIdFrom}/MergeInto/{MangaIdInto} + */ + mangaMergeIntoPartialUpdate = ( + mangaIdFrom: string, + mangaIdInto: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaIdFrom}/MergeInto/${mangaIdInto}`, + method: "PATCH", + format: "blob", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaCoverList + * @summary Returns Cover of API.Schema.MangaContext.Manga with MangaId + * @request GET:/v2/Manga/{MangaId}/Cover + */ + mangaCoverList = ( + mangaId: string, + query?: { + /** + * If width is provided, height needs to also be provided + * @format int32 + */ + width?: number; + /** + * If height is provided, width needs to also be provided + * @format int32 + */ + height?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/Cover`, + method: "GET", + query: query, + format: "blob", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaChaptersList + * @summary Returns all API.Schema.MangaContext.Chapter of API.Schema.MangaContext.Manga with MangaId + * @request GET:/v2/Manga/{MangaId}/Chapters + */ + mangaChaptersList = (mangaId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Manga/${mangaId}/Chapters`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaChaptersDownloadedList + * @summary Returns all downloaded API.Schema.MangaContext.Chapter for API.Schema.MangaContext.Manga with MangaId + * @request GET:/v2/Manga/{MangaId}/Chapters/Downloaded + */ + mangaChaptersDownloadedList = (mangaId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Manga/${mangaId}/Chapters/Downloaded`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaChaptersNotDownloadedList + * @summary Returns all API.Schema.MangaContext.Chapter not downloaded for API.Schema.MangaContext.Manga with MangaId + * @request GET:/v2/Manga/{MangaId}/Chapters/NotDownloaded + */ + mangaChaptersNotDownloadedList = ( + mangaId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/Chapters/NotDownloaded`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaChapterLatestAvailableList + * @summary Returns the latest API.Schema.MangaContext.Chapter of requested API.Schema.MangaContext.Manga available on API.MangaConnectors.MangaConnector + * @request GET:/v2/Manga/{MangaId}/Chapter/LatestAvailable + */ + mangaChapterLatestAvailableList = ( + mangaId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/Chapter/LatestAvailable`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaChapterLatestDownloadedList + * @summary Returns the latest API.Schema.MangaContext.Chapter of requested API.Schema.MangaContext.Manga that is downloaded + * @request GET:/v2/Manga/{MangaId}/Chapter/LatestDownloaded + */ + mangaChapterLatestDownloadedList = ( + mangaId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/Chapter/LatestDownloaded`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaIgnoreChaptersBeforePartialUpdate + * @summary Configure the API.Schema.MangaContext.Chapter cut-off for API.Schema.MangaContext.Manga + * @request PATCH:/v2/Manga/{MangaId}/IgnoreChaptersBefore + */ + mangaIgnoreChaptersBeforePartialUpdate = ( + mangaId: string, + data: number, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/IgnoreChaptersBefore`, + method: "PATCH", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaChangeLibraryCreate + * @summary Move API.Schema.MangaContext.Manga to different API.Schema.MangaContext.FileLibrary + * @request POST:/v2/Manga/{MangaId}/ChangeLibrary/{LibraryId} + */ + mangaChangeLibraryCreate = ( + mangaId: string, + libraryId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/ChangeLibrary/${libraryId}`, + method: "POST", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaSetAsDownloadFromCreate + * @summary (Un-)Marks API.Schema.MangaContext.Manga as requested for Download from API.MangaConnectors.MangaConnector + * @request POST:/v2/Manga/{MangaId}/SetAsDownloadFrom/{MangaConnectorName}/{IsRequested} + */ + mangaSetAsDownloadFromCreate = ( + mangaId: string, + mangaConnectorName: string, + isRequested: boolean, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/SetAsDownloadFrom/${mangaConnectorName}/${isRequested}`, + method: "POST", + ...params, + }); + /** + * No description + * + * @tags Manga + * @name MangaSearchOnCreate + * @summary Initiate a search for API.Schema.MangaContext.Manga on a different API.MangaConnectors.MangaConnector + * @request POST:/v2/Manga/{MangaId}/SearchOn/{MangaConnectorName} + */ + mangaSearchOnCreate = ( + mangaId: string, + mangaConnectorName: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Manga/${mangaId}/SearchOn/${mangaConnectorName}`, + method: "POST", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MangaConnector + * @name MangaConnectorList + * @summary Get all API.MangaConnectors.MangaConnector (Scanlation-Sites) + * @request GET:/v2/MangaConnector + */ + mangaConnectorList = (params: RequestParams = {}) => + this.request({ + path: `/v2/MangaConnector`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MangaConnector + * @name MangaConnectorDetail + * @summary Returns the API.MangaConnectors.MangaConnector (Scanlation-Sites) with the requested Name + * @request GET:/v2/MangaConnector/{MangaConnectorName} + */ + mangaConnectorDetail = ( + mangaConnectorName: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/MangaConnector/${mangaConnectorName}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MangaConnector + * @name MangaConnectorEnabledList + * @summary Get all enabled API.MangaConnectors.MangaConnector (Scanlation-Sites) + * @request GET:/v2/MangaConnector/Enabled + */ + mangaConnectorEnabledList = (params: RequestParams = {}) => + this.request({ + path: `/v2/MangaConnector/Enabled`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MangaConnector + * @name MangaConnectorDisabledList + * @summary Get all disabled API.MangaConnectors.MangaConnector (Scanlation-Sites) + * @request GET:/v2/MangaConnector/Disabled + */ + mangaConnectorDisabledList = (params: RequestParams = {}) => + this.request({ + path: `/v2/MangaConnector/Disabled`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MangaConnector + * @name MangaConnectorSetEnabledPartialUpdate + * @summary Enabled or disables API.MangaConnectors.MangaConnector (Scanlation-Sites) with Name + * @request PATCH:/v2/MangaConnector/{MangaConnectorName}/SetEnabled/{Enabled} + */ + mangaConnectorSetEnabledPartialUpdate = ( + mangaConnectorName: string, + enabled: boolean, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/MangaConnector/${mangaConnectorName}/SetEnabled/${enabled}`, + method: "PATCH", + ...params, + }); + /** + * No description + * + * @tags MetadataFetcher + * @name MetadataFetcherList + * @summary Get all API.Schema.MangaContext.MetadataFetchers.MetadataFetcher (Metadata-Sites) + * @request GET:/v2/MetadataFetcher + */ + metadataFetcherList = (params: RequestParams = {}) => + this.request({ + path: `/v2/MetadataFetcher`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MetadataFetcher + * @name MetadataFetcherLinksList + * @summary Returns all API.Schema.MangaContext.MetadataFetchers.MetadataEntry + * @request GET:/v2/MetadataFetcher/Links + */ + metadataFetcherLinksList = (params: RequestParams = {}) => + this.request({ + path: `/v2/MetadataFetcher/Links`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags MetadataFetcher + * @name MetadataFetcherSearchMangaCreate + * @summary Searches API.Schema.MangaContext.MetadataFetchers.MetadataFetcher (Metadata-Sites) for Manga-Metadata + * @request POST:/v2/MetadataFetcher/{MetadataFetcherName}/SearchManga/{MangaId} + */ + metadataFetcherSearchMangaCreate = ( + mangaId: string, + metadataFetcherName: string, + data: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/MetadataFetcher/${metadataFetcherName}/SearchManga/${mangaId}`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags MetadataFetcher + * @name MetadataFetcherLinkCreate + * @summary Links API.Schema.MangaContext.MetadataFetchers.MetadataFetcher (Metadata-Sites) using Provider-Specific Identifier to API.Schema.MangaContext.Manga + * @request POST:/v2/MetadataFetcher/{MetadataFetcherName}/Link/{MangaId} + */ + metadataFetcherLinkCreate = ( + mangaId: string, + metadataFetcherName: string, + data: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/MetadataFetcher/${metadataFetcherName}/Link/${mangaId}`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags MetadataFetcher + * @name MetadataFetcherUnlinkCreate + * @summary Un-Links API.Schema.MangaContext.MetadataFetchers.MetadataFetcher (Metadata-Sites) from API.Schema.MangaContext.Manga + * @request POST:/v2/MetadataFetcher/{MetadataFetcherName}/Unlink/{MangaId} + */ + metadataFetcherUnlinkCreate = ( + mangaId: string, + metadataFetcherName: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/MetadataFetcher/${metadataFetcherName}/Unlink/${mangaId}`, + method: "POST", + ...params, + }); + /** + * No description + * + * @tags NotificationConnector + * @name NotificationConnectorList + * @summary Gets all configured API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector + * @request GET:/v2/NotificationConnector + */ + notificationConnectorList = (params: RequestParams = {}) => + this.request({ + path: `/v2/NotificationConnector`, + method: "GET", + format: "json", + ...params, + }); + /** + * @description Formatting placeholders: "%title" and "%text" can be placed in url, header-values and body and will be replaced when notifications are sent + * + * @tags NotificationConnector + * @name NotificationConnectorUpdate + * @summary Creates a new API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector + * @request PUT:/v2/NotificationConnector + */ + notificationConnectorUpdate = ( + data: NotificationConnector, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/NotificationConnector`, + method: "PUT", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags NotificationConnector + * @name NotificationConnectorDetail + * @summary Returns API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector with requested Name + * @request GET:/v2/NotificationConnector/{Name} + */ + notificationConnectorDetail = (name: string, params: RequestParams = {}) => + this.request({ + path: `/v2/NotificationConnector/${name}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags NotificationConnector + * @name NotificationConnectorDelete + * @summary Deletes the API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector with the requested Name + * @request DELETE:/v2/NotificationConnector/{Name} + */ + notificationConnectorDelete = (name: string, params: RequestParams = {}) => + this.request({ + path: `/v2/NotificationConnector/${name}`, + method: "DELETE", + ...params, + }); + /** + * @description Priority needs to be between 0 and 10 + * + * @tags NotificationConnector + * @name NotificationConnectorGotifyUpdate + * @summary Creates a new Gotify-API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector + * @request PUT:/v2/NotificationConnector/Gotify + */ + notificationConnectorGotifyUpdate = ( + data: GotifyRecord, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/NotificationConnector/Gotify`, + method: "PUT", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * @description Priority needs to be between 1 and 5 + * + * @tags NotificationConnector + * @name NotificationConnectorNtfyUpdate + * @summary Creates a new Ntfy-API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector + * @request PUT:/v2/NotificationConnector/Ntfy + */ + notificationConnectorNtfyUpdate = ( + data: NtfyRecord, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/NotificationConnector/Ntfy`, + method: "PUT", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * @description https://pushover.net/api + * + * @tags NotificationConnector + * @name NotificationConnectorPushoverUpdate + * @summary Creates a new Pushover-API.Schema.NotificationsContext.NotificationConnectors.NotificationConnector + * @request PUT:/v2/NotificationConnector/Pushover + */ + notificationConnectorPushoverUpdate = ( + data: PushoverRecord, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/NotificationConnector/Pushover`, + method: "PUT", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags Query + * @name QueryAuthorDetail + * @summary Returns the API.Schema.MangaContext.Author with AuthorId + * @request GET:/v2/Query/Author/{AuthorId} + */ + queryAuthorDetail = (authorId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Query/Author/${authorId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Query + * @name QueryMangasWithAuthorIdDetail + * @summary Returns all API.Schema.MangaContext.Manga which where Authored by API.Schema.MangaContext.Author with AuthorId + * @request GET:/v2/Query/Mangas/WithAuthorId/{AuthorId} + */ + queryMangasWithAuthorIdDetail = ( + authorId: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Query/Mangas/WithAuthorId/${authorId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Query + * @name QueryMangasWithTagDetail + * @summary Returns all API.Schema.MangaContext.Manga with !:Tag + * @request GET:/v2/Query/Mangas/WithTag/{Tag} + */ + queryMangasWithTagDetail = (tag: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Query/Mangas/WithTag/${tag}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Query + * @name QueryChapterDetail + * @summary Returns API.Schema.MangaContext.Chapter with ChapterId + * @request GET:/v2/Query/Chapter/{ChapterId} + */ + queryChapterDetail = (chapterId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Query/Chapter/${chapterId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Search + * @name SearchDetail + * @summary Initiate a search for a API.Schema.MangaContext.Manga on API.MangaConnectors.MangaConnector with searchTerm + * @request GET:/v2/Search/{MangaConnectorName}/{Query} + */ + searchDetail = ( + mangaConnectorName: string, + query: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Search/${mangaConnectorName}/${query}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Search + * @name SearchUrlCreate + * @summary Returns API.Schema.MangaContext.Manga from the API.MangaConnectors.MangaConnector associated with url + * @request POST:/v2/Search/Url + */ + searchUrlCreate = (data: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Search/Url`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsList + * @summary Get all API.Tranga.Settings + * @request GET:/v2/Settings + */ + settingsList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsUserAgentList + * @summary Get the current UserAgent used by Tranga + * @request GET:/v2/Settings/UserAgent + */ + settingsUserAgentList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/UserAgent`, + method: "GET", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsUserAgentPartialUpdate + * @summary Set a new UserAgent + * @request PATCH:/v2/Settings/UserAgent + */ + settingsUserAgentPartialUpdate = (data: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/UserAgent`, + method: "PATCH", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsUserAgentDelete + * @summary Reset the UserAgent to default + * @request DELETE:/v2/Settings/UserAgent + */ + settingsUserAgentDelete = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/UserAgent`, + method: "DELETE", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsRequestLimitsList + * @summary Get all Request-Limits + * @request GET:/v2/Settings/RequestLimits + */ + settingsRequestLimitsList = (params: RequestParams = {}) => + this.request< + { + /** @format int32 */ + Default?: number; + /** @format int32 */ + MangaDexFeed?: number; + /** @format int32 */ + MangaImage?: number; + /** @format int32 */ + MangaCover?: number; + /** @format int32 */ + MangaDexImage?: number; + /** @format int32 */ + MangaInfo?: number; + }, + any + >({ + path: `/v2/Settings/RequestLimits`, + method: "GET", + format: "json", + ...params, + }); + /** + * @description

NOT IMPLEMENTED

+ * + * @tags Settings + * @name SettingsRequestLimitsPartialUpdate + * @summary Update all Request-Limits to new values + * @request PATCH:/v2/Settings/RequestLimits + */ + settingsRequestLimitsPartialUpdate = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/RequestLimits`, + method: "PATCH", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsRequestLimitsDelete + * @summary Reset Request-Limit + * @request DELETE:/v2/Settings/RequestLimits + */ + settingsRequestLimitsDelete = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/RequestLimits`, + method: "DELETE", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsRequestLimitsPartialUpdate2 + * @summary Updates a Request-Limit value + * @request PATCH:/v2/Settings/RequestLimits/{RequestType} + * @originalName settingsRequestLimitsPartialUpdate + * @duplicate + */ + settingsRequestLimitsPartialUpdate2 = ( + requestType: RequestType, + data: number, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Settings/RequestLimits/${requestType}`, + method: "PATCH", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsRequestLimitsDelete2 + * @summary Reset Request-Limit + * @request DELETE:/v2/Settings/RequestLimits/{RequestType} + * @originalName settingsRequestLimitsDelete + * @duplicate + */ + settingsRequestLimitsDelete2 = ( + requestType: RequestType, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Settings/RequestLimits/${requestType}`, + method: "DELETE", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsImageCompressionLevelList + * @summary Returns Level of Image-Compression for Images + * @request GET:/v2/Settings/ImageCompressionLevel + */ + settingsImageCompressionLevelList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/ImageCompressionLevel`, + method: "GET", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsImageCompressionLevelPartialUpdate + * @summary Set the Image-Compression-Level for Images + * @request PATCH:/v2/Settings/ImageCompressionLevel/{level} + */ + settingsImageCompressionLevelPartialUpdate = ( + level: number, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Settings/ImageCompressionLevel/${level}`, + method: "PATCH", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsBwImagesList + * @summary Get state of Black/White-Image setting + * @request GET:/v2/Settings/BWImages + */ + settingsBwImagesList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/BWImages`, + method: "GET", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsBwImagesPartialUpdate + * @summary Enable/Disable conversion of Images to Black and White + * @request PATCH:/v2/Settings/BWImages/{enabled} + */ + settingsBwImagesPartialUpdate = ( + enabled: boolean, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Settings/BWImages/${enabled}`, + method: "PATCH", + ...params, + }); + /** + * @description Placeholders: %M Obj Name %V Volume %C Chapter %T Title %A Author (first in list) %I Chapter Internal ID %i Obj Internal ID %Y Year (Obj) ?_(...) replace _ with a value from above: Everything inside the braces will only be added if the value of %_ is not null + * + * @tags Settings + * @name SettingsChapterNamingSchemeList + * @summary Gets the Chapter Naming Scheme + * @request GET:/v2/Settings/ChapterNamingScheme + */ + settingsChapterNamingSchemeList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/ChapterNamingScheme`, + method: "GET", + ...params, + }); + /** + * @description Placeholders: %M Obj Name %V Volume %C Chapter %T Title %A Author (first in list) %Y Year (Obj) ?_(...) replace _ with a value from above: Everything inside the braces will only be added if the value of %_ is not null + * + * @tags Settings + * @name SettingsChapterNamingSchemePartialUpdate + * @summary Sets the Chapter Naming Scheme + * @request PATCH:/v2/Settings/ChapterNamingScheme + */ + settingsChapterNamingSchemePartialUpdate = ( + data: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Settings/ChapterNamingScheme`, + method: "PATCH", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsFlareSolverrUrlCreate + * @summary Sets the FlareSolverr-URL + * @request POST:/v2/Settings/FlareSolverr/Url + */ + settingsFlareSolverrUrlCreate = (data: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/FlareSolverr/Url`, + method: "POST", + body: data, + type: ContentType.Json, + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsFlareSolverrUrlDelete + * @summary Resets the FlareSolverr-URL (HttpClient does not use FlareSolverr anymore) + * @request DELETE:/v2/Settings/FlareSolverr/Url + */ + settingsFlareSolverrUrlDelete = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/FlareSolverr/Url`, + method: "DELETE", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsFlareSolverrTestCreate + * @summary Test FlareSolverr + * @request POST:/v2/Settings/FlareSolverr/Test + */ + settingsFlareSolverrTestCreate = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/FlareSolverr/Test`, + method: "POST", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsDownloadLanguageList + * @summary Returns the language in which Manga are downloaded + * @request GET:/v2/Settings/DownloadLanguage + */ + settingsDownloadLanguageList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Settings/DownloadLanguage`, + method: "GET", + ...params, + }); + /** + * No description + * + * @tags Settings + * @name SettingsDownloadLanguagePartialUpdate + * @summary Sets the language in which Manga are downloaded + * @request PATCH:/v2/Settings/DownloadLanguage/{Language} + */ + settingsDownloadLanguagePartialUpdate = ( + language: string, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Settings/DownloadLanguage/${language}`, + method: "PATCH", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerList + * @summary Returns all API.Workers.BaseWorker.Keys + * @request GET:/v2/Worker + */ + workerList = (params: RequestParams = {}) => + this.request({ + path: `/v2/Worker`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerWithIDsCreate + * @summary Returns API.Workers.BaseWorker with requested WorkerIds + * @request POST:/v2/Worker/WithIDs + */ + workerWithIDsCreate = (data: string[], params: RequestParams = {}) => + this.request({ + path: `/v2/Worker/WithIDs`, + method: "POST", + body: data, + type: ContentType.Json, + format: "json", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerStateDetail + * @summary Get all API.Workers.BaseWorker in requested API.Workers.WorkerExecutionState + * @request GET:/v2/Worker/State/{State} + */ + workerStateDetail = ( + state: WorkerExecutionState, + params: RequestParams = {}, + ) => + this.request({ + path: `/v2/Worker/State/${state}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerDetail + * @summary Return API.Workers.BaseWorker with WorkerId + * @request GET:/v2/Worker/{WorkerId} + */ + workerDetail = (workerId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Worker/${workerId}`, + method: "GET", + format: "json", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerDelete + * @summary Delete API.Workers.BaseWorker with WorkerId and all child-API.Workers.BaseWorkers + * @request DELETE:/v2/Worker/{WorkerId} + */ + workerDelete = (workerId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Worker/${workerId}`, + method: "DELETE", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerStartCreate + * @summary Starts API.Workers.BaseWorker with WorkerId + * @request POST:/v2/Worker/{WorkerId}/Start + */ + workerStartCreate = (workerId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Worker/${workerId}/Start`, + method: "POST", + ...params, + }); + /** + * No description + * + * @tags Worker + * @name WorkerStopCreate + * @summary Stops API.Workers.BaseWorker with WorkerId + * @request POST:/v2/Worker/{WorkerId}/Stop + */ + workerStopCreate = (workerId: string, params: RequestParams = {}) => + this.request({ + path: `/v2/Worker/${workerId}/Stop`, + method: "POST", + ...params, + }); +} diff --git a/tranga-website/src/apiClient/data-contracts.ts b/tranga-website/src/apiClient/data-contracts.ts new file mode 100644 index 0000000..77791de --- /dev/null +++ b/tranga-website/src/apiClient/data-contracts.ts @@ -0,0 +1,335 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export enum WorkerExecutionState { + Failed = "Failed", + Cancelled = "Cancelled", + Created = "Created", + Waiting = "Waiting", + Running = "Running", + Completed = "Completed", +} + +export enum RequestType { + Default = "Default", + MangaDexFeed = "MangaDexFeed", + MangaImage = "MangaImage", + MangaCover = "MangaCover", + MangaDexImage = "MangaDexImage", + MangaInfo = "MangaInfo", +} + +export enum MangaReleaseStatus { + Continuing = "Continuing", + Completed = "Completed", + OnHiatus = "OnHiatus", + Cancelled = "Cancelled", + Unreleased = "Unreleased", +} + +export enum LibraryType { + Komga = "Komga", + Kavita = "Kavita", +} + +export interface AltTitle { + /** + * @minLength 0 + * @maxLength 8 + */ + language: string; + /** + * @minLength 0 + * @maxLength 256 + */ + title: string; + key?: string | null; +} + +export interface Author { + /** + * @minLength 0 + * @maxLength 128 + */ + authorName: string; + key?: string | null; +} + +export interface BaseWorker { + /** Workers this Worker depends on being completed before running. */ + dependsOn?: BaseWorker[] | null; + /** Dependencies and dependencies of dependencies. See also API.Workers.BaseWorker.DependsOn. */ + allDependencies?: BaseWorker[] | null; + /** API.Workers.BaseWorker.AllDependencies and Self. */ + dependenciesAndSelf?: BaseWorker[] | null; + /** API.Workers.BaseWorker.DependsOn where API.Workers.WorkerExecutionState is less than Completed. */ + missingDependencies?: BaseWorker[] | null; + allDependenciesFulfilled?: boolean; + key?: string | null; +} + +export interface Chapter { + /** + * @minLength 0 + * @maxLength 64 + */ + parentMangaId: string; + idsOnMangaConnectors?: Record; + /** @format int32 */ + volumeNumber?: number | null; + /** + * @minLength 0 + * @maxLength 10 + */ + chapterNumber: string; + /** + * @minLength 0 + * @maxLength 256 + */ + title?: string | null; + /** + * @minLength 0 + * @maxLength 256 + */ + fileName: string; + downloaded: boolean; + fullArchiveFilePath?: string | null; + key?: string | null; +} + +export interface FileLibrary { + /** + * @minLength 0 + * @maxLength 256 + */ + basePath: string; + /** + * @minLength 0 + * @maxLength 512 + */ + libraryName: string; + key?: string | null; +} + +export interface GotifyRecord { + name?: string | null; + endpoint?: string | null; + appToken?: string | null; + /** @format int32 */ + priority?: number; +} + +export interface LibraryConnector { + libraryType: LibraryType; + /** + * @format uri + * @minLength 0 + * @maxLength 256 + */ + baseUrl: string; + /** + * @minLength 0 + * @maxLength 256 + */ + auth: string; + key?: string | null; +} + +export interface Link { + /** + * @minLength 0 + * @maxLength 64 + */ + linkProvider: string; + /** + * @format uri + * @minLength 0 + * @maxLength 2048 + */ + linkUrl: string; + key?: string | null; +} + +export interface Manga { + /** + * @minLength 0 + * @maxLength 512 + */ + name: string; + /** @minLength 1 */ + description: string; + releaseStatus: MangaReleaseStatus; + /** + * @minLength 0 + * @maxLength 64 + */ + libraryId?: string | null; + authors?: Author[] | null; + mangaTags?: MangaTag[] | null; + links?: Link[] | null; + altTitles?: AltTitle[] | null; + /** @format float */ + ignoreChaptersBefore: number; + /** + * @minLength 0 + * @maxLength 1024 + */ + directoryName: string; + /** @format int32 */ + year?: number | null; + /** + * @minLength 0 + * @maxLength 8 + */ + originalLanguage?: string | null; + chapterIds?: string[] | null; + idsOnMangaConnectors?: Record; + key?: string | null; +} + +export interface MangaConnector { + /** + * @minLength 0 + * @maxLength 32 + */ + name: string; + /** + * @minLength 0 + * @maxLength 8 + */ + supportedLanguages: string[]; + /** + * @minLength 0 + * @maxLength 2048 + */ + iconUrl: string; + /** + * @minLength 0 + * @maxLength 256 + */ + baseUris: string[]; + enabled: boolean; +} + +export interface MangaTag { + /** + * @minLength 0 + * @maxLength 64 + */ + tag: string; +} + +export interface MetadataEntry { + mangaId?: string | null; + metadataFetcherName?: string | null; + identifier?: string | null; +} + +export interface MetadataSearchResult { + identifier?: string | null; + name?: string | null; + url?: string | null; + description?: string | null; + coverUrl?: string | null; +} + +export interface NotificationConnector { + /** + * @minLength 0 + * @maxLength 64 + */ + name: string; + /** + * @format uri + * @minLength 0 + * @maxLength 2048 + */ + url: string; + headers: Record; + /** + * @minLength 0 + * @maxLength 8 + */ + httpMethod: string; + /** + * @minLength 0 + * @maxLength 4096 + */ + body: string; +} + +export interface NtfyRecord { + name?: string | null; + endpoint?: string | null; + username?: string | null; + password?: string | null; + topic?: string | null; + /** @format int32 */ + priority?: number; +} + +export interface ProblemDetails { + type?: string | null; + title?: string | null; + /** @format int32 */ + status?: number | null; + detail?: string | null; + instance?: string | null; + [key: string]: any; +} + +export interface PushoverRecord { + name?: string | null; + appToken?: string | null; + user?: string | null; +} + +export interface TrangaSettings { + downloadLocation?: string | null; + userAgent?: string | null; + /** @format int32 */ + imageCompression?: number; + blackWhiteImages?: boolean; + flareSolverrUrl?: string | null; + /** + * Placeholders: + * %M Obj Name + * %V Volume + * %C Chapter + * %T Title + * %A Author (first in list) + * %I Chapter Internal ID + * %i Obj Internal ID + * %Y Year (Obj) + * + * ?_(...) replace _ with a value from above: + * Everything inside the braces will only be added if the value of %_ is not null + */ + chapterNamingScheme?: string | null; + /** @format int32 */ + workCycleTimeoutMs?: number; + requestLimits?: { + /** @format int32 */ + Default?: number; + /** @format int32 */ + MangaDexFeed?: number; + /** @format int32 */ + MangaImage?: number; + /** @format int32 */ + MangaCover?: number; + /** @format int32 */ + MangaDexImage?: number; + /** @format int32 */ + MangaInfo?: number; + } | null; + downloadLanguage?: string | null; +} diff --git a/tranga-website/src/apiClient/http-client.ts b/tranga-website/src/apiClient/http-client.ts new file mode 100644 index 0000000..f2f9b13 --- /dev/null +++ b/tranga-website/src/apiClient/http-client.ts @@ -0,0 +1,260 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export type QueryParamsType = Record; +export type ResponseFormat = keyof Omit; + +export interface FullRequestParams extends Omit { + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseFormat; + /** request body */ + body?: unknown; + /** base url */ + baseUrl?: string; + /** request cancellation token */ + cancelToken?: CancelToken; +} + +export type RequestParams = Omit< + FullRequestParams, + "body" | "method" | "query" | "path" +>; + +export interface ApiConfig { + baseUrl?: string; + baseApiParams?: Omit; + securityWorker?: ( + securityData: SecurityDataType | null, + ) => Promise | RequestParams | void; + customFetch?: typeof fetch; +} + +export interface HttpResponse + extends Response { + data: D; + error: E; +} + +type CancelToken = Symbol | string | number; + +export enum ContentType { + Json = "application/json", + JsonApi = "application/vnd.api+json", + FormData = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded", + Text = "text/plain", +} + +export class HttpClient { + public baseUrl: string = ""; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig["securityWorker"]; + private abortControllers = new Map(); + private customFetch = (...fetchParams: Parameters) => + fetch(...fetchParams); + + private baseApiParams: RequestParams = { + credentials: "same-origin", + headers: {}, + redirect: "follow", + referrerPolicy: "no-referrer", + }; + + constructor(apiConfig: ApiConfig = {}) { + Object.assign(this, apiConfig); + } + + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; + + protected encodeQueryParam(key: string, value: any) { + const encodedKey = encodeURIComponent(key); + return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; + } + + protected addQueryParam(query: QueryParamsType, key: string) { + return this.encodeQueryParam(key, query[key]); + } + + protected addArrayQueryParam(query: QueryParamsType, key: string) { + const value = query[key]; + return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); + } + + protected toQueryString(rawQuery?: QueryParamsType): string { + const query = rawQuery || {}; + const keys = Object.keys(query).filter( + (key) => "undefined" !== typeof query[key], + ); + return keys + .map((key) => + Array.isArray(query[key]) + ? this.addArrayQueryParam(query, key) + : this.addQueryParam(query, key), + ) + .join("&"); + } + + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ""; + } + + private contentFormatters: Record any> = { + [ContentType.Json]: (input: any) => + input !== null && (typeof input === "object" || typeof input === "string") + ? JSON.stringify(input) + : input, + [ContentType.JsonApi]: (input: any) => + input !== null && (typeof input === "object" || typeof input === "string") + ? JSON.stringify(input) + : input, + [ContentType.Text]: (input: any) => + input !== null && typeof input !== "string" + ? JSON.stringify(input) + : input, + [ContentType.FormData]: (input: any) => + Object.keys(input || {}).reduce((formData, key) => { + const property = input[key]; + formData.append( + key, + property instanceof Blob + ? property + : typeof property === "object" && property !== null + ? JSON.stringify(property) + : `${property}`, + ); + return formData; + }, new FormData()), + [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), + }; + + protected mergeRequestParams( + params1: RequestParams, + params2?: RequestParams, + ): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected createAbortSignal = ( + cancelToken: CancelToken, + ): AbortSignal | undefined => { + if (this.abortControllers.has(cancelToken)) { + const abortController = this.abortControllers.get(cancelToken); + if (abortController) { + return abortController.signal; + } + return void 0; + } + + const abortController = new AbortController(); + this.abortControllers.set(cancelToken, abortController); + return abortController.signal; + }; + + public abortRequest = (cancelToken: CancelToken) => { + const abortController = this.abortControllers.get(cancelToken); + + if (abortController) { + abortController.abort(); + this.abortControllers.delete(cancelToken); + } + }; + + public request = async ({ + body, + secure, + path, + type, + query, + format, + baseUrl, + cancelToken, + ...params + }: FullRequestParams): Promise> => { + const secureParams = + ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const queryString = query && this.toQueryString(query); + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; + const responseFormat = format || requestParams.format; + + return this.customFetch( + `${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, + { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData + ? { "Content-Type": type } + : {}), + }, + signal: + (cancelToken + ? this.createAbortSignal(cancelToken) + : requestParams.signal) || null, + body: + typeof body === "undefined" || body === null + ? null + : payloadFormatter(body), + }, + ).then(async (response) => { + const r = response.clone() as HttpResponse; + r.data = null as unknown as T; + r.error = null as unknown as E; + + const data = !responseFormat + ? r + : await response[responseFormat]() + .then((data) => { + if (r.ok) { + r.data = data; + } else { + r.error = data; + } + return r; + }) + .catch((e) => { + r.error = e; + return r; + }); + + if (cancelToken) { + this.abortControllers.delete(cancelToken); + } + + if (!response.ok) throw data; + return data; + }); + }; +}