Compare commits
7 Commits
bugfix-#59
...
service-st
| Author | SHA1 | Date | |
|---|---|---|---|
| ae9ec75983 | |||
| b8fb179ac6 | |||
| 94692bb6d4 | |||
| 4b795aca5d | |||
| fbf6bed4fe | |||
|
|
18b18c3d81 | ||
| a939bf4c71 |
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@@ -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@master
|
uses: elgohr/Publish-Docker-Github-Action@v5
|
||||||
with:
|
with:
|
||||||
name: frishi/threetwo
|
name: frishi/threetwo
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:17.3-alpine
|
FROM node:18.15.0-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 python3 git libc6-compat autoconf automake bash libjpeg-turbo-dev libpng-dev mesa-dev mesa libxi build-base gcc libtool nasm
|
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 yarn --ignore-engines
|
RUN yarn --ignore-engines
|
||||||
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
EXPOSE 3050
|
EXPOSE 5173
|
||||||
|
|
||||||
ENTRYPOINT [ "npm", "start" ]
|
ENTRYPOINT [ "npm", "start" ]
|
||||||
@@ -40,9 +40,8 @@ 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:3050` in your default browser
|
3. This will open `http://localhost:5173` in your default browser
|
||||||
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`
|
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.
|
||||||
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
|
||||||
|
|||||||
44
package.json
44
package.json
@@ -7,7 +7,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"dev": "rimraf dist && npm run build && vite",
|
"dev": "rimraf dist && npm run build && vite",
|
||||||
"prod": "npm run build && vite",
|
"start": "npm run build && vite",
|
||||||
"docs": "jsdoc -c jsdoc.json",
|
"docs": "jsdoc -c jsdoc.json",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build"
|
"build-storybook": "storybook build"
|
||||||
@@ -20,32 +20,31 @@
|
|||||||
"@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.1.1",
|
"@fortawesome/fontawesome-free": "^6.3.0",
|
||||||
"@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": "^17.0.3",
|
"@types/react-router-dom": "^5.3.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.1",
|
"airdcpp-apisocket": "~2.4.5-beta.2",
|
||||||
"axios": "^0.27.2",
|
"axios": "^1.3.4",
|
||||||
|
"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.1.0",
|
"ellipsize": "^0.5.1",
|
||||||
"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",
|
||||||
@@ -57,16 +56,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.1.0",
|
"react-dom": "^18.2.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": "^7.2.6",
|
"react-redux": "^8.0.5",
|
||||||
"react-router": "^6.2.2",
|
"react-router": "^6.9.0",
|
||||||
"react-router-dom": "^6.2.2",
|
"react-router-dom": "^6.9.0",
|
||||||
"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",
|
||||||
@@ -79,12 +78,10 @@
|
|||||||
"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.5",
|
"styled-components": "^5.3.9",
|
||||||
"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": {
|
||||||
@@ -103,20 +100,19 @@
|
|||||||
"@storybook/react-vite": "^7.0.0-rc.3",
|
"@storybook/react-vite": "^7.0.0-rc.3",
|
||||||
"@storybook/testing-library": "^0.0.14-next.1",
|
"@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": "^17.0.3",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react-dom": "^17.0.2",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@types/react-redux": "^7.1.16",
|
"@types/react-redux": "^7.1.25",
|
||||||
"@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",
|
||||||
@@ -138,7 +134,7 @@
|
|||||||
"sass": "^1.58.1",
|
"sass": "^1.58.1",
|
||||||
"storybook": "^7.0.0-rc.3",
|
"storybook": "^7.0.0-rc.3",
|
||||||
"tslint": "^6.1.3",
|
"tslint": "^6.1.3",
|
||||||
"typescript": "^4.9",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.1.1"
|
"vite": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import rateLimiter from "axios-rate-limit";
|
import rateLimiter from "axios-rate-limit";
|
||||||
import {
|
import { setupCache } from "axios-cache-interceptor";
|
||||||
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,
|
||||||
@@ -26,25 +22,22 @@ 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 axios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
||||||
method: "get",
|
method: "get",
|
||||||
params: options,
|
params: options,
|
||||||
axiosCacheAdapter,
|
}).then((response) => {
|
||||||
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,
|
||||||
@@ -70,9 +63,6 @@ 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) {
|
||||||
|
|||||||
@@ -34,11 +34,27 @@ 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>>({
|
||||||
@@ -127,49 +143,50 @@ export const getComicBooks = (options) => async (dispatch) => {
|
|||||||
* @returns Nothing.
|
* @returns Nothing.
|
||||||
* @param payload
|
* @param payload
|
||||||
*/
|
*/
|
||||||
export const importToDB = (sourceName: string, metadata?: any) => (dispatch) => {
|
export const importToDB =
|
||||||
try {
|
(sourceName: string, metadata?: any) => (dispatch) => {
|
||||||
const comicBookMetadata = {
|
try {
|
||||||
importType: "new",
|
const comicBookMetadata = {
|
||||||
payload: {
|
importType: "new",
|
||||||
rawFileDetails: {
|
payload: {
|
||||||
name: "",
|
rawFileDetails: {
|
||||||
},
|
name: "",
|
||||||
importStatus: {
|
|
||||||
isImported: true,
|
|
||||||
tagged: false,
|
|
||||||
matchedResult: {
|
|
||||||
score: "0",
|
|
||||||
},
|
},
|
||||||
|
importStatus: {
|
||||||
|
isImported: true,
|
||||||
|
tagged: false,
|
||||||
|
matchedResult: {
|
||||||
|
score: "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourcedMetadata: metadata || null,
|
||||||
|
acquisition: { source: { wanted: true, name: sourceName } },
|
||||||
},
|
},
|
||||||
sourcedMetadata: metadata || null,
|
};
|
||||||
acquisition: { source: { wanted: true, name: sourceName } },
|
dispatch({
|
||||||
}
|
type: IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS,
|
||||||
};
|
|
||||||
dispatch({
|
|
||||||
type: IMS_CV_METADATA_IMPORT_CALL_IN_PROGRESS,
|
|
||||||
});
|
|
||||||
return axios
|
|
||||||
.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) {
|
return axios
|
||||||
dispatch({
|
.request({
|
||||||
type: IMS_CV_METADATA_IMPORT_FAILED,
|
url: `${LIBRARY_SERVICE_BASE_URI}/rawImportToDb`,
|
||||||
importError: error,
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchVolumeGroups = () => async (dispatch) => {
|
export const fetchVolumeGroups = () => async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
@@ -254,24 +271,23 @@ 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
|
||||||
@@ -349,4 +365,4 @@ export const analyzeImage =
|
|||||||
type: IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
type: IMG_ANALYSIS_DATA_FETCH_SUCCESS,
|
||||||
result: foo.data,
|
result: foo.data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,14 +15,15 @@ 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) ? hostname : "Invalid hostname; it should not contain special characters";
|
return isNil(matches) && matches.length !== 0
|
||||||
|
? 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);
|
||||||
@@ -39,7 +40,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,
|
||||||
)
|
)
|
||||||
@@ -67,13 +68,20 @@ export const AirDCPPSettingsForm = (): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div className="control is-expanded">
|
<div className="control is-expanded">
|
||||||
<Field
|
<Field name="hostname" validate={hostValidator}>
|
||||||
name="hostname"
|
|
||||||
validate={hostValidator}>
|
|
||||||
{({ input, meta }) => (
|
{({ input, meta }) => (
|
||||||
<div>
|
<div>
|
||||||
<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>}
|
{...input}
|
||||||
|
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>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import React, { useEffect, useContext, ReactElement } from "react";
|
import React, { useEffect, useContext, ReactElement } from "react";
|
||||||
import {
|
import { getBundlesForComic } from "../../actions/airdcpp.actions";
|
||||||
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";
|
||||||
|
|||||||
@@ -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 { isUndefined } from "lodash";
|
import { isEmpty } from "lodash";
|
||||||
|
|
||||||
export const RawFileDetails = (props): ReactElement => {
|
export const RawFileDetails = (props): ReactElement => {
|
||||||
const { rawFileDetails, inferredMetadata } = props.data;
|
const { rawFileDetails, inferredMetadata } = props.data;
|
||||||
@@ -34,6 +34,14 @@ 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>
|
||||||
@@ -53,7 +61,7 @@ export const RawFileDetails = (props): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!isUndefined(inferredMetadata.issue.number) ? (
|
{!isEmpty(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>
|
||||||
@@ -81,6 +89,7 @@ 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,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
getWeeklyPullList({
|
getWeeklyPullList({
|
||||||
startDate: "2023-1-25",
|
startDate: "2023-5-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;
|
||||||
|
|||||||
@@ -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={`/img/${icon}`} />
|
<img src={`/src/client/assets/img/${icon}`} />
|
||||||
</span>
|
</span>
|
||||||
</dl>
|
</dl>
|
||||||
<dl>
|
<dl>
|
||||||
|
|||||||
@@ -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="/dist/img/cvlogo.svg" />
|
<img src="/src/client/assets/img/cvlogo.svg" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{isNil(rawFileDetails) && (
|
{isNil(rawFileDetails) && (
|
||||||
|
|||||||
@@ -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">
|
<div className="navbar-dropdown is-right is-boxed">
|
||||||
<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 pt-4 pr-2 pl-2 is-right airdcpp-status">
|
<div className="navbar-dropdown pr-2 pl-2 is-right airdcpp-status is-boxed">
|
||||||
{/* 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">
|
<div className="navbar-dropdown pr-2 pl-2 is-right is-boxed">
|
||||||
<pre>
|
<pre>
|
||||||
{JSON.stringify(socketDisconnectionReason, null, 2)}
|
{JSON.stringify(socketDisconnectionReason, null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo, useCallback, ReactElement } from "react";
|
import React, { 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";
|
||||||
|
|||||||
25
src/client/components/ServiceStatuses/ServiceStatuses.tsx
Normal file
25
src/client/components/ServiceStatuses/ServiceStatuses.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@ 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";
|
||||||
|
|
||||||
@@ -22,6 +23,10 @@ export const Settings = (props: ISettingsProps): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "core-service",
|
||||||
|
content: <ServiceStatuses />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "flushdb",
|
id: "flushdb",
|
||||||
content: (
|
content: (
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ 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}
|
||||||
|
|||||||
@@ -139,4 +139,7 @@ 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";
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ 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",
|
||||||
|
|||||||
@@ -57,6 +57,21 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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": [
|
||||||
@@ -67,7 +82,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 5,
|
"id": 6,
|
||||||
"category": "acknowledgments",
|
"category": "acknowledgments",
|
||||||
"displayName": "Acknowledgments",
|
"displayName": "Acknowledgments",
|
||||||
"children": [
|
"children": [
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ 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";
|
||||||
@@ -58,6 +60,7 @@ const initialState = {
|
|||||||
librarySearchResultCount: 0,
|
librarySearchResultCount: 0,
|
||||||
libraryQueueResults: [],
|
libraryQueueResults: [],
|
||||||
librarySearchError: {},
|
librarySearchError: {},
|
||||||
|
libraryServiceStatus: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function fileOpsReducer(state = initialState, action) {
|
function fileOpsReducer(state = initialState, action) {
|
||||||
@@ -153,13 +156,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],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +274,12 @@ 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,
|
||||||
@@ -283,4 +291,4 @@ function fileOpsReducer(state = initialState, action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default fileOpsReducer;
|
export default fileOpsReducer;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export default defineConfig({
|
|||||||
publicDir: "public",
|
publicDir: "public",
|
||||||
base: "",
|
base: "",
|
||||||
build: "esnext",
|
build: "esnext",
|
||||||
|
server: { host: true },
|
||||||
plugins: [
|
plugins: [
|
||||||
nodeResolve({
|
nodeResolve({
|
||||||
// browser: true
|
// browser: true
|
||||||
|
|||||||
Reference in New Issue
Block a user