Compare commits

..

11 Commits

41 changed files with 608 additions and 3462 deletions

View File

@@ -1,14 +1,14 @@
module.exports = { module.exports = {
extends: ["plugin:react/recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended", "plugin:css-modules/recommended", "plugin:storybook/recommended", "plugin:storybook/recommended", "plugin:storybook/recommended", "plugin:storybook/recommended"], extends: ["plugin:react/recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended", "plugin:css-modules/recommended", "plugin:storybook/recommended", "plugin:storybook/recommended", "plugin:storybook/recommended"],
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
sourceType: "module", sourceType: "module",
ecmaVersion: 2020, ecmaVersion: 2020,
ecmaFeatures: { ecmaFeatures: {
jsx: true // Allows for the parsing of JSX jsx: true // Allows for the parsing of JSX
} }
}, },
plugins: ["@typescript-eslint", "css-modules"], plugins: ["@typescript-eslint", "css-modules"],
settings: { settings: {
"import/resolver": { "import/resolver": {
@@ -18,9 +18,9 @@ module.exports = {
}, },
react: { react: {
version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use version: "detect" // Tells eslint-plugin-react to automatically detect the version of React to use
} }
}, },
// Fine tune rules // Fine tune rules
rules: { rules: {
"@typescript-eslint/no-var-requires": 0 "@typescript-eslint/no-var-requires": 0

View File

@@ -12,7 +12,7 @@ jobs:
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- name: Publish to Registry - name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@v5 uses: elgohr/Publish-Docker-Github-Action@master
with: with:
name: frishi/threetwo name: frishi/threetwo
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}

19
.storybook/main.js Normal file
View File

@@ -0,0 +1,19 @@
module.exports = {
stories: [
"../src/client/stories/*.stories.mdx",
"../src/client/stories/*.stories.@(js|jsx|ts|tsx)",
],
staticDirs: [
"../src/client/stories/assets"
],
addons: [
"@storybook/addon-links",
"@storybook/preset-scss",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: "@storybook/react",
core: {
builder: "webpack5",
},
};

View File

@@ -1,17 +0,0 @@
import type { StorybookConfig } from "@storybook/react-vite";
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
},
};
export default config;

View File

@@ -1,3 +0,0 @@
<script>
window.global = window;
</script>

9
.storybook/preview.js Normal file
View File

@@ -0,0 +1,9 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}

View File

