🪢Added resolvers for lib, dashboard endpoints
This commit is contained in:
139
examples/frontend/components/ComicDetailContainer.tsx
Normal file
139
examples/frontend/components/ComicDetailContainer.tsx
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
* ComicDetailContainer - GraphQL Version
|
||||||
|
*
|
||||||
|
* This file should replace the existing ComicDetailContainer.tsx
|
||||||
|
* Location: src/client/components/ComicDetail/ComicDetailContainer.tsx
|
||||||
|
*
|
||||||
|
* Key changes from REST version:
|
||||||
|
* 1. Uses executeGraphQLQuery instead of axios directly
|
||||||
|
* 2. Parses JSON strings from sourcedMetadata
|
||||||
|
* 3. Maps GraphQL 'id' to REST '_id' for backward compatibility
|
||||||
|
* 4. Better error and loading states
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { ReactElement } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { ComicDetail } from "../ComicDetail/ComicDetail";
|
||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { executeGraphQLQuery, transformComicToRestFormat } from "../../services/api/GraphQLApi";
|
||||||
|
import {
|
||||||
|
GET_COMIC_DETAIL_QUERY,
|
||||||
|
ComicDetailQueryResponse
|
||||||
|
} from "../../graphql/queries/comicDetail";
|
||||||
|
|
||||||
|
export const ComicDetailContainer = (): ReactElement | null => {
|
||||||
|
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: comicBookDetailData,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["comicBookMetadata", comicObjectId],
|
||||||
|
queryFn: async () => {
|
||||||
|
// Execute GraphQL query
|
||||||
|
const result = await executeGraphQLQuery<ComicDetailQueryResponse>(
|
||||||
|
GET_COMIC_DETAIL_QUERY,
|
||||||
|
{ id: comicObjectId }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Transform to REST format for backward compatibility
|
||||||
|
const transformedComic = transformComicToRestFormat(result.comic);
|
||||||
|
|
||||||
|
// Return in the format expected by ComicDetail component
|
||||||
|
return {
|
||||||
|
data: transformedComic,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
enabled: !!comicObjectId, // Only run query if we have an ID
|
||||||
|
staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
|
||||||
|
retry: 2, // Retry failed requests twice
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-screen-xl px-4 py-4">
|
||||||
|
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
||||||
|
<strong className="font-bold">Error loading comic: </strong>
|
||||||
|
<span className="block sm:inline">
|
||||||
|
{error instanceof Error ? error.message : 'Unknown error'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-screen-xl px-4 py-4">
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<div className="text-gray-500 dark:text-gray-400">
|
||||||
|
Loading comic details...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
comicBookDetailData?.data && (
|
||||||
|
<ComicDetail
|
||||||
|
data={comicBookDetailData.data}
|
||||||
|
queryClient={queryClient}
|
||||||
|
comicObjectId={comicObjectId}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternative implementation with feature flag for gradual rollout
|
||||||
|
* Uncomment this version if you want to toggle between REST and GraphQL
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
export const ComicDetailContainer = (): ReactElement | null => {
|
||||||
|
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
// Feature flag to toggle between REST and GraphQL
|
||||||
|
const USE_GRAPHQL = import.meta.env.VITE_USE_GRAPHQL === 'true';
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: comicBookDetailData,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["comicBookMetadata", comicObjectId],
|
||||||
|
queryFn: async () => {
|
||||||
|
if (USE_GRAPHQL) {
|
||||||
|
// GraphQL implementation
|
||||||
|
const result = await executeGraphQLQuery<ComicDetailQueryResponse>(
|
||||||
|
GET_COMIC_DETAIL_QUERY,
|
||||||
|
{ id: comicObjectId }
|
||||||
|
);
|
||||||
|
|
||||||
|
const transformedComic = transformComicToRestFormat(result.comic);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: transformedComic,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// REST implementation (fallback)
|
||||||
|
const response = await axios({
|
||||||
|
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
|
||||||
|
method: "POST",
|
||||||
|
data: { id: comicObjectId },
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled: !!comicObjectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ... rest of the component remains the same
|
||||||
|
};
|
||||||
|
*/
|
||||||
248
examples/frontend/graphql-queries/comicDetail.ts
Normal file
248
examples/frontend/graphql-queries/comicDetail.ts
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
/**
|
||||||
|
* GraphQL query to fetch complete comic detail data
|
||||||
|
*
|
||||||
|
* This file should be placed in the frontend project at:
|
||||||
|
* src/client/graphql/queries/comicDetail.ts
|
||||||
|
*
|
||||||
|
* Matches the data structure expected by ComicDetail component
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const GET_COMIC_DETAIL_QUERY = `
|
||||||
|
query GetComicDetail($id: ID!) {
|
||||||
|
comic(id: $id) {
|
||||||
|
id
|
||||||
|
|
||||||
|
# Raw file information
|
||||||
|
rawFileDetails {
|
||||||
|
name
|
||||||
|
filePath
|
||||||
|
fileSize
|
||||||
|
extension
|
||||||
|
mimeType
|
||||||
|
containedIn
|
||||||
|
pageCount
|
||||||
|
archive {
|
||||||
|
uncompressed
|
||||||
|
expandedPath
|
||||||
|
}
|
||||||
|
cover {
|
||||||
|
filePath
|
||||||
|
stats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Inferred metadata from filename parsing
|
||||||
|
inferredMetadata {
|
||||||
|
issue {
|
||||||
|
name
|
||||||
|
number
|
||||||
|
year
|
||||||
|
subtitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sourced metadata from various providers
|
||||||
|
sourcedMetadata {
|
||||||
|
comicInfo
|
||||||
|
comicvine
|
||||||
|
metron
|
||||||
|
gcd
|
||||||
|
locg {
|
||||||
|
name
|
||||||
|
publisher
|
||||||
|
url
|
||||||
|
cover
|
||||||
|
description
|
||||||
|
price
|
||||||
|
rating
|
||||||
|
pulls
|
||||||
|
potw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Import status
|
||||||
|
importStatus {
|
||||||
|
isImported
|
||||||
|
tagged
|
||||||
|
matchedResult {
|
||||||
|
score
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Acquisition/download information
|
||||||
|
acquisition {
|
||||||
|
source {
|
||||||
|
wanted
|
||||||
|
name
|
||||||
|
}
|
||||||
|
directconnect {
|
||||||
|
downloads {
|
||||||
|
bundleId
|
||||||
|
name
|
||||||
|
size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
torrent {
|
||||||
|
infoHash
|
||||||
|
name
|
||||||
|
announce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Timestamps
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TypeScript type for the query response
|
||||||
|
* Generated from GraphQL schema
|
||||||
|
*/
|
||||||
|
export interface ComicDetailQueryResponse {
|
||||||
|
comic: {
|
||||||
|
id: string;
|
||||||
|
rawFileDetails?: {
|
||||||
|
name?: string;
|
||||||
|
filePath?: string;
|
||||||
|
fileSize?: number;
|
||||||
|
extension?: string;
|
||||||
|
mimeType?: string;
|
||||||
|
containedIn?: string;
|
||||||
|
pageCount?: number;
|
||||||
|
archive?: {
|
||||||
|
uncompressed?: boolean;
|
||||||
|
expandedPath?: string;
|
||||||
|
};
|
||||||
|
cover?: {
|
||||||
|
filePath?: string;
|
||||||
|
stats?: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
inferredMetadata?: {
|
||||||
|
issue?: {
|
||||||
|
name?: string;
|
||||||
|
number?: number;
|
||||||
|
year?: string;
|
||||||
|
subtitle?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
sourcedMetadata?: {
|
||||||
|
comicInfo?: string; // JSON string - needs parsing
|
||||||
|
comicvine?: string; // JSON string - needs parsing
|
||||||
|
metron?: string; // JSON string - needs parsing
|
||||||
|
gcd?: string; // JSON string - needs parsing
|
||||||
|
locg?: {
|
||||||
|
name?: string;
|
||||||
|
publisher?: string;
|
||||||
|
url?: string;
|
||||||
|
cover?: string;
|
||||||
|
description?: string;
|
||||||
|
price?: string;
|
||||||
|
rating?: number;
|
||||||
|
pulls?: number;
|
||||||
|
potw?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
importStatus?: {
|
||||||
|
isImported?: boolean;
|
||||||
|
tagged?: boolean;
|
||||||
|
matchedResult?: {
|
||||||
|
score?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
acquisition?: {
|
||||||
|
source?: {
|
||||||
|
wanted?: boolean;
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
directconnect?: {
|
||||||
|
downloads?: Array<{
|
||||||
|
bundleId?: number;
|
||||||
|
name?: string;
|
||||||
|
size?: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
torrent?: Array<{
|
||||||
|
infoHash?: string;
|
||||||
|
name?: string;
|
||||||
|
announce?: string[];
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
createdAt?: string;
|
||||||
|
updatedAt?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal query for basic comic information
|
||||||
|
* Use this when you only need basic details
|
||||||
|
*/
|
||||||
|
export const GET_COMIC_BASIC_QUERY = `
|
||||||
|
query GetComicBasic($id: ID!) {
|
||||||
|
comic(id: $id) {
|
||||||
|
id
|
||||||
|
rawFileDetails {
|
||||||
|
name
|
||||||
|
filePath
|
||||||
|
fileSize
|
||||||
|
pageCount
|
||||||
|
}
|
||||||
|
inferredMetadata {
|
||||||
|
issue {
|
||||||
|
name
|
||||||
|
number
|
||||||
|
year
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for comic metadata only (no file details)
|
||||||
|
* Use this when you only need metadata
|
||||||
|
*/
|
||||||
|
export const GET_COMIC_METADATA_QUERY = `
|
||||||
|
query GetComicMetadata($id: ID!) {
|
||||||
|
comic(id: $id) {
|
||||||
|
id
|
||||||
|
sourcedMetadata {
|
||||||
|
comicInfo
|
||||||
|
comicvine
|
||||||
|
metron
|
||||||
|
gcd
|
||||||
|
locg {
|
||||||
|
name
|
||||||
|
publisher
|
||||||
|
description
|
||||||
|
rating
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canonicalMetadata {
|
||||||
|
title {
|
||||||
|
value
|
||||||
|
provenance {
|
||||||
|
source
|
||||||
|
confidence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
series {
|
||||||
|
value
|
||||||
|
provenance {
|
||||||
|
source
|
||||||
|
confidence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publisher {
|
||||||
|
value
|
||||||
|
provenance {
|
||||||
|
source
|
||||||
|
confidence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
260
examples/frontend/graphql-queries/libraryQueries.ts
Normal file
260
examples/frontend/graphql-queries/libraryQueries.ts
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
/**
|
||||||
|
* GraphQL queries for library operations
|
||||||
|
* Examples for getComicBooks, getComicBookGroups, getLibraryStatistics, and searchIssue
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to get comic books with pagination and filtering
|
||||||
|
*/
|
||||||
|
export const GET_COMIC_BOOKS = `
|
||||||
|
query GetComicBooks($paginationOptions: PaginationOptionsInput!, $predicate: PredicateInput) {
|
||||||
|
getComicBooks(paginationOptions: $paginationOptions, predicate: $predicate) {
|
||||||
|
docs {
|
||||||
|
id
|
||||||
|
canonicalMetadata {
|
||||||
|
title {
|
||||||
|
value
|
||||||
|
provenance {
|
||||||
|
source
|
||||||
|
confidence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
series {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
issueNumber {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
publisher {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
coverImage {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawFileDetails {
|
||||||
|
name
|
||||||
|
filePath
|
||||||
|
fileSize
|
||||||
|
extension
|
||||||
|
}
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
totalDocs
|
||||||
|
limit
|
||||||
|
page
|
||||||
|
totalPages
|
||||||
|
hasNextPage
|
||||||
|
hasPrevPage
|
||||||
|
nextPage
|
||||||
|
prevPage
|
||||||
|
pagingCounter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to get comic book groups (volumes)
|
||||||
|
*/
|
||||||
|
export const GET_COMIC_BOOK_GROUPS = `
|
||||||
|
query GetComicBookGroups {
|
||||||
|
getComicBookGroups {
|
||||||
|
id
|
||||||
|
volumes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
count_of_issues
|
||||||
|
publisher {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
start_year
|
||||||
|
image {
|
||||||
|
medium_url
|
||||||
|
thumb_url
|
||||||
|
}
|
||||||
|
description
|
||||||
|
site_detail_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to get library statistics
|
||||||
|
*/
|
||||||
|
export const GET_LIBRARY_STATISTICS = `
|
||||||
|
query GetLibraryStatistics {
|
||||||
|
getLibraryStatistics {
|
||||||
|
totalDocuments
|
||||||
|
comicDirectorySize {
|
||||||
|
totalSize
|
||||||
|
totalSizeInMB
|
||||||
|
totalSizeInGB
|
||||||
|
fileCount
|
||||||
|
}
|
||||||
|
statistics {
|
||||||
|
fileTypes {
|
||||||
|
id
|
||||||
|
data
|
||||||
|
}
|
||||||
|
publisherWithMostComicsInLibrary {
|
||||||
|
id
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example usage with variables for getComicBooks
|
||||||
|
*/
|
||||||
|
export const exampleGetComicBooksVariables = {
|
||||||
|
paginationOptions: {
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
sort: "-createdAt", // Sort by creation date, descending
|
||||||
|
lean: false,
|
||||||
|
pagination: true,
|
||||||
|
},
|
||||||
|
predicate: {
|
||||||
|
// Optional: Add filters here
|
||||||
|
// Example: { "canonicalMetadata.publisher.value": "Marvel" }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Get first page of comics
|
||||||
|
*/
|
||||||
|
export const exampleGetFirstPage = {
|
||||||
|
query: GET_COMIC_BOOKS,
|
||||||
|
variables: {
|
||||||
|
paginationOptions: {
|
||||||
|
page: 1,
|
||||||
|
limit: 20,
|
||||||
|
sort: "-createdAt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Get comics with specific filters
|
||||||
|
*/
|
||||||
|
export const exampleGetFilteredComics = {
|
||||||
|
query: GET_COMIC_BOOKS,
|
||||||
|
variables: {
|
||||||
|
paginationOptions: {
|
||||||
|
page: 1,
|
||||||
|
limit: 10,
|
||||||
|
},
|
||||||
|
predicate: {
|
||||||
|
"importStatus.isImported": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query to search issues using Elasticsearch
|
||||||
|
*/
|
||||||
|
export const SEARCH_ISSUE = `
|
||||||
|
query SearchIssue($query: SearchIssueQueryInput, $pagination: SearchPaginationInput, $type: SearchType!) {
|
||||||
|
searchIssue(query: $query, pagination: $pagination, type: $type) {
|
||||||
|
hits {
|
||||||
|
total {
|
||||||
|
value
|
||||||
|
relation
|
||||||
|
}
|
||||||
|
max_score
|
||||||
|
hits {
|
||||||
|
_index
|
||||||
|
_id
|
||||||
|
_score
|
||||||
|
_source {
|
||||||
|
id
|
||||||
|
canonicalMetadata {
|
||||||
|
title {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
series {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
issueNumber {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
publisher {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawFileDetails {
|
||||||
|
name
|
||||||
|
filePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
took
|
||||||
|
timed_out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Search all comics
|
||||||
|
*/
|
||||||
|
export const exampleSearchAll = {
|
||||||
|
query: SEARCH_ISSUE,
|
||||||
|
variables: {
|
||||||
|
type: "all",
|
||||||
|
pagination: {
|
||||||
|
size: 10,
|
||||||
|
from: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Search by volume name
|
||||||
|
*/
|
||||||
|
export const exampleSearchByVolumeName = {
|
||||||
|
query: SEARCH_ISSUE,
|
||||||
|
variables: {
|
||||||
|
query: {
|
||||||
|
volumeName: "Spider-Man",
|
||||||
|
},
|
||||||
|
type: "volumeName",
|
||||||
|
pagination: {
|
||||||
|
size: 20,
|
||||||
|
from: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Search wanted comics
|
||||||
|
*/
|
||||||
|
export const exampleSearchWanted = {
|
||||||
|
query: SEARCH_ISSUE,
|
||||||
|
variables: {
|
||||||
|
type: "wanted",
|
||||||
|
pagination: {
|
||||||
|
size: 50,
|
||||||
|
from: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Search volumes
|
||||||
|
*/
|
||||||
|
export const exampleSearchVolumes = {
|
||||||
|
query: SEARCH_ISSUE,
|
||||||
|
variables: {
|
||||||
|
type: "volumes",
|
||||||
|
pagination: {
|
||||||
|
size: 10,
|
||||||
|
from: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
165
examples/frontend/services/GraphQLApi.ts
Normal file
165
examples/frontend/services/GraphQLApi.ts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* GraphQL API Client Utility
|
||||||
|
*
|
||||||
|
* This file should be placed in the frontend project at:
|
||||||
|
* src/client/services/api/GraphQLApi.ts
|
||||||
|
*
|
||||||
|
* Simple wrapper around axios for executing GraphQL queries and mutations
|
||||||
|
* No additional dependencies needed (no Apollo Client)
|
||||||
|
* Works seamlessly with React Query
|
||||||
|
*/
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
// Update this to match your frontend constants file
|
||||||
|
// import { LIBRARY_SERVICE_BASE_URI } from '../../constants/endpoints';
|
||||||
|
const LIBRARY_SERVICE_BASE_URI = process.env.REACT_APP_LIBRARY_SERVICE_BASE_URI || 'http://localhost:3000/api/library';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a GraphQL query against the threetwo-core-service GraphQL endpoint
|
||||||
|
*
|
||||||
|
* @param query - GraphQL query string
|
||||||
|
* @param variables - Query variables
|
||||||
|
* @returns Promise with query result data
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const result = await executeGraphQLQuery<ComicDetailQueryResponse>(
|
||||||
|
* GET_COMIC_DETAIL_QUERY,
|
||||||
|
* { id: 'comic-id-123' }
|
||||||
|
* );
|
||||||
|
* console.log(result.comic.rawFileDetails.name);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const executeGraphQLQuery = async <T = any>(
|
||||||
|
query: string,
|
||||||
|
variables?: Record<string, any>
|
||||||
|
): Promise<T> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${LIBRARY_SERVICE_BASE_URI}/graphql`,
|
||||||
|
{
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// GraphQL can return partial data with errors
|
||||||
|
if (response.data.errors) {
|
||||||
|
console.error('GraphQL errors:', response.data.errors);
|
||||||
|
throw new Error(
|
||||||
|
`GraphQL errors: ${response.data.errors.map((e: any) => e.message).join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('GraphQL query failed:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a GraphQL mutation against the threetwo-core-service GraphQL endpoint
|
||||||
|
*
|
||||||
|
* @param mutation - GraphQL mutation string
|
||||||
|
* @param variables - Mutation variables
|
||||||
|
* @returns Promise with mutation result data
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const result = await executeGraphQLMutation<{ setMetadataField: Comic }>(
|
||||||
|
* SET_METADATA_FIELD_MUTATION,
|
||||||
|
* { comicId: '123', field: 'title', value: 'New Title' }
|
||||||
|
* );
|
||||||
|
* console.log(result.setMetadataField.canonicalMetadata.title);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const executeGraphQLMutation = async <T = any>(
|
||||||
|
mutation: string,
|
||||||
|
variables?: Record<string, any>
|
||||||
|
): Promise<T> => {
|
||||||
|
// Mutations use the same endpoint as queries
|
||||||
|
return executeGraphQLQuery<T>(mutation, variables);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to parse JSON strings from sourcedMetadata
|
||||||
|
* GraphQL returns these fields as JSON strings that need parsing
|
||||||
|
*
|
||||||
|
* @param sourcedMetadata - The sourcedMetadata object from GraphQL response
|
||||||
|
* @returns Parsed sourcedMetadata with JSON fields converted to objects
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const comic = result.comic;
|
||||||
|
* comic.sourcedMetadata = parseSourcedMetadata(comic.sourcedMetadata);
|
||||||
|
* // Now comic.sourcedMetadata.comicInfo is an object, not a string
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const parseSourcedMetadata = (sourcedMetadata: any) => {
|
||||||
|
if (!sourcedMetadata) return sourcedMetadata;
|
||||||
|
|
||||||
|
const parsed = { ...sourcedMetadata };
|
||||||
|
|
||||||
|
// Parse JSON strings
|
||||||
|
if (parsed.comicInfo && typeof parsed.comicInfo === 'string') {
|
||||||
|
try {
|
||||||
|
parsed.comicInfo = JSON.parse(parsed.comicInfo);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to parse comicInfo:', e);
|
||||||
|
parsed.comicInfo = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.comicvine && typeof parsed.comicvine === 'string') {
|
||||||
|
try {
|
||||||
|
parsed.comicvine = JSON.parse(parsed.comicvine);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to parse comicvine:', e);
|
||||||
|
parsed.comicvine = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.metron && typeof parsed.metron === 'string') {
|
||||||
|
try {
|
||||||
|
parsed.metron = JSON.parse(parsed.metron);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to parse metron:', e);
|
||||||
|
parsed.metron = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.gcd && typeof parsed.gcd === 'string') {
|
||||||
|
try {
|
||||||
|
parsed.gcd = JSON.parse(parsed.gcd);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to parse gcd:', e);
|
||||||
|
parsed.gcd = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to transform GraphQL comic response to REST format
|
||||||
|
* Ensures backward compatibility with existing components
|
||||||
|
*
|
||||||
|
* @param comic - Comic object from GraphQL response
|
||||||
|
* @returns Comic object in REST format with _id field
|
||||||
|
*/
|
||||||
|
export const transformComicToRestFormat = (comic: any) => {
|
||||||
|
if (!comic) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: comic.id,
|
||||||
|
...comic,
|
||||||
|
sourcedMetadata: parseSourcedMetadata(comic.sourcedMetadata),
|
||||||
|
};
|
||||||
|
};
|
||||||
87
examples/test-graphql-endpoint.sh
Executable file
87
examples/test-graphql-endpoint.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Test GraphQL Endpoint Script
|
||||||
|
# This script tests the GraphQL endpoint with various queries
|
||||||
|
|
||||||
|
GRAPHQL_URL="http://localhost:3000/graphql"
|
||||||
|
|
||||||
|
echo "🧪 Testing GraphQL Endpoint: $GRAPHQL_URL"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 1: List Comics
|
||||||
|
echo "📚 Test 1: List Comics (first 5)"
|
||||||
|
echo "--------------------------------"
|
||||||
|
curl -s -X POST $GRAPHQL_URL \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"query": "query { comics(limit: 5) { comics { id rawFileDetails { name pageCount } } totalCount } }"
|
||||||
|
}' | jq '.'
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 2: Get Single Comic (you need to replace COMIC_ID)
|
||||||
|
echo "📖 Test 2: Get Single Comic"
|
||||||
|
echo "--------------------------------"
|
||||||
|
echo "⚠️ Replace COMIC_ID with an actual comic ID from your database"
|
||||||
|
read -p "Enter Comic ID (or press Enter to skip): " COMIC_ID
|
||||||
|
|
||||||
|
if [ ! -z "$COMIC_ID" ]; then
|
||||||
|
curl -s -X POST $GRAPHQL_URL \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"query\": \"query GetComic(\$id: ID!) { comic(id: \$id) { id rawFileDetails { name filePath fileSize pageCount } sourcedMetadata { locg { name publisher rating } } } }\",
|
||||||
|
\"variables\": { \"id\": \"$COMIC_ID\" }
|
||||||
|
}" | jq '.'
|
||||||
|
else
|
||||||
|
echo "Skipped"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 3: Get User Preferences
|
||||||
|
echo "⚙️ Test 3: Get User Preferences"
|
||||||
|
echo "--------------------------------"
|
||||||
|
curl -s -X POST $GRAPHQL_URL \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"query": "query { userPreferences(userId: \"default\") { id userId conflictResolution minConfidenceThreshold autoMerge { enabled onImport onMetadataUpdate } } }"
|
||||||
|
}' | jq '.'
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 4: Search Comics
|
||||||
|
echo "🔍 Test 4: Search Comics"
|
||||||
|
echo "--------------------------------"
|
||||||
|
read -p "Enter search term (or press Enter to skip): " SEARCH_TERM
|
||||||
|
|
||||||
|
if [ ! -z "$SEARCH_TERM" ]; then
|
||||||
|
curl -s -X POST $GRAPHQL_URL \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"query\": \"query SearchComics(\$search: String) { comics(search: \$search, limit: 10) { comics { id rawFileDetails { name } } totalCount } }\",
|
||||||
|
\"variables\": { \"search\": \"$SEARCH_TERM\" }
|
||||||
|
}" | jq '.'
|
||||||
|
else
|
||||||
|
echo "Skipped"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Test 5: GraphQL Introspection (get schema info)
|
||||||
|
echo "🔬 Test 5: Introspection - Available Queries"
|
||||||
|
echo "--------------------------------"
|
||||||
|
curl -s -X POST $GRAPHQL_URL \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"query": "{ __schema { queryType { fields { name description } } } }"
|
||||||
|
}' | jq '.data.__schema.queryType.fields[] | {name, description}'
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "✅ GraphQL endpoint tests complete!"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Tips:"
|
||||||
|
echo " - Open http://localhost:3000/graphql in your browser for GraphQL Playground"
|
||||||
|
echo " - Use 'jq' for better JSON formatting (install with: apt-get install jq)"
|
||||||
|
echo " - Check the docs at: docs/FRONTEND_GRAPHQL_INTEGRATION.md"
|
||||||
@@ -27,6 +27,202 @@ export const resolvers = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get comic books with advanced pagination and filtering
|
||||||
|
*/
|
||||||
|
getComicBooks: async (
|
||||||
|
_: any,
|
||||||
|
{
|
||||||
|
paginationOptions,
|
||||||
|
predicate = {},
|
||||||
|
}: {
|
||||||
|
paginationOptions: any;
|
||||||
|
predicate?: any;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const result = await Comic.paginate(predicate, paginationOptions);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching comic books:", error);
|
||||||
|
throw new Error("Failed to fetch comic books");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get comic book groups (volumes with multiple issues)
|
||||||
|
*/
|
||||||
|
getComicBookGroups: async () => {
|
||||||
|
try {
|
||||||
|
const volumes = await Comic.aggregate([
|
||||||
|
{
|
||||||
|
$project: {
|
||||||
|
volumeInfo:
|
||||||
|
"$sourcedMetadata.comicvine.volumeInformation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$unwind: "$volumeInfo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: "$_id",
|
||||||
|
volumes: {
|
||||||
|
$addToSet: "$volumeInfo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$unwind: "$volumes",
|
||||||
|
},
|
||||||
|
{ $sort: { updatedAt: -1 } },
|
||||||
|
{ $skip: 0 },
|
||||||
|
{ $limit: 5 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
return volumes.map((vol) => ({
|
||||||
|
id: vol._id.toString(),
|
||||||
|
volumes: vol.volumes,
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching comic book groups:", error);
|
||||||
|
throw new Error("Failed to fetch comic book groups");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get library statistics
|
||||||
|
*/
|
||||||
|
getLibraryStatistics: async () => {
|
||||||
|
try {
|
||||||
|
const { getSizeOfDirectory } = require("../../utils/file.utils");
|
||||||
|
const { COMICS_DIRECTORY } = require("../../constants/directories");
|
||||||
|
|
||||||
|
const comicDirectorySize = await getSizeOfDirectory(
|
||||||
|
COMICS_DIRECTORY,
|
||||||
|
[".cbz", ".cbr", ".cb7"]
|
||||||
|
);
|
||||||
|
const totalCount = await Comic.countDocuments({});
|
||||||
|
const statistics = await Comic.aggregate([
|
||||||
|
{
|
||||||
|
$facet: {
|
||||||
|
fileTypes: [
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
"rawFileDetails.extension": {
|
||||||
|
$in: [".cbr", ".cbz", ".cb7"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: "$rawFileDetails.extension",
|
||||||
|
data: { $push: "$$ROOT._id" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
issues: [
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
"sourcedMetadata.comicvine.volumeInformation":
|
||||||
|
{
|
||||||
|
$gt: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: "$sourcedMetadata.comicvine.volumeInformation",
|
||||||
|
data: { $push: "$$ROOT._id" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fileLessComics: [
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
rawFileDetails: {
|
||||||
|
$exists: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
issuesWithComicInfoXML: [
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
"sourcedMetadata.comicInfo": {
|
||||||
|
$exists: true,
|
||||||
|
$gt: { $size: 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
publisherWithMostComicsInLibrary: [
|
||||||
|
{
|
||||||
|
$unwind:
|
||||||
|
"$sourcedMetadata.comicvine.volumeInformation.publisher",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: "$sourcedMetadata.comicvine.volumeInformation.publisher.name",
|
||||||
|
count: { $sum: 1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ $sort: { count: -1 } },
|
||||||
|
{ $limit: 1 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalDocuments: totalCount,
|
||||||
|
comicDirectorySize,
|
||||||
|
statistics,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching library statistics:", error);
|
||||||
|
throw new Error("Failed to fetch library statistics");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search issues using Elasticsearch
|
||||||
|
*/
|
||||||
|
searchIssue: async (
|
||||||
|
_: any,
|
||||||
|
{
|
||||||
|
query,
|
||||||
|
pagination = { size: 10, from: 0 },
|
||||||
|
type,
|
||||||
|
}: {
|
||||||
|
query?: { volumeName?: string; issueNumber?: string };
|
||||||
|
pagination?: { size?: number; from?: number };
|
||||||
|
type: string;
|
||||||
|
},
|
||||||
|
context: any
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
// Get broker from context (set up in GraphQL service)
|
||||||
|
const broker = context?.broker;
|
||||||
|
|
||||||
|
if (!broker) {
|
||||||
|
throw new Error("Broker not available in context");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the search service through the broker
|
||||||
|
const result = await broker.call("search.issue", {
|
||||||
|
query: query || {},
|
||||||
|
pagination,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error searching issues:", error);
|
||||||
|
throw new Error(`Failed to search issues: ${error.message}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List comics with pagination and filtering
|
* List comics with pagination and filtering
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -239,6 +239,25 @@ export const typeDefs = gql`
|
|||||||
series: String
|
series: String
|
||||||
): ComicConnection!
|
): ComicConnection!
|
||||||
|
|
||||||
|
# Get comic books with advanced pagination and filtering
|
||||||
|
getComicBooks(
|
||||||
|
paginationOptions: PaginationOptionsInput!
|
||||||
|
predicate: PredicateInput
|
||||||
|
): ComicBooksResult!
|
||||||
|
|
||||||
|
# Get comic book groups (volumes with multiple issues)
|
||||||
|
getComicBookGroups: [ComicBookGroup!]!
|
||||||
|
|
||||||
|
# Get library statistics
|
||||||
|
getLibraryStatistics: LibraryStatistics!
|
||||||
|
|
||||||
|
# Search issues using Elasticsearch
|
||||||
|
searchIssue(
|
||||||
|
query: SearchIssueQueryInput
|
||||||
|
pagination: SearchPaginationInput
|
||||||
|
type: SearchType!
|
||||||
|
): SearchIssueResult!
|
||||||
|
|
||||||
# Get user preferences
|
# Get user preferences
|
||||||
userPreferences(userId: String = "default"): UserPreferences
|
userPreferences(userId: String = "default"): UserPreferences
|
||||||
|
|
||||||
@@ -439,4 +458,162 @@ export const typeDefs = gql`
|
|||||||
message: String
|
message: String
|
||||||
canonicalMetadataResolved: Boolean!
|
canonicalMetadataResolved: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pagination options input
|
||||||
|
input PaginationOptionsInput {
|
||||||
|
page: Int
|
||||||
|
limit: Int
|
||||||
|
sort: String
|
||||||
|
lean: Boolean
|
||||||
|
leanWithId: Boolean
|
||||||
|
offset: Int
|
||||||
|
pagination: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
# Predicate input for filtering
|
||||||
|
# Note: This is a placeholder. In practice, predicates are passed as JSON objects
|
||||||
|
# and handled dynamically in the resolver
|
||||||
|
scalar PredicateInput
|
||||||
|
|
||||||
|
# Comic books result with pagination
|
||||||
|
type ComicBooksResult {
|
||||||
|
docs: [Comic!]!
|
||||||
|
totalDocs: Int!
|
||||||
|
limit: Int!
|
||||||
|
page: Int
|
||||||
|
totalPages: Int!
|
||||||
|
hasNextPage: Boolean!
|
||||||
|
hasPrevPage: Boolean!
|
||||||
|
nextPage: Int
|
||||||
|
prevPage: Int
|
||||||
|
pagingCounter: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Comic book group (volume with issues)
|
||||||
|
type ComicBookGroup {
|
||||||
|
id: ID!
|
||||||
|
volumes: VolumeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Volume information
|
||||||
|
type VolumeInfo {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
count_of_issues: Int
|
||||||
|
publisher: Publisher
|
||||||
|
start_year: String
|
||||||
|
image: VolumeImage
|
||||||
|
description: String
|
||||||
|
site_detail_url: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# Publisher information
|
||||||
|
type Publisher {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
api_detail_url: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# Volume image
|
||||||
|
type VolumeImage {
|
||||||
|
icon_url: String
|
||||||
|
medium_url: String
|
||||||
|
screen_url: String
|
||||||
|
screen_large_url: String
|
||||||
|
small_url: String
|
||||||
|
super_url: String
|
||||||
|
thumb_url: String
|
||||||
|
tiny_url: String
|
||||||
|
original_url: String
|
||||||
|
image_tags: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# Library statistics
|
||||||
|
type LibraryStatistics {
|
||||||
|
totalDocuments: Int!
|
||||||
|
comicDirectorySize: DirectorySize!
|
||||||
|
statistics: [StatisticsFacet!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Directory size information
|
||||||
|
type DirectorySize {
|
||||||
|
totalSize: Float!
|
||||||
|
totalSizeInMB: Float!
|
||||||
|
totalSizeInGB: Float!
|
||||||
|
fileCount: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Statistics facet
|
||||||
|
type StatisticsFacet {
|
||||||
|
fileTypes: [FileTypeStats!]
|
||||||
|
issues: [IssueStats!]
|
||||||
|
fileLessComics: [Comic!]
|
||||||
|
issuesWithComicInfoXML: [Comic!]
|
||||||
|
publisherWithMostComicsInLibrary: [PublisherStats!]
|
||||||
|
}
|
||||||
|
|
||||||
|
# File type statistics
|
||||||
|
type FileTypeStats {
|
||||||
|
id: String!
|
||||||
|
data: [ID!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Issue statistics
|
||||||
|
type IssueStats {
|
||||||
|
id: VolumeInfo
|
||||||
|
data: [ID!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Publisher statistics
|
||||||
|
type PublisherStats {
|
||||||
|
id: String!
|
||||||
|
count: Int!
|
||||||
|
}
|
||||||
|
# Search issue query input
|
||||||
|
input SearchIssueQueryInput {
|
||||||
|
volumeName: String
|
||||||
|
issueNumber: String
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search pagination input
|
||||||
|
input SearchPaginationInput {
|
||||||
|
size: Int
|
||||||
|
from: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search type enum
|
||||||
|
enum SearchType {
|
||||||
|
all
|
||||||
|
volumeName
|
||||||
|
wanted
|
||||||
|
volumes
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search issue result
|
||||||
|
type SearchIssueResult {
|
||||||
|
hits: SearchHits!
|
||||||
|
took: Int
|
||||||
|
timed_out: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search hits
|
||||||
|
type SearchHits {
|
||||||
|
total: SearchTotal!
|
||||||
|
max_score: Float
|
||||||
|
hits: [SearchHit!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search total
|
||||||
|
type SearchTotal {
|
||||||
|
value: Int!
|
||||||
|
relation: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search hit
|
||||||
|
type SearchHit {
|
||||||
|
_index: String!
|
||||||
|
_id: String!
|
||||||
|
_score: Float
|
||||||
|
_source: Comic!
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
583
package-lock.json
generated
583
package-lock.json
generated
@@ -39,11 +39,12 @@
|
|||||||
"leven": "^3.1.0",
|
"leven": "^3.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mkdirp": "^0.5.5",
|
"mkdirp": "^0.5.5",
|
||||||
|
"moleculer-apollo-server": "^0.4.0",
|
||||||
"moleculer-bullmq": "^3.0.0",
|
"moleculer-bullmq": "^3.0.0",
|
||||||
"moleculer-db": "^0.8.23",
|
"moleculer-db": "^0.8.23",
|
||||||
"moleculer-db-adapter-mongoose": "^0.9.2",
|
"moleculer-db-adapter-mongoose": "^0.9.2",
|
||||||
"moleculer-io": "^2.2.0",
|
"moleculer-io": "^2.2.0",
|
||||||
"moleculer-web": "^0.10.8",
|
"moleculer-web": "^0.10.5",
|
||||||
"mongoosastic-ts": "^6.0.3",
|
"mongoosastic-ts": "^6.0.3",
|
||||||
"mongoose": "^6.10.4",
|
"mongoose": "^6.10.4",
|
||||||
"mongoose-paginate-v2": "^1.3.18",
|
"mongoose-paginate-v2": "^1.3.18",
|
||||||
@@ -4265,6 +4266,18 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@whatwg-node/promise-helpers": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.9.8",
|
"version": "0.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
|
||||||
@@ -5718,6 +5731,18 @@
|
|||||||
"yup": "0.32.9"
|
"yup": "0.32.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-inspect": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -5782,6 +5807,12 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dataloader": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.7",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
@@ -8052,6 +8083,15 @@
|
|||||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graphql-subscriptions": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-kZCdevgmzDjGAOqH7GlDmQXYAkuHoKpMlJrqF40HMPhUhM5ZWSFSxCwD/nSi6AkaijmMfsFhoJRGJ27UseCvRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "^15.7.2 || ^16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/graphql-tag": {
|
"node_modules/graphql-tag": {
|
||||||
"version": "2.12.6",
|
"version": "2.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
|
||||||
@@ -8067,6 +8107,32 @@
|
|||||||
"graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
"graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/graphql-ws": {
|
||||||
|
"version": "6.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.7.tgz",
|
||||||
|
"integrity": "sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@fastify/websocket": "^10 || ^11",
|
||||||
|
"crossws": "~0.3",
|
||||||
|
"graphql": "^15.10.1 || ^16",
|
||||||
|
"ws": "^8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@fastify/websocket": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"crossws": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/har-schema": {
|
"node_modules/har-schema": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||||
@@ -10946,6 +11012,438 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moleculer-apollo-server": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moleculer-apollo-server/-/moleculer-apollo-server-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-uHI8XgYyczOMAhS4OdGgq/5jO+JmqRsuJPkcQqXRMXGJk1WjAy35kHkRsJBImFGfLlR4YKlsBVAPqEIs22QAFg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollo/server": "^5.0.0",
|
||||||
|
"@graphql-tools/schema": "^10.0.25",
|
||||||
|
"dataloader": "^2.2.3",
|
||||||
|
"graphql-subscriptions": "^3.0.0",
|
||||||
|
"graphql-ws": "^6.0.6",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"object-hash": "^3.0.0",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20.x.x"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "^16.0.0",
|
||||||
|
"moleculer": "^0.14.0 || ^0.15.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/server": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-E0/2C5Rqp7bWCjaDh4NzYuEPDZ+dltTf2c0FI6GCKJA6GBetVferX3h1//1rS4+NxD36wrJsGGJK+xyT/M3ysg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollo/cache-control-types": "^1.0.3",
|
||||||
|
"@apollo/server-gateway-interface": "^2.0.0",
|
||||||
|
"@apollo/usage-reporting-protobuf": "^4.1.1",
|
||||||
|
"@apollo/utils.createhash": "^3.0.0",
|
||||||
|
"@apollo/utils.fetcher": "^3.0.0",
|
||||||
|
"@apollo/utils.isnodelike": "^3.0.0",
|
||||||
|
"@apollo/utils.keyvaluecache": "^4.0.0",
|
||||||
|
"@apollo/utils.logger": "^3.0.0",
|
||||||
|
"@apollo/utils.usagereporting": "^2.1.0",
|
||||||
|
"@apollo/utils.withrequired": "^3.0.0",
|
||||||
|
"@graphql-tools/schema": "^10.0.0",
|
||||||
|
"async-retry": "^1.2.1",
|
||||||
|
"body-parser": "^2.2.2",
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"finalhandler": "^2.1.0",
|
||||||
|
"loglevel": "^1.6.8",
|
||||||
|
"lru-cache": "^11.1.0",
|
||||||
|
"negotiator": "^1.0.0",
|
||||||
|
"uuid": "^11.1.0",
|
||||||
|
"whatwg-mimetype": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "^16.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/server-gateway-interface": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollo/usage-reporting-protobuf": "^4.1.1",
|
||||||
|
"@apollo/utils.fetcher": "^3.0.0",
|
||||||
|
"@apollo/utils.keyvaluecache": "^4.0.0",
|
||||||
|
"@apollo/utils.logger": "^3.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "14.x || 15.x || 16.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.createhash": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollo/utils.isnodelike": "^3.0.0",
|
||||||
|
"sha.js": "^2.4.11"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.fetcher": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.isnodelike": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.keyvaluecache": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@apollo/utils.logger": "^3.0.0",
|
||||||
|
"lru-cache": "^11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.logger": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@apollo/utils.withrequired": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@graphql-tools/merge": {
|
||||||
|
"version": "9.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.7.tgz",
|
||||||
|
"integrity": "sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@graphql-tools/utils": "^11.0.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@graphql-tools/schema": {
|
||||||
|
"version": "10.0.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.31.tgz",
|
||||||
|
"integrity": "sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@graphql-tools/merge": "^9.1.7",
|
||||||
|
"@graphql-tools/utils": "^11.0.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/@graphql-tools/utils": {
|
||||||
|
"version": "11.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.0.tgz",
|
||||||
|
"integrity": "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@graphql-typed-document-node/core": "^3.1.1",
|
||||||
|
"@whatwg-node/promise-helpers": "^1.0.0",
|
||||||
|
"cross-inspect": "1.0.1",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/body-parser": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "^3.1.2",
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"debug": "^4.4.3",
|
||||||
|
"http-errors": "^2.0.0",
|
||||||
|
"iconv-lite": "^0.7.0",
|
||||||
|
"on-finished": "^2.4.1",
|
||||||
|
"qs": "^6.14.1",
|
||||||
|
"raw-body": "^3.0.1",
|
||||||
|
"type-is": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/debug": {
|
||||||
|
"version": "4.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
|
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/finalhandler": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^4.4.0",
|
||||||
|
"encodeurl": "^2.0.0",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"on-finished": "^2.4.1",
|
||||||
|
"parseurl": "^1.3.3",
|
||||||
|
"statuses": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/http-errors": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"depd": "~2.0.0",
|
||||||
|
"inherits": "~2.0.4",
|
||||||
|
"setprototypeof": "~1.2.0",
|
||||||
|
"statuses": "~2.0.2",
|
||||||
|
"toidentifier": "~1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/iconv-lite": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/lru-cache": {
|
||||||
|
"version": "11.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
|
||||||
|
"integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"engines": {
|
||||||
|
"node": "20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/media-typer": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/mime-db": {
|
||||||
|
"version": "1.54.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||||
|
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/mime-types": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "^1.54.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/negotiator": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/qs": {
|
||||||
|
"version": "6.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
|
||||||
|
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/raw-body": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "~3.1.2",
|
||||||
|
"http-errors": "~2.0.1",
|
||||||
|
"iconv-lite": "~0.7.0",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/statuses": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/type-is": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"media-typer": "^1.1.0",
|
||||||
|
"mime-types": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/uuid": {
|
||||||
|
"version": "11.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||||
|
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/esm/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/whatwg-mimetype": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moleculer-apollo-server/node_modules/ws": {
|
||||||
|
"version": "8.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||||
|
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/moleculer-bullmq": {
|
"node_modules/moleculer-bullmq": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/moleculer-bullmq/-/moleculer-bullmq-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/moleculer-bullmq/-/moleculer-bullmq-3.0.0.tgz",
|
||||||
@@ -14084,10 +14582,20 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-hash": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
|
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@@ -15787,14 +16295,69 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.0.6",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.7",
|
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"get-intrinsic": "^1.2.4",
|
"object-inspect": "^1.13.3",
|
||||||
"object-inspect": "^1.13.1"
|
"side-channel-list": "^1.0.0",
|
||||||
|
"side-channel-map": "^1.0.1",
|
||||||
|
"side-channel-weakmap": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-list": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"object-inspect": "^1.13.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-map": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.5",
|
||||||
|
"object-inspect": "^1.13.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/side-channel-weakmap": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.5",
|
||||||
|
"object-inspect": "^1.13.3",
|
||||||
|
"side-channel-map": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/server": "^4.12.2",
|
"@apollo/server": "^4.12.2",
|
||||||
|
"moleculer-apollo-server": "^0.4.0",
|
||||||
"@bluelovers/fast-glob": "https://github.com/rishighan/fast-glob-v2-api.git",
|
"@bluelovers/fast-glob": "https://github.com/rishighan/fast-glob-v2-api.git",
|
||||||
"@elastic/elasticsearch": "^8.13.1",
|
"@elastic/elasticsearch": "^8.13.1",
|
||||||
"@jorgeferrero/stream-to-buffer": "^2.0.6",
|
"@jorgeferrero/stream-to-buffer": "^2.0.6",
|
||||||
|
|||||||
Reference in New Issue
Block a user