@@ -1,18 +0,0 @@
import type { Preview } from "@storybook/react";
const preview: Preview = {
parameters: {
backgrounds: {
default: "light",
},
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;

BIN
ComicVine Matching.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
DC++ integration.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

BIN
Dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

View File

@@ -1,4 +1,4 @@
FROM node:18.15.0-alpine FROM node:17.3-alpine
LABEL maintainer="Rishi Ghan <rishi.ghan@gmail.com>" LABEL maintainer="Rishi Ghan <rishi.ghan@gmail.com>"
WORKDIR /threetwo WORKDIR /threetwo
@@ -9,11 +9,11 @@ COPY nodemon.json ./
COPY jsdoc.json ./ COPY jsdoc.json ./
# RUN apt-get update && apt-get install -y git python3 build-essential autoconf automake g++ libpng-dev make # RUN apt-get update && apt-get install -y git python3 build-essential autoconf automake g++ libpng-dev make
RUN apk --no-cache add g++ make libpng-dev git python3 libc6-compat autoconf automake libjpeg-turbo-dev libpng-dev mesa-dev mesa libxi build-base gcc libtool nasm RUN apk --no-cache add g++ make libpng-dev python3 git libc6-compat autoconf automake bash libjpeg-turbo-dev libpng-dev mesa-dev mesa libxi build-base gcc libtool nasm
RUN yarn --ignore-engines RUN yarn --ignore-engines
COPY . . COPY . .
EXPOSE 5173 EXPOSE 3050
ENTRYPOINT [ "npm", "start" ] ENTRYPOINT [ "npm", "start" ]

BIN
Library.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -6,13 +6,13 @@ ThreeTwo! _aims to be_ a comic book curation app.
### Screenshots ### Screenshots
![](https://github.com/rishighan/threetwo-visual-updates/raw/67e56878eb0381c73c1dea746a45253d3dcaa184/update_december_2022/Dashboard.png) ![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/Dashboard.png)
![](https://github.com/rishighan/threetwo-visual-updates/raw/67e56878eb0381c73c1dea746a45253d3dcaa184/update_december_2022/Library.png) ![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/Library.png)
![](https://github.com/rishighan/threetwo-visual-updates/raw/67e56878eb0381c73c1dea746a45253d3dcaa184/update_december_2022/DC%2B%2B%20integration.png) ![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/DC%2B%2B%20integration.png)
![](https://github.com/rishighan/threetwo-visual-updates/raw/67e56878eb0381c73c1dea746a45253d3dcaa184/update_december_2022/ComicVine%20Matching.png) ![](https://raw.githubusercontent.com/rishighan/threetwo/rishighan-screenshots-dec-2022/ComicVine%20Matching.png)
### 🦄 Early Development Support Channel ### 🦄 Early Development Support Channel
@@ -40,8 +40,9 @@ For debugging and troubleshooting, you can run this app locally using these step
1. Clone this repo using `git clone https://github.com/rishighan/threetwo.git` 1. Clone this repo using `git clone https://github.com/rishighan/threetwo.git`
2. `yarn run dev` (you can ignore the warnings) 2. `yarn run dev` (you can ignore the warnings)
3. This will open `http://localhost:5173` in your default browser 3. This will open `http://localhost:3050` in your default browser
4. Note that this is simply the UI layer and won't offer anything beyond a scaffold. You have to spin up the microservices locally to get it to work. 4. For testing `OPDS` functionality, create a folder called `comics` under `/src/server` and put some comics in there. The `OPDS` feed is accessed to `http://localhost:8050/api/opds`
5. Note that this is simply the UI layer and won't offer anything beyond a scaffold. You have to spin up the microservices locally to get it to work.
## Troubleshooting ## Troubleshooting

View File

@@ -7,10 +7,8 @@
"scripts": { "scripts": {
"build": "vite build", "build": "vite build",
"dev": "rimraf dist && npm run build && vite", "dev": "rimraf dist && npm run build && vite",
"start": "npm run build && vite", "prod": "npm run build && vite",
"docs": "jsdoc -c jsdoc.json", "docs": "jsdoc -c jsdoc.json"
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}, },
"author": "Rishi Ghan", "author": "Rishi Ghan",
"license": "MIT", "license": "MIT",
@@ -20,31 +18,32 @@
"@dnd-kit/core": "^4.0.0", "@dnd-kit/core": "^4.0.0",
"@dnd-kit/sortable": "^5.0.0", "@dnd-kit/sortable": "^5.0.0",
"@dnd-kit/utilities": "^3.2.0", "@dnd-kit/utilities": "^3.2.0",
"@fortawesome/fontawesome-free": "^6.3.0", "@fortawesome/fontawesome-free": "^6.1.1",
"@redux-devtools/extension": "^3.2.2", "@redux-devtools/extension": "^3.2.2",
"@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-node-resolve": "^15.0.1",
"@tanstack/react-table": "^8.5.11", "@tanstack/react-table": "^8.5.11",
"@types/axios": "^0.14.0",
"@types/mime-types": "^2.1.0", "@types/mime-types": "^2.1.0",
"@types/react-router-dom": "^5.3.3", "@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"@types/react-redux": "^7.1.16",
"@types/react-router-dom": "^5.1.7",
"@types/socket.io": "^3.0.2", "@types/socket.io": "^3.0.2",
"@types/socket.io-client": "^3.0.0", "@types/socket.io-client": "^3.0.0",
"@vitejs/plugin-react": "^3.1.0", "@vitejs/plugin-react": "^3.1.0",
"airdcpp-apisocket": "~2.4.5-beta.2", "airdcpp-apisocket": "2.4.5-beta.1",
"axios": "^1.3.4", "axios": "^0.27.2",
"axios-cache-interceptor": "^1.0.1",
"axios-rate-limit": "^1.3.0", "axios-rate-limit": "^1.3.0",
"axios-simple-cache-adapter": "^1.1.0",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"babel-preset-minify": "^0.5.2", "babel-preset-minify": "^0.5.2",
"better-docs": "^2.7.2", "better-docs": "^2.7.2",
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"dayjs": "^1.10.6", "dayjs": "^1.10.6",
"ellipsize": "^0.5.1", "ellipsize": "^0.1.0",
"express": "^4.17.1", "express": "^4.17.1",
"filename-parser": "^1.0.2", "filename-parser": "^1.0.2",
"final-form": "^4.20.2", "final-form": "^4.20.2",
"final-form-arrays": "^3.0.2", "final-form-arrays": "^3.0.2",
"history": "^5.3.0",
"html-to-text": "^8.1.0", "html-to-text": "^8.1.0",
"jsdoc": "^3.6.10", "jsdoc": "^3.6.10",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@@ -56,16 +55,16 @@
"react-collapsible": "^2.9.0", "react-collapsible": "^2.9.0",
"react-comic-viewer": "^0.4.0", "react-comic-viewer": "^0.4.0",
"react-day-picker": "^8.6.0", "react-day-picker": "^8.6.0",
"react-dom": "^18.2.0", "react-dom": "^18.1.0",
"react-fast-compare": "^3.2.0", "react-fast-compare": "^3.2.0",
"react-final-form": "^6.5.9", "react-final-form": "^6.5.9",
"react-final-form-arrays": "^3.1.4", "react-final-form-arrays": "^3.1.4",
"react-loader-spinner": "^4.0.0", "react-loader-spinner": "^4.0.0",
"react-masonry-css": "^1.0.16", "react-masonry-css": "^1.0.16",
"react-modal": "^3.15.1", "react-modal": "^3.15.1",
"react-redux": "^8.0.5", "react-redux": "^7.2.6",
"react-router": "^6.9.0", "react-router": "^6.2.2",
"react-router-dom": "^6.9.0", "react-router-dom": "^6.2.2",
"react-select": "^5.3.2", "react-select": "^5.3.2",
"react-select-async-paginate": "^0.7.2", "react-select-async-paginate": "^0.7.2",
"react-slick": "^0.29.0", "react-slick": "^0.29.0",
@@ -78,10 +77,12 @@
"redux-thunk": "^2.4.2", "redux-thunk": "^2.4.2",
"slick-carousel": "^1.8.1", "slick-carousel": "^1.8.1",
"socket.io-client": "^4.3.2", "socket.io-client": "^4.3.2",
"styled-components": "^5.3.9", "styled-components": "^5.3.5",
"threetwo-ui-typings": "^1.0.14", "threetwo-ui-typings": "^1.0.14",
"vite-plugin-html": "^3.2.0", "vite-plugin-html": "^3.2.0",
"websocket": "^1.0.34", "websocket": "^1.0.34",
"ws": "^7.5.3",
"xml2js": "^0.4.23",
"xregexp": "^5.0.2" "xregexp": "^5.0.2"
}, },
"devDependencies": { "devDependencies": {
@@ -92,27 +93,21 @@
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.13.0", "@babel/preset-typescript": "^7.13.0",
"@storybook/addon-essentials": "^7.0.0-rc.3",
"@storybook/addon-interactions": "^7.0.0-rc.3",
"@storybook/addon-links": "^7.0.0-rc.3",
"@storybook/blocks": "^7.0.0-rc.3",
"@storybook/react": "^7.0.0-rc.3",
"@storybook/react-vite": "^7.0.0-rc.3",
"@storybook/testing-library": "^0.0.14-next.1",
"@tsconfig/node14": "^1.0.0", "@tsconfig/node14": "^1.0.0",
"@types/ellipsize": "^0.1.1",
"@types/express": "^4.17.8", "@types/express": "^4.17.8",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^14.14.34", "@types/node": "^14.14.34",
"@types/react": "^18.0.28", "@types/react": "^17.0.3",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^17.0.2",
"@types/react-redux": "^7.1.25", "@types/react-redux": "^7.1.16",
"@typescript-eslint/eslint-plugin": "^4.17.0", "@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.17.0", "@typescript-eslint/parser": "^4.17.0",
"babel-eslint": "^10.0.0", "babel-eslint": "^10.0.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"comlink": "^4.3.0",
"concurrently": "^4.0.0",
"eslint": "^7.22.0", "eslint": "^7.22.0",
"eslint-config-airbnb": "^18.2.1", "eslint-config-airbnb": "^18.2.1",
"eslint-config-airbnb-base": "^14.2.1", "eslint-config-airbnb-base": "^14.2.1",
@@ -122,7 +117,6 @@
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-prettier": "^3.3.1", "eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.22.0", "eslint-plugin-react": "^7.22.0",
"eslint-plugin-storybook": "^0.6.11",
"express": "^4.17.1", "express": "^4.17.1",
"install": "^0.13.0", "install": "^0.13.0",
"jest": "^26.6.3", "jest": "^26.6.3",
@@ -132,9 +126,8 @@
"react-refresh": "^0.14.0", "react-refresh": "^0.14.0",
"rimraf": "^4.1.3", "rimraf": "^4.1.3",
"sass": "^1.58.1", "sass": "^1.58.1",
"storybook": "^7.0.0-rc.3",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"typescript": "^5.0.2", "typescript": "^4.2.3",
"vite": "^4.2.0" "vite": "^4.1.1"
} }
} }

View File

@@ -1,6 +1,10 @@
import axios from "axios"; import axios from "axios";
import rateLimiter from "axios-rate-limit"; import rateLimiter from "axios-rate-limit";
import { setupCache } from "axios-cache-interceptor"; import {
AxiosCacheRequestConfig,
createCacheAdapter,
} from "axios-simple-cache-adapter";
import qs from "qs";
import { import {
CV_SEARCH_SUCCESS, CV_SEARCH_SUCCESS,
CV_API_CALL_IN_PROGRESS, CV_API_CALL_IN_PROGRESS,
@@ -22,22 +26,25 @@ import {
LIBRARY_SERVICE_BASE_URI, LIBRARY_SERVICE_BASE_URI,
} from "../constants/endpoints"; } from "../constants/endpoints";
const axiosCacheAdapter = createCacheAdapter();
const http = rateLimiter(axios.create(), { const http = rateLimiter(axios.create(), {
maxRequests: 1, maxRequests: 1,
perMilliseconds: 1000, perMilliseconds: 1000,
maxRPS: 1, maxRPS: 1,
}); });
const cachedAxios = setupCache(axios);
export const getWeeklyPullList = (options) => async (dispatch) => { export const getWeeklyPullList = (options) => async (dispatch) => {
try { try {
dispatch({ dispatch({
type: CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS, type: CV_WEEKLY_PULLLIST_CALL_IN_PROGRESS,
}); });
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, { await axios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
method: "get", method: "get",
params: options, params: options,
}).then((response) => { axiosCacheAdapter,
cache: 1000, // value in MS
} as AxiosCacheRequestConfig).then((response) => {
dispatch({ dispatch({
type: CV_WEEKLY_PULLLIST_FETCHED, type: CV_WEEKLY_PULLLIST_FETCHED,
data: response.data.result, data: response.data.result,
@@ -63,6 +70,9 @@ export const comicinfoAPICall = (options) => async (dispatch) => {
"Content-Type": "application/json", "Content-Type": "application/json",
"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Origin": "*",
}, },
paramsSerializer: (params) => {
return qs.stringify(params, { arrayFormat: "repeat" });
},
}); });
switch (options.callURIAction) { switch (options.callURIAction) {

View File

@@ -34,27 +34,11 @@ import {
WANTED_COMICS_FETCHED, WANTED_COMICS_FETCHED,
VOLUMES_FETCHED, VOLUMES_FETCHED,
CV_WEEKLY_PULLLIST_FETCHED, CV_WEEKLY_PULLLIST_FETCHED,
LIBRARY_SERVICE_HEALTH,
} from "../constants/action-types"; } from "../constants/action-types";
import { success } from "react-notification-system-redux"; import { success } from "react-notification-system-redux";
import { isNil, map } from "lodash"; import { isNil, map } from "lodash";
export const getServiceStatus = (serviceName?: string) => async dispatch => {
axios
.request({
url: `${LIBRARY_SERVICE_BASE_URI}/getHealthInformation`,
method: "GET",
transformResponse: (r: string) => JSON.parse(r),
})
.then((response) => {
const { data } = response;
dispatch({
type: LIBRARY_SERVICE_HEALTH,
status: data,
});
});
};
export async function walkFolder(path: string): Promise<Array<IFolderData>> { export async function walkFolder(path: string): Promise<Array<IFolderData>> {
return axios return axios
.request<Array<IFolderData>>({ .request<Array<IFolderData>>({
@@ -143,50 +127,49 @@ export const getComicBooks = (options) => async (dispatch) => {
* @returns Nothing. * @returns Nothing.
* @param payload * @param payload
*/ */
export const importToDB = export const importToDB = (sourceName: string, metadata?: any) => (dispatch) => {
(sourceName: string, metadata?: any) => (dispatch) => { try {
try { const comicBookMetadata = {
const comicBookMetadata = { importType: "new",
importType: "new", payload: {
payload: { rawFileDetails: {
rawFileDetails: { name: "",
name: "",
},
importStatus: {
isImported: true,
tagged: false,
matchedResult: {
score: "0",
},
},
sourcedMetadata: metadata || null,
acquisition: { source: { wanted: true, name: sourceName } },
}, },
}; importStatus: {
dispatch({ isImported: true,
type: IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS, tagged: false,
}); matchedResult: {
return axios score: "0",
.request({ },
url: `${LIBRARY_SERVICE_BASE_URI}/rawImportToDb`, },
method: "POST", sourcedMetadata: metadata || null,
data: comicBookMetadata, acquisition: { source: { wanted: true, name: sourceName } },
// transformResponse: (r: string) => JSON.parse(r), }
}) };
.then((response) => { dispatch({
const { data } = response; type: IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS,
dispatch({ });
type: IMS_CV_METADATA_IMPORT_SUCCESSFUL, return axios
importResult: data, .request({
}); url: `${LIBRARY_SERVICE_BASE_URI}/rawImportToDb`,
method: "POST",
data: comicBookMetadata,
// transformResponse: (r: string) => JSON.parse(r),
})
.then((response) => {
const { data } = response;
dispatch({
type: IMS_CV_METADATA_IMPORT_SUCCESSFUL,
importResult: data,
}); });
} catch (error) {
dispatch({
type: IMS_CV_METADATA_IMPORT_FAILED,
importError: error,
}); });
} } catch (error) {
}; dispatch({
type: IMS_CV_METADATA_IMPORT_FAILED,
importError: error,
});
}
};
export const fetchVolumeGroups = () => async (dispatch) => { export const fetchVolumeGroups = () => async (dispatch) => {
try { try {
@@ -271,23 +254,24 @@ export const fetchComicVineMatches =
* @returns {any} * @returns {any}
*/ */
export const extractComicArchive = export const extractComicArchive =
(path: string, options: any): any => (path: string, options: any): any =>
async (dispatch) => { async (dispatch) => {
dispatch({ dispatch({
type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS, type: IMS_COMIC_BOOK_ARCHIVE_EXTRACTION_CALL_IN_PROGRESS,
}); });
await axios({ await axios({
method: "POST", method: "POST",
url: `${LIBRARY_SERVICE_BASE_URI}/uncompressFullArchive`, url: `${LIBRARY_SERVICE_BASE_URI}/uncompressFullArchive`,
headers: { headers: {
"Content-Type": "application/json; charset=utf-8", "Content-Type": "application/json; charset=utf-8",
}, },
data: { data: {
filePath: path, filePath: path,
options, options,
}, },
}); });
}; };
/** /**
* Description * Description
@@ -317,7 +301,7 @@ export const searchIssue = (query, options) => async (dispatch) => {
case "wantedComicsPage": case "wantedComicsPage":
dispatch({ dispatch({
type: WANTED_COMICS_FETCHED, type: WANTED_COMICS_FETCHED,
data: response.data.hits, data: response.data.body,
}); });
break; break;
case "globalSearchBar": case "globalSearchBar":
@@ -330,13 +314,13 @@ export const searchIssue = (query, options) => async (dispatch) => {
case "libraryPage": case "libraryPage":
dispatch({ dispatch({
type: SS_SEARCH_RESULTS_FETCHED, type: SS_SEARCH_RESULTS_FETCHED,
data: response.data.hits, data: response.data.body,
}); });
break; break;
case "volumesPage": case "volumesPage":
dispatch({ dispatch({
type: VOLUMES_FETCHED, type: VOLUMES_FETCHED,
data: response.data.hits, data: response.data.body,
}); });
break; break;
@@ -365,4 +349,4 @@ export const analyzeImage =
type: IMG_ANALYSIS_DATA_FETCH_SUCCESS, type: IMG_ANALYSIS_DATA_FETCH_SUCCESS,
result: foo.data, result: foo.data,
}); });
}; };

View File

@@ -15,15 +15,14 @@ export const AirDCPPSettingsForm = (): ReactElement => {
try { try {
if (!isUndefined(hostname)) { if (!isUndefined(hostname)) {
const matches = hostname.match(hostnameRegex); const matches = hostname.match(hostnameRegex);
return isNil(matches) && matches.length !== 0 return (isNil(matches) && matches.length !== 0) ? hostname : "Invalid hostname; it should not contain special characters";
? hostname
: "Invalid hostname; it should not contain special characters";
} }
} catch { }
catch {
return null; return null;
} }
}; }
const onSubmit = useCallback(async (values) => { const onSubmit = useCallback(async (values) => {
try { try {
airDCPPSettings.setSettings(values); airDCPPSettings.setSettings(values);
@@ -40,7 +39,7 @@ export const AirDCPPSettingsForm = (): ReactElement => {
airDCPPSettings.setSettings({}); airDCPPSettings.setSettings({});
dispatch(deleteSettings()); dispatch(deleteSettings());
}, []); }, []);
const validate = async () => {}; const validate = async () => { };
const initFormData = !isUndefined( const initFormData = !isUndefined(
airDCPPSettings.airDCPPState.settings.directConnect, airDCPPSettings.airDCPPState.settings.directConnect,
) )
@@ -68,20 +67,13 @@ export const AirDCPPSettingsForm = (): ReactElement => {
</span> </span>
</p> </p>
<div className="control is-expanded"> <div className="control is-expanded">
<Field name="hostname" validate={hostValidator}> <Field
name="hostname"
validate={hostValidator}>
{({ input, meta }) => ( {({ input, meta }) => (
<div> <div>
<input <input {...input} type="text" placeholder="AirDC++ hostname" className="input" />
{...input} {meta.error && meta.touched && <span className="is-size-7 has-text-danger">{meta.error}</span>}
type="text"
placeholder="AirDC++ hostname"
className="input"
/>
{meta.error && meta.touched && (
<span className="is-size-7 has-text-danger">
{meta.error}
</span>
)}
</div> </div>
)} )}
</Field> </Field>

View File

@@ -1,5 +1,7 @@
import React, { useEffect, useContext, ReactElement } from "react"; import React, { useEffect, useContext, ReactElement } from "react";
import { getBundlesForComic } from "../../actions/airdcpp.actions"; import {
getBundlesForComic,
} from "../../actions/airdcpp.actions";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { RootState } from "threetwo-ui-typings"; import { RootState } from "threetwo-ui-typings";
import { isEmpty, isNil, map } from "lodash"; import { isEmpty, isNil, map } from "lodash";

View File

@@ -1,7 +1,7 @@
import React, { ReactElement } from "react"; import React, { ReactElement } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import prettyBytes from "pretty-bytes"; import prettyBytes from "pretty-bytes";
import { isEmpty } from "lodash"; import { isUndefined } from "lodash";
export const RawFileDetails = (props): ReactElement => { export const RawFileDetails = (props): ReactElement => {
const { rawFileDetails, inferredMetadata } = props.data; const { rawFileDetails, inferredMetadata } = props.data;
@@ -34,14 +34,6 @@ export const RawFileDetails = (props): ReactElement => {
</span> </span>
</div> </div>
</div> </div>
<div className="control">
<div className="tags has-addons">
<span className="tag">MIME type</span>
<span className="tag is-primary is-light">
{rawFileDetails.mimeType}
</span>
</div>
</div>
</div> </div>
</dd> </dd>
</dl> </dl>
@@ -61,7 +53,7 @@ export const RawFileDetails = (props): ReactElement => {
</span> </span>
</div> </div>
</div> </div>
{!isEmpty(inferredMetadata.issue.number) ? ( {!isUndefined(inferredMetadata.issue.number) ? (
<div className="control"> <div className="control">
<div className="tags has-addons"> <div className="tags has-addons">
<span className="tag">Number</span> <span className="tag">Number</span>
@@ -89,7 +81,6 @@ RawFileDetails.propTypes = {
fileSize: PropTypes.number, fileSize: PropTypes.number,
path: PropTypes.string, path: PropTypes.string,
extension: PropTypes.string, extension: PropTypes.string,
mimeType: PropTypes.string,
cover: PropTypes.shape({ cover: PropTypes.shape({
filePath: PropTypes.string, filePath: PropTypes.string,
}), }),

View File

@@ -20,7 +20,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
useEffect(() => { useEffect(() => {
dispatch( dispatch(
getWeeklyPullList({ getWeeklyPullList({
startDate: "2023-5-25", startDate: "2023-1-25",
pageSize: "15", pageSize: "15",
currentPage: "1", currentPage: "1",
}), }),
@@ -127,7 +127,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
<Slider {...settings} ref={(c) => (sliderRef = c)}> <Slider {...settings} ref={(c) => (sliderRef = c)}>
{!isNil(pullList) && {!isNil(pullList) &&
pullList && pullList &&
map(pullList, ({ issue }, idx) => { map(pullList, ({issue}, idx) => {
return ( return (
<Card <Card
key={idx} key={idx}
@@ -161,4 +161,4 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
); );
}; };
export default PullList; export default PullList;

View File

@@ -83,14 +83,14 @@ export const RecentlyImported = ({
{/* Raw file presence */} {/* Raw file presence */}
{isNil(rawFileDetails) && ( {isNil(rawFileDetails) && (
<span className="icon custom-icon is-small has-text-danger mr-2"> <span className="icon custom-icon is-small has-text-danger mr-2">
<img src="/src/client/assets/img/missing-file.svg" /> <img src="/src/client/img/missing-file.svg" />
</span> </span>
)} )}
{/* ComicInfo.xml presence */} {/* ComicInfo.xml presence */}
{!isNil(comicInfo) && !isEmpty(comicInfo) && ( {!isNil(comicInfo) && !isEmpty(comicInfo) && (
<span className="icon custom-icon is-small has-text-danger"> <span className="icon custom-icon is-small has-text-danger">
<img <img
src="/src/client/assets/img/comicinfoxml.svg" src="/src/client/img/comicinfoxml.svg"
alt={"ComicInfo.xml file detected."} alt={"ComicInfo.xml file detected."}
/> />
</span> </span>
@@ -125,7 +125,7 @@ export const RecentlyImported = ({
<dd className="is-size-9"> <dd className="is-size-9">
<dl> <dl>
<span className="icon custom-icon"> <span className="icon custom-icon">
<img src={`/src/client/assets/img/${icon}`} /> <img src={`/img/${icon}`} />
</span> </span>
</dl> </dl>
<dl> <dl>

View File

@@ -87,7 +87,7 @@ export const WantedComicsList = ({
)} )}
{!isEmpty(locg) && ( {!isEmpty(locg) && (
<span className="icon custom-icon"> <span className="icon custom-icon">
<img src="/src/client/assets/img/locglogo.svg" /> <img src="/src/client/img/locglogo.svg" />
</span> </span>
)} )}
{/* Issue type */} {/* Issue type */}

View File

@@ -3,6 +3,7 @@ import { getTransfers } from "../../actions/airdcpp.actions";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { AirDCPPSocketContext } from "../../context/AirDCPPSocket"; import { AirDCPPSocketContext } from "../../context/AirDCPPSocket";
import { isEmpty, isNil, isUndefined } from "lodash"; import { isEmpty, isNil, isUndefined } from "lodash";
import { searchIssue } from "../../actions/fileops.actions";
import { determineCoverFile } from "../../shared/utils/metadata.utils"; import { determineCoverFile } from "../../shared/utils/metadata.utils";
import MetadataPanel from "../shared/MetadataPanel"; import MetadataPanel from "../shared/MetadataPanel";
@@ -46,6 +47,7 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
}) })
setBundles(foo); setBundles(foo);
} }
}, [issueBundles]) }, [issueBundles])
return !isNil(bundles) ? return !isNil(bundles) ?
@@ -54,9 +56,9 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
<h1 className="title">Downloads</h1> <h1 className="title">Downloads</h1>
<div className="columns"> <div className="columns">
<div className="column is-half"> <div className="column is-half">
{bundles.map((bundle, idx) => { {bundles.map(bundle => {
console.log(bundle); console.log(bundle);
return <div key={idx}> return <>
<MetadataPanel <MetadataPanel
data={bundle} data={bundle}
imageStyle={{ maxWidth: 80 }} imageStyle={{ maxWidth: 80 }}
@@ -68,33 +70,30 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
margin: "0 0 8px 0", margin: "0 0 8px 0",
}} /> }} />
<table className="table is-size-7"> <table className="table is-size-7">
<thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Size</th> <th>Size</th>
<th>Type</th> <th>Type</th>
<th>Bundle ID</th> <th>Bundle ID</th>
</tr> </tr>
</thead>
<tbody> {bundle.acquisition.directconnect.downloads.map((bundle) => {
{bundle.acquisition.directconnect.downloads.map((bundle, idx) => { return(<tr>
return (<tr key={idx}>
<td>{bundle.name}</td> <td>{bundle.name}</td>
<td>{bundle.size}</td> <td>{bundle.size}</td>
<td>{bundle.type.str}</td> <td>{bundle.type.str}</td>
<td><span className="tag is-warning">{bundle.bundleId}</span></td> <td><span className="tag is-warning">{bundle.bundleId}</span></td>
</tr>) </tr>)
})} })}
</tbody> </table>
</table>
{/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */} {/* <pre>{JSON.stringify(bundle.acquisition.directconnect.downloads, null, 2)}</pre> */}
</div> </>
})} })}
</div> </div>
</div> </div>
</section> </section>
</div> : <div>There are no downloads.</div>; </div> : <div>asd</div>;
}; };
export default Downloads; export default Downloads;

View File

@@ -61,10 +61,9 @@ export const SearchBar = (data: ISearchBarProps): ReactElement => {
margin: "60px 0 0 350px", margin: "60px 0 0 350px",
}} }}
> >
{map(searchResults, (result, idx) => ( {map(searchResults, (result) => (
<MetadataPanel <MetadataPanel
data={result} data={result}
key={idx}
imageStyle={{ maxWidth: 70 }} imageStyle={{ maxWidth: 70 }}
titleStyle={{ fontSize: "0.8rem" }} titleStyle={{ fontSize: "0.8rem" }}
tagsStyle={{ fontSize: "0.7rem" }} tagsStyle={{ fontSize: "0.7rem" }}

View File

@@ -19,7 +19,10 @@ export const Library = (): ReactElement => {
const searchResults = useSelector( const searchResults = useSelector(
(state: RootState) => state.fileOps.libraryComics, (state: RootState) => state.fileOps.libraryComics,
); );
const searchError = useSelector((state: RootState) => state.fileOps.librarySearchError); const searchError = useSelector((state: RootState) => {
console.log(state);
return state.fileOps.librarySearchError;
});
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch( dispatch(
@@ -218,9 +221,9 @@ export const Library = (): ReactElement => {
<div> <div>
<div className="library"> <div className="library">
<T2Table <T2Table
totalPages={searchResults.total.value} totalPages={searchResults.hits.total.value}
columns={columns} columns={columns}
sourceData={searchResults?.hits} sourceData={searchResults?.hits?.hits}
rowClickHandler={navigateToComicDetail} rowClickHandler={navigateToComicDetail}
paginationHandlers={{ paginationHandlers={{
nextPage, nextPage,

View File

@@ -73,7 +73,7 @@ export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
<div className="content is-flex is-flex-direction-row"> <div className="content is-flex is-flex-direction-row">
{!isEmpty(sourcedMetadata.comicvine) && ( {!isEmpty(sourcedMetadata.comicvine) && (
<span className="icon cv-icon is-small"> <span className="icon cv-icon is-small">
<img src="/src/client/assets/img/cvlogo.svg" /> <img src="/dist/img/cvlogo.svg" />
</span> </span>
)} )}
{isNil(rawFileDetails) && ( {isNil(rawFileDetails) && (

View File

@@ -85,7 +85,7 @@ const Navbar: React.FunctionComponent = (props) => {
{downloadProgressTick && <div className="pulsating-circle"></div>} {downloadProgressTick && <div className="pulsating-circle"></div>}
</a> </a>
{!isUndefined(downloadProgressTick) ? ( {!isUndefined(downloadProgressTick) ? (
<div className="navbar-dropdown is-right is-boxed"> <div className="navbar-dropdown is-right">
<a className="navbar-item"> <a className="navbar-item">
<DownloadProgressTick data={downloadProgressTick} /> <DownloadProgressTick data={downloadProgressTick} />
</a> </div> </a> </div>
@@ -98,7 +98,7 @@ const Navbar: React.FunctionComponent = (props) => {
<a className="navbar-link is-arrowless has-text-success"> <a className="navbar-link is-arrowless has-text-success">
<i className="fa-solid fa-bolt"></i> <i className="fa-solid fa-bolt"></i>
</a> </a>
<div className="navbar-dropdown pr-2 pl-2 is-right airdcpp-status is-boxed"> <div className="navbar-dropdown pt-4 pr-2 pl-2 is-right airdcpp-status">
{/* AirDC++ Session Information */} {/* AirDC++ Session Information */}
<p> <p>
@@ -133,7 +133,7 @@ const Navbar: React.FunctionComponent = (props) => {
<a className="navbar-link is-arrowless has-text-danger"> <a className="navbar-link is-arrowless has-text-danger">
<i className="fa-solid fa-bolt"></i> <i className="fa-solid fa-bolt"></i>
</a> </a>
<div className="navbar-dropdown pr-2 pl-2 is-right is-boxed"> <div className="navbar-dropdown pr-2 pl-2 is-right">
<pre> <pre>
{JSON.stringify(socketDisconnectionReason, null, 2)} {JSON.stringify(socketDisconnectionReason, null, 2)}
</pre> </pre>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, ReactElement } from "react"; import React, { useMemo, useCallback, ReactElement } from "react";
import { isNil, isEmpty } from "lodash"; import { isNil, isEmpty } from "lodash";
import { IExtractedComicBookCoverFile, RootState } from "threetwo-ui-typings"; import { IExtractedComicBookCoverFile, RootState } from "threetwo-ui-typings";
import { importToDB } from "../actions/fileops.actions"; import { importToDB } from "../actions/fileops.actions";

View File

@@ -1,25 +0,0 @@
import React, { ReactElement } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { getServiceStatus } from "../../actions/fileops.actions";
export const ServiceStatuses = (): ReactElement => {
const serviceStatus = useSelector(
(state: RootState) => state.fileOps.libraryServiceStatus,
);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getServiceStatus());
}, []);
return (
<div className="is-clearfix">
<div className="mt-4">
<h3 className="title">Core Services</h3>
<h6 className="subtitle has-text-grey-light">
Statuses for core services
</h6>
</div>
<pre>{JSON.stringify(serviceStatus, null, 2)}</pre>
</div>
);
};

View File

@@ -2,7 +2,6 @@ import React, { useState, ReactElement } from "react";
import { AirDCPPSettingsForm } from "./AirDCPPSettings/AirDCPPSettingsForm"; import { AirDCPPSettingsForm } from "./AirDCPPSettings/AirDCPPSettingsForm";
import { AirDCPPHubsForm } from "./AirDCPPSettings/AirDCPPHubsForm"; import { AirDCPPHubsForm } from "./AirDCPPSettings/AirDCPPHubsForm";
import { SystemSettingsForm } from "./SystemSettings/SystemSettingsForm"; import { SystemSettingsForm } from "./SystemSettings/SystemSettingsForm";
import { ServiceStatuses } from "./ServiceStatuses/ServiceStatuses";
import settingsObject from "../constants/settings/settingsMenu.json"; import settingsObject from "../constants/settings/settingsMenu.json";
import { isUndefined, map } from "lodash"; import { isUndefined, map } from "lodash";
@@ -23,10 +22,6 @@ export const Settings = (props: ISettingsProps): ReactElement => {
</div> </div>
), ),
}, },
{
id: "core-service",
content: <ServiceStatuses />,
},
{ {
id: "flushdb", id: "flushdb",
content: ( content: (

View File

@@ -166,8 +166,8 @@ export const Volumes = (props): ReactElement => {
<div> <div>
<div className="library"> <div className="library">
<T2Table <T2Table
sourceData={volumes?.hits} sourceData={volumes?.hits?.hits}
totalPages={volumes.hits.length} totalPages={volumes.hits.hits.length}
paginationHandlers={{ paginationHandlers={{
nextPage: () => {}, nextPage: () => {},
previousPage: () => {}, previousPage: () => {},

View File

@@ -73,8 +73,8 @@ export const WantedComics = (props): ReactElement => {
id: "downloadDetails", id: "downloadDetails",
accessorKey: "acquisition", accessorKey: "acquisition",
cell: data => <ol> cell: data => <ol>
{data.getValue().directconnect.downloads.map((download, idx) => { {data.getValue().directconnect.downloads.map(download => {
return <li className="is-size-7" key={idx}>{download.name}</li>; return <li className="is-size-7">{download.name}</li>;
})} })}
</ol> </ol>
}, },

View File

@@ -57,12 +57,6 @@ export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
> >
{rawFileDetails.extension} {rawFileDetails.extension}
</span> </span>
<span
className="tag is-success is-light has-text-weight-semibold"
style={props.tagsStyle}
>
{rawFileDetails.mimeType}
</span>
<span <span
className="tag is-success is-light" className="tag is-success is-light"
style={props.tagsStyle} style={props.tagsStyle}

View File

@@ -20,6 +20,8 @@ export const T2Table = (tableOptions): ReactElement => {
pageIndex: 1, pageIndex: 1,
pageSize: 15, pageSize: 15,
}); });
console.log(sourceData)
const pagination = useMemo( const pagination = useMemo(
() => ({ () => ({
@@ -79,12 +81,12 @@ export const T2Table = (tableOptions): ReactElement => {
{/* <p>{totalPages} comics in all</p> */} {/* <p>{totalPages} comics in all</p> */}
</div> </div>
<div className="field has-addons"> <div className="field has-addons">
<div className="control"> <p className="control">
<div className="button" onClick={() => goToPreviousPage()}> <i className="fas fa-chevron-left"></i></div> <div className="button" onClick={() => goToPreviousPage()}> <i className="fas fa-chevron-left"></i></div>
</div> </p>
<div className="control"> <p className="control">
<div className="button" onClick={() => goToNextPage()}> <i className="fas fa-chevron-right"></i> </div> <div className="button" onClick={() => goToNextPage()}> <i className="fas fa-chevron-right"></i> </div>
</div> </p>
<div className="field has-addons ml-5"> <div className="field has-addons ml-5">
<p className="control"> <p className="control">

View File

@@ -139,7 +139,4 @@ export const SETTINGS_DB_FLUSH_SUCCESS = "SETTINGS_DB_FLUSH_SUCCESS";
// Metron Metadata // Metron Metadata
export const METRON_DATA_FETCH_SUCCESS = "METRON_DATA_FETCH_SUCCESS"; export const METRON_DATA_FETCH_SUCCESS = "METRON_DATA_FETCH_SUCCESS";
export const METRON_DATA_FETCH_IN_PROGRESS = "METRON_DATA_FETCH_IN_PROGRESS"; export const METRON_DATA_FETCH_IN_PROGRESS = "METRON_DATA_FETCH_IN_PROGRESS";
export const METRON_DATA_FETCH_ERROR = "METRON_DATA_FETCH_ERROR"; export const METRON_DATA_FETCH_ERROR = "METRON_DATA_FETCH_ERROR";
// service health statuses
export const LIBRARY_SERVICE_HEALTH = "LIBRARY_SERVICE_HEALTH";

View File

@@ -8,7 +8,6 @@ export const hostURIBuilder = (options: Record<string, string>): string => {
options.apiPath options.apiPath
); );
}; };
console.log(import.meta);
export const CORS_PROXY_SERVER_URI = hostURIBuilder({ export const CORS_PROXY_SERVER_URI = hostURIBuilder({
protocol: "http", protocol: "http",

View File

@@ -57,21 +57,6 @@
}, },
{ {
"id": 4, "id": 4,
"category": "statuses",
"displayName": "Service Status",
"children": [
{
"id": "core-service",
"displayName": "Core Services"
},
{
"id": "metadata-service",
"displayName": "Metadata Services"
}
]
},
{
"id": 5,
"category": "system", "category": "system",
"displayName": "System", "displayName": "System",
"children": [ "children": [
@@ -82,7 +67,7 @@
] ]
}, },
{ {
"id": 6, "id": 5,
"category": "acknowledgments", "category": "acknowledgments",
"displayName": "Acknowledgments", "displayName": "Acknowledgments",
"children": [ "children": [

View File

@@ -30,8 +30,6 @@ import {
SS_SEARCH_RESULTS_FETCHED_SPECIAL, SS_SEARCH_RESULTS_FETCHED_SPECIAL,
VOLUMES_FETCHED, VOLUMES_FETCHED,
COMICBOOK_EXTRACTION_SUCCESS, COMICBOOK_EXTRACTION_SUCCESS,
LIBRARY_SERVICE_HEALTH,
HEALTH_STATUS_TICK,
} from "../constants/action-types"; } from "../constants/action-types";
import { removeLeadingPeriod } from "../shared/utils/formatting.utils"; import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
import { LIBRARY_SERVICE_HOST } from "../constants/endpoints"; import { LIBRARY_SERVICE_HOST } from "../constants/endpoints";
@@ -60,7 +58,6 @@ const initialState = {
librarySearchResultCount: 0, librarySearchResultCount: 0,
libraryQueueResults: [], libraryQueueResults: [],
librarySearchError: {}, librarySearchError: {},
libraryServiceStatus: {},
}; };
function fileOpsReducer(state = initialState, action) { function fileOpsReducer(state = initialState, action) {
@@ -156,13 +153,13 @@ function fileOpsReducer(state = initialState, action) {
} }
case LS_COVER_EXTRACTED: { case LS_COVER_EXTRACTED: {
console.log("BASH", action); console.log("BASH", action);
if (state.recentComics.length === 5) { if(state.recentComics.length === 5) {
state.recentComics.pop(); state.recentComics.pop();
} }
return { return {
...state, ...state,
librarySearchResultCount: state.librarySearchResultCount + 1, librarySearchResultCount: state.librarySearchResultCount + 1,
recentComics: [...state.recentComics, action.result.data.importResult], recentComics: [...state.recentComics, action.result.data.importResult]
}; };
} }
@@ -249,7 +246,7 @@ function fileOpsReducer(state = initialState, action) {
case WANTED_COMICS_FETCHED: { case WANTED_COMICS_FETCHED: {
const foo = []; const foo = [];
if (!isUndefined(action.data.hits)) { if (!isUndefined(action.data.hits)) {
map(action.data.hits, ({ _source }) => { map(action.data.hits.hits, ({ _source }) => {
foo.push(_source); foo.push(_source);
}); });
} }
@@ -274,12 +271,7 @@ function fileOpsReducer(state = initialState, action) {
SSCallInProgress: false, SSCallInProgress: false,
}; };
} }
case LIBRARY_SERVICE_HEALTH: {
return {
...state,
libraryServiceStatus: action.status,
};
}
case FILEOPS_STATE_RESET: { case FILEOPS_STATE_RESET: {
return { return {
...state, ...state,
@@ -291,4 +283,4 @@ function fileOpsReducer(state = initialState, action) {
} }
} }
export default fileOpsReducer; export default fileOpsReducer;

View File

@@ -30,20 +30,20 @@ RawFileDetails.args = {
}, },
"inferredMetadata": { "inferredMetadata": {
"issue": { "issue": {
"name": "Batman Annual 02", "name": "Fantastic Four 143",
"number": 197402, "number": 197402,
"year": "1974", "year": "1974",
"subtitle": "" "subtitle": ""
} }
}, },
"rawFileDetails": { "rawFileDetails": {
"name": "Batman Annual 02", "name": "197402 Fantastic Four 143",
"filePath": "/Users/rishi/work/threetwo-core-service/comics/197402 Fantastic Four 143.cbz", "filePath": "/Users/rishi/work/threetwo-core-service/comics/197402 Fantastic Four 143.cbz",
"fileSize": 10341074, "fileSize": 10341074,
"extension": ".cbz", "extension": ".cbz",
"containedIn": "./userdata/covers/197402 Fantastic Four 143", "containedIn": "./userdata/covers/197402 Fantastic Four 143",
"cover": { "cover": {
"filePath": "userdata/covers/Batman Annual 02/Batman 002_00a.jpg" "filePath": "userdata/covers/197402 Fantastic Four 143/FF143_01.jpg"
} }
}, },
"sourcedMetadata": { "comicInfo": null, "comicvine": { "aliases": [] } }, "sourcedMetadata": { "comicInfo": null, "comicvine": { "aliases": [] } },

View File

@@ -6,7 +6,6 @@ export default defineConfig({
publicDir: "public", publicDir: "public",
base: "", base: "",
build: "esnext", build: "esnext",
server: { host: true },
plugins: [ plugins: [
nodeResolve({ nodeResolve({
// browser: true // browser: true

3556
yarn.lock

File diff suppressed because it is too large Load Diff