Compare commits
4 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2ada7d88e | ||
|
|
b490ab96be | ||
|
|
44721019fa | ||
|
|
15ba2a1caf |
@@ -334,29 +334,75 @@ export const registerMessagingHandlers = ({ socket, client }) => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "tag-added":
|
case "tag-added":
|
||||||
client.cache.modify({
|
// Ensure `job_conversations` is properly formatted
|
||||||
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
const formattedJobConversations = job_conversations.map((jc) => ({
|
||||||
fields: {
|
__typename: "job_conversations",
|
||||||
job_conversations: (existing = []) => [...existing, ...job_conversations]
|
jobid: jc.jobid || jc.job?.id,
|
||||||
|
conversationid: conversationId,
|
||||||
|
job: jc.job || {
|
||||||
|
__typename: "jobs",
|
||||||
|
id: data.selectedJob.id,
|
||||||
|
ro_number: data.selectedJob.ro_number,
|
||||||
|
ownr_co_nm: data.selectedJob.ownr_co_nm,
|
||||||
|
ownr_fn: data.selectedJob.ownr_fn,
|
||||||
|
ownr_ln: data.selectedJob.ownr_ln
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
break;
|
|
||||||
|
|
||||||
case "tag-removed":
|
|
||||||
client.cache.modify({
|
client.cache.modify({
|
||||||
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
||||||
fields: {
|
fields: {
|
||||||
job_conversations: (existing = [], { readField }) => {
|
job_conversations: (existing = []) => {
|
||||||
return existing.filter((jobRef) => {
|
// Ensure no duplicates based on both `conversationid` and `jobid`
|
||||||
// Read the `jobid` field safely, even if the structure is normalized
|
const existingLinks = new Set(
|
||||||
const jobId = readField("jobid", jobRef);
|
existing.map((jc) => {
|
||||||
return jobId !== fields.jobId;
|
const jobId = client.cache.readFragment({
|
||||||
|
id: client.cache.identify(jc),
|
||||||
|
fragment: gql`
|
||||||
|
fragment JobConversationLinkAdded on job_conversations {
|
||||||
|
jobid
|
||||||
|
conversationid
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})?.jobid;
|
||||||
|
return `${jobId}:${conversationId}`; // Unique identifier for a job-conversation link
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const newItems = formattedJobConversations.filter((jc) => {
|
||||||
|
const uniqueLink = `${jc.jobid}:${jc.conversationid}`;
|
||||||
|
return !existingLinks.has(uniqueLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return [...existing, ...newItems];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "tag-removed":
|
||||||
|
try {
|
||||||
|
const conversationCacheId = client.cache.identify({ __typename: "conversations", id: conversationId });
|
||||||
|
|
||||||
|
// Evict the specific cache entry for job_conversations
|
||||||
|
client.cache.evict({
|
||||||
|
id: conversationCacheId,
|
||||||
|
fieldName: "job_conversations"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Garbage collect evicted entries
|
||||||
|
client.cache.gc();
|
||||||
|
|
||||||
|
logLocal("handleConversationChanged - tag removed - Refetched conversation list after state change", {
|
||||||
|
conversationId,
|
||||||
|
type
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error refetching queries after conversation state change: (Tag Removed)", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logLocal("handleConversationChanged - Unhandled type", { type });
|
logLocal("handleConversationChanged - Unhandled type", { type });
|
||||||
client.cache.modify({
|
client.cache.modify({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Virtuoso } from "react-virtuoso";
|
import { Virtuoso } from "react-virtuoso";
|
||||||
import { renderMessage } from "./renderMessage";
|
import { renderMessage } from "./renderMessage";
|
||||||
import "./chat-message-list.styles.scss";
|
import "./chat-message-list.styles.scss";
|
||||||
@@ -16,7 +16,7 @@ export default function ChatMessageListComponent({ messages }) {
|
|||||||
loadedImagesRef.current = 0;
|
loadedImagesRef.current = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const preloadImages = (imagePaths, onComplete) => {
|
const preloadImages = useCallback((imagePaths, onComplete) => {
|
||||||
resetImageLoadState();
|
resetImageLoadState();
|
||||||
|
|
||||||
if (imagePaths.length === 0) {
|
if (imagePaths.length === 0) {
|
||||||
@@ -34,7 +34,7 @@ export default function ChatMessageListComponent({ messages }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
// Ensure all images are loaded on initial render
|
// Ensure all images are loaded on initial render
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -51,7 +51,7 @@ export default function ChatMessageListComponent({ messages }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [messages]);
|
}, [messages, preloadImages]);
|
||||||
|
|
||||||
// Handle scrolling when new messages are added
|
// Handle scrolling when new messages are added
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -69,7 +69,7 @@ export default function ChatMessageListComponent({ messages }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [messages, atBottom]);
|
}, [messages, atBottom, preloadImages]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chat">
|
<div className="chat">
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons";
|
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons";
|
||||||
import { useApolloClient, useLazyQuery } from "@apollo/client";
|
import { useApolloClient, useLazyQuery, useQuery } from "@apollo/client";
|
||||||
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
|
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
|
import { CONVERSATION_LIST_QUERY, UNREAD_CONVERSATION_COUNT } from "../../graphql/conversations.queries";
|
||||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||||
import { selectChatVisible, selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import { selectChatVisible, selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||||
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
||||||
@@ -38,6 +38,14 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
|
|||||||
...(pollInterval > 0 ? { pollInterval } : {})
|
...(pollInterval > 0 ? { pollInterval } : {})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Query for unread count when chat is not visible
|
||||||
|
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
skip: chatVisible, // Skip when chat is visible
|
||||||
|
...(pollInterval > 0 ? { pollInterval } : {})
|
||||||
|
});
|
||||||
|
|
||||||
// Socket connection status
|
// Socket connection status
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleSocketStatus = () => {
|
const handleSocketStatus = () => {
|
||||||
@@ -77,23 +85,29 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
|
|||||||
|
|
||||||
// Get unread count from the cache
|
// Get unread count from the cache
|
||||||
const unreadCount = (() => {
|
const unreadCount = (() => {
|
||||||
try {
|
if (chatVisible) {
|
||||||
const cachedData = client.readQuery({
|
try {
|
||||||
query: CONVERSATION_LIST_QUERY,
|
const cachedData = client.readQuery({
|
||||||
variables: { offset: 0 }
|
query: CONVERSATION_LIST_QUERY,
|
||||||
});
|
variables: { offset: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
if (!cachedData?.conversations) return 0;
|
if (!cachedData?.conversations) return 0;
|
||||||
|
|
||||||
// Aggregate unread message count
|
// Aggregate unread message count
|
||||||
return cachedData.conversations.reduce((total, conversation) => {
|
return cachedData.conversations.reduce((total, conversation) => {
|
||||||
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
|
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
|
||||||
return total + unread;
|
return total + unread;
|
||||||
}, 0);
|
}, 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("Unread count not found in cache:", error);
|
console.warn("Unread count not found in cache:", error);
|
||||||
return 0; // Fallback if not in cache
|
return 0; // Fallback if not in cache
|
||||||
|
}
|
||||||
|
} else if (unreadData?.messages_aggregate?.aggregate?.count) {
|
||||||
|
// Use the unread count from the query result
|
||||||
|
return unreadData.messages_aggregate.aggregate.count;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -52,20 +52,26 @@ export function ChatTagRoContainer({ conversation, bodyshop }) {
|
|||||||
// Find the job details from the search data
|
// Find the job details from the search data
|
||||||
const selectedJob = data?.search_jobs.find((job) => job.id === option.key);
|
const selectedJob = data?.search_jobs.find((job) => job.id === option.key);
|
||||||
if (!selectedJob) return;
|
if (!selectedJob) return;
|
||||||
const newJobConversation = {
|
|
||||||
__typename: "job_conversations",
|
|
||||||
jobid: selectedJob.id,
|
|
||||||
conversationid: conversation.id,
|
|
||||||
job: {
|
|
||||||
__typename: "jobs",
|
|
||||||
...selectedJob
|
|
||||||
}
|
|
||||||
};
|
|
||||||
socket.emit("conversation-modified", {
|
socket.emit("conversation-modified", {
|
||||||
conversationId: conversation.id,
|
conversationId: conversation.id,
|
||||||
bodyshopId: bodyshop.id,
|
bodyshopId: bodyshop.id,
|
||||||
type: "tag-added",
|
type: "tag-added",
|
||||||
job_conversations: [newJobConversation]
|
selectedJob,
|
||||||
|
job_conversations: [
|
||||||
|
{
|
||||||
|
__typename: "job_conversations",
|
||||||
|
jobid: selectedJob.id,
|
||||||
|
conversationid: conversation.id,
|
||||||
|
job: {
|
||||||
|
__typename: "jobs",
|
||||||
|
id: selectedJob.id,
|
||||||
|
ro_number: selectedJob.ro_number,
|
||||||
|
ownr_co_nm: selectedJob.ownr_co_nm,
|
||||||
|
ownr_fn: selectedJob.ownr_fn,
|
||||||
|
ownr_ln: selectedJob.ownr_ln
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { setContext } from "@apollo/client/link/context";
|
|||||||
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
|
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
|
||||||
import { RetryLink } from "@apollo/client/link/retry";
|
import { RetryLink } from "@apollo/client/link/retry";
|
||||||
import { WebSocketLink } from "@apollo/client/link/ws";
|
import { WebSocketLink } from "@apollo/client/link/ws";
|
||||||
import { getMainDefinition, offsetLimitPagination } from "@apollo/client/utilities";
|
import { getMainDefinition } from "@apollo/client/utilities";
|
||||||
//import { split } from "apollo-link";
|
//import { split } from "apollo-link";
|
||||||
import apolloLogger from "apollo-link-logger";
|
import apolloLogger from "apollo-link-logger";
|
||||||
//import axios from "axios";
|
//import axios from "axios";
|
||||||
@@ -143,36 +143,7 @@ middlewares.push(
|
|||||||
new SentryLink().concat(roundTripLink.concat(retryLink.concat(errorLink.concat(authLink.concat(link)))))
|
new SentryLink().concat(roundTripLink.concat(retryLink.concat(errorLink.concat(authLink.concat(link)))))
|
||||||
);
|
);
|
||||||
|
|
||||||
const cache = new InMemoryCache({
|
const cache = new InMemoryCache({});
|
||||||
typePolicies: {
|
|
||||||
conversations: {
|
|
||||||
fields: {
|
|
||||||
job_conversations: {
|
|
||||||
merge(existing = [], incoming = [], { readField }) {
|
|
||||||
const merged = new Map();
|
|
||||||
|
|
||||||
// Add existing data to the map
|
|
||||||
existing.forEach((jobConversation) => {
|
|
||||||
// Use `readField` to get the unique `jobid`, fallback to `__ref`
|
|
||||||
const jobId = readField("jobid", jobConversation) || jobConversation.__ref;
|
|
||||||
if (jobId) merged.set(jobId, jobConversation);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add or replace with incoming data
|
|
||||||
incoming.forEach((jobConversation) => {
|
|
||||||
// Use `readField` to get the unique `jobid`, fallback to `__ref`
|
|
||||||
const jobId = readField("jobid", jobConversation) || jobConversation.__ref;
|
|
||||||
if (jobId) merged.set(jobId, jobConversation);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the merged data as an array
|
|
||||||
return Array.from(merged.values());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const client = new ApolloClient({
|
const client = new ApolloClient({
|
||||||
link: ApolloLink.from(middlewares),
|
link: ApolloLink.from(middlewares),
|
||||||
cache,
|
cache,
|
||||||
|
|||||||
304
package-lock.json
generated
304
package-lock.json
generated
@@ -41,7 +41,6 @@
|
|||||||
"intuit-oauth": "^4.1.3",
|
"intuit-oauth": "^4.1.3",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
"json-2-csv": "^5.5.6",
|
"json-2-csv": "^5.5.6",
|
||||||
"juice": "^11.0.0",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-timezone": "^0.5.46",
|
"moment-timezone": "^0.5.46",
|
||||||
@@ -3360,15 +3359,6 @@
|
|||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-colors": {
|
|
||||||
"version": "4.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
|
||||||
"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
@@ -3937,15 +3927,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commander": {
|
|
||||||
"version": "12.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
|
||||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/component-emitter": {
|
"node_modules/component-emitter": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
|
||||||
@@ -4623,31 +4604,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/encoding-sniffer": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "^0.6.3",
|
|
||||||
"whatwg-encoding": "^3.1.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/encoding-sniffer/node_modules/iconv-lite": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
@@ -4734,18 +4690,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escape-goat": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/escape-html": {
|
"node_modules/escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
@@ -6102,69 +6046,6 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/juice": {
|
|
||||||
"version": "11.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/juice/-/juice-11.0.0.tgz",
|
|
||||||
"integrity": "sha512-sGF8hPz9/Wg+YXbaNDqc1Iuoaw+J/P9lBHNQKXAGc9pPNjCd4fyPai0Zxj7MRtdjMr0lcgk5PjEIkP2b8R9F3w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cheerio": "^1.0.0",
|
|
||||||
"commander": "^12.1.0",
|
|
||||||
"mensch": "^0.3.4",
|
|
||||||
"slick": "^1.12.2",
|
|
||||||
"web-resource-inliner": "^7.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"juice": "bin/juice"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/juice/node_modules/cheerio": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cheerio-select": "^2.1.0",
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.1.0",
|
|
||||||
"encoding-sniffer": "^0.2.0",
|
|
||||||
"htmlparser2": "^9.1.0",
|
|
||||||
"parse5": "^7.1.2",
|
|
||||||
"parse5-htmlparser2-tree-adapter": "^7.0.0",
|
|
||||||
"parse5-parser-stream": "^7.1.2",
|
|
||||||
"undici": "^6.19.5",
|
|
||||||
"whatwg-mimetype": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/juice/node_modules/htmlparser2": {
|
|
||||||
"version": "9.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
|
|
||||||
"integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
|
|
||||||
"funding": [
|
|
||||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/fb55"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.1.0",
|
|
||||||
"entities": "^4.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jwa": {
|
"node_modules/jwa": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||||
@@ -6417,12 +6298,6 @@
|
|||||||
"cssom": "^0.5.0"
|
"cssom": "^0.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mensch": {
|
|
||||||
"version": "0.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz",
|
|
||||||
"integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/merge-descriptors": {
|
"node_modules/merge-descriptors": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||||
@@ -6905,18 +6780,6 @@
|
|||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse5-parser-stream": {
|
|
||||||
"version": "7.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
|
|
||||||
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"parse5": "^7.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
"node_modules/parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
@@ -8431,15 +8294,6 @@
|
|||||||
"node": ">= 4.0.0"
|
"node": ">= 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
|
||||||
"version": "6.21.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz",
|
|
||||||
"integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.19.8",
|
"version": "6.19.8",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||||
@@ -8501,15 +8355,6 @@
|
|||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/valid-data-url": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
@@ -8534,131 +8379,6 @@
|
|||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/web-resource-inliner": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-NlfnGF8MY9ZUwFjyq3vOUBx7KwF8bmE+ywR781SB0nWB6MoMxN4BA8gtgP1KGTZo/O/AyWJz7HZpR704eaj4mg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-colors": "^4.1.1",
|
|
||||||
"escape-goat": "^3.0.0",
|
|
||||||
"htmlparser2": "^5.0.0",
|
|
||||||
"mime": "^2.4.6",
|
|
||||||
"valid-data-url": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/dom-serializer": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.0.1",
|
|
||||||
"domhandler": "^4.2.0",
|
|
||||||
"entities": "^2.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": {
|
|
||||||
"version": "4.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
|
||||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/domhandler": {
|
|
||||||
"version": "3.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
|
|
||||||
"integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/domutils": {
|
|
||||||
"version": "2.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
|
||||||
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"dom-serializer": "^1.0.1",
|
|
||||||
"domelementtype": "^2.2.0",
|
|
||||||
"domhandler": "^4.2.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": {
|
|
||||||
"version": "4.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
|
|
||||||
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/entities": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/htmlparser2": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.0.1",
|
|
||||||
"domhandler": "^3.3.0",
|
|
||||||
"domutils": "^2.4.2",
|
|
||||||
"entities": "^2.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/htmlparser2?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/web-resource-inliner/node_modules/mime": {
|
|
||||||
"version": "2.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
|
|
||||||
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"mime": "cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
@@ -8687,30 +8407,6 @@
|
|||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/whatwg-encoding": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "0.6.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-encoding/node_modules/iconv-lite": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-mimetype": {
|
"node_modules/whatwg-mimetype": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
"intuit-oauth": "^4.1.3",
|
"intuit-oauth": "^4.1.3",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
"json-2-csv": "^5.5.6",
|
"json-2-csv": "^5.5.6",
|
||||||
"juice": "^11.0.0",
|
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-timezone": "^0.5.46",
|
"moment-timezone": "^0.5.46",
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
const { isObject } = require("lodash");
|
|
||||||
|
|
||||||
const validateCanvasRequestMiddleware = (req, res, next) => {
|
|
||||||
const { w, h, values, keys, override } = req.body;
|
|
||||||
if (!values || !keys) {
|
|
||||||
return res.status(400).send("Missing required data");
|
|
||||||
}
|
|
||||||
if (override && !isObject(override)) {
|
|
||||||
return res.status(400).send("Override must be an object");
|
|
||||||
}
|
|
||||||
if (w && (!Number.isFinite(w) || w <= 0)) {
|
|
||||||
return res.status(400).send("Width must be a positive number");
|
|
||||||
}
|
|
||||||
if (h && (!Number.isFinite(h) || h <= 0)) {
|
|
||||||
return res.status(400).send("Height must be a positive number");
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = validateCanvasRequestMiddleware;
|
|
||||||
@@ -5,120 +5,89 @@ const logger = require("../utils/logger");
|
|||||||
const { backgroundColors, borderColors } = require("./canvas-colors");
|
const { backgroundColors, borderColors } = require("./canvas-colors");
|
||||||
const { isObject, defaultsDeep, isNumber } = require("lodash");
|
const { isObject, defaultsDeep, isNumber } = require("lodash");
|
||||||
|
|
||||||
let isProcessing = false;
|
|
||||||
const requestQueue = [];
|
|
||||||
|
|
||||||
const processCanvasRequest = async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { w, h, values, keys, override } = req.body;
|
|
||||||
logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override });
|
|
||||||
|
|
||||||
// Set dimensions with defaults
|
|
||||||
const width = isNumber(w) ? w : 500;
|
|
||||||
const height = isNumber(h) ? h : 275;
|
|
||||||
|
|
||||||
const configuration = {
|
|
||||||
type: "doughnut",
|
|
||||||
data: {
|
|
||||||
labels: keys,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: values,
|
|
||||||
backgroundColor: backgroundColors,
|
|
||||||
borderColor: borderColors,
|
|
||||||
borderWidth: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
animation: false,
|
|
||||||
devicePixelRatio: 4,
|
|
||||||
responsive: false,
|
|
||||||
maintainAspectRatio: true,
|
|
||||||
circumference: 180,
|
|
||||||
rotation: -90,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
labels: {
|
|
||||||
boxWidth: 20,
|
|
||||||
font: {
|
|
||||||
family: "'Montserrat'",
|
|
||||||
size: 10,
|
|
||||||
style: "normal",
|
|
||||||
weight: "normal"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
position: "left"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we have a valid override object, merge it with the default configuration object.
|
|
||||||
// This allows for you to override the default configuration with a custom one.
|
|
||||||
const defaults = () => {
|
|
||||||
if (!override || !isObject(override)) {
|
|
||||||
return configuration;
|
|
||||||
}
|
|
||||||
return defaultsDeep(override, configuration);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate chart
|
|
||||||
let canvas = createCanvas(width, height);
|
|
||||||
let ctx = canvas.getContext("2d");
|
|
||||||
let chart = new Chart(ctx, defaults());
|
|
||||||
const result = canvas.toDataURL();
|
|
||||||
|
|
||||||
chart.destroy();
|
|
||||||
canvas.width = 0;
|
|
||||||
canvas.height = 0;
|
|
||||||
ctx = null;
|
|
||||||
canvas = null;
|
|
||||||
chart = null;
|
|
||||||
|
|
||||||
res.status(200).send(result);
|
|
||||||
} catch (error) {
|
|
||||||
if (chart) chart.destroy();
|
|
||||||
if (canvas) {
|
|
||||||
canvas.width = 0;
|
|
||||||
canvas.height = 0;
|
|
||||||
}
|
|
||||||
ctx = null;
|
|
||||||
canvas = null;
|
|
||||||
chart = null;
|
|
||||||
|
|
||||||
logger.log("inbound-canvas-creation", "error", "jsr", null, { error: error.message, stack: error.stack });
|
|
||||||
res.status(500).send("Error generating canvas");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const processNextInQueue = async () => {
|
|
||||||
if (requestQueue.length === 0) {
|
|
||||||
isProcessing = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { req, res } = requestQueue.shift();
|
|
||||||
await processCanvasRequest(req, res);
|
|
||||||
processNextInQueue();
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.canvastest = function (req, res) {
|
exports.canvastest = function (req, res) {
|
||||||
|
//console.log("Incoming test request.", req);
|
||||||
res.status(200).send("OK");
|
res.status(200).send("OK");
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.canvas = async function (req, res) {
|
exports.canvas = function (req, res) {
|
||||||
if (isProcessing) {
|
const { w, h, values, keys, override } = req.body;
|
||||||
if (requestQueue.length >= 100) {
|
//console.log("Incoming Canvas Request:", w, h, values, keys, override);
|
||||||
// Set a maximum queue size
|
logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override });
|
||||||
return res.status(503).send("Server is busy. Please try again later.");
|
// Gate required values
|
||||||
}
|
if (!values || !keys) {
|
||||||
requestQueue.push({ req, res });
|
res.status(400).send("Missing required data");
|
||||||
logger.log("inbound-canvas-creation-queue", "debug", "jsr", null, { queue: requestQueue.length });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isProcessing = true;
|
// Override must be an object if it exists
|
||||||
await processCanvasRequest(req, res);
|
if (override && !isObject(override)) {
|
||||||
processNextInQueue();
|
res.status(400).send("Override must be an object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default Width and Height
|
||||||
|
let [width, height] = [500, 275];
|
||||||
|
|
||||||
|
// Allow for custom width and height
|
||||||
|
if (isNumber(w)) {
|
||||||
|
width = w;
|
||||||
|
}
|
||||||
|
if (isNumber(h)) {
|
||||||
|
height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configuration = {
|
||||||
|
type: "doughnut",
|
||||||
|
data: {
|
||||||
|
labels: keys,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: values,
|
||||||
|
backgroundColor: backgroundColors,
|
||||||
|
borderColor: borderColors,
|
||||||
|
borderWidth: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
devicePixelRatio: 4,
|
||||||
|
responsive: false,
|
||||||
|
maintainAspectRatio: true,
|
||||||
|
circumference: 180,
|
||||||
|
rotation: -90,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
boxWidth: 20,
|
||||||
|
font: {
|
||||||
|
family: "'Montserrat'",
|
||||||
|
size: 10,
|
||||||
|
style: "normal",
|
||||||
|
weight: "normal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
position: "left"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we have a valid override object, merge it with the default configuration object.
|
||||||
|
// This allows for you to override the default configuration with a custom one.
|
||||||
|
const defaults = () => {
|
||||||
|
if (!override || !isObject(override)) {
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
return defaultsDeep(override, configuration);
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).send(
|
||||||
|
(() => {
|
||||||
|
const canvas = createCanvas(width, height);
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
new Chart(ctx, defaults());
|
||||||
|
return canvas.toDataURL();
|
||||||
|
})()
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,36 +3,24 @@ require("dotenv").config({
|
|||||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
});
|
});
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
//const inlineCssTool = require("inline-css");
|
const inlineCssTool = require("inline-css");
|
||||||
const juice = require("juice");
|
|
||||||
|
|
||||||
exports.inlinecss = async (req, res) => {
|
exports.inlinecss = (req, res) => {
|
||||||
//Perform request validation
|
//Perform request validation
|
||||||
|
|
||||||
logger.log("email-inline-css", "DEBUG", req.user.email, null, null);
|
logger.log("email-inline-css", "DEBUG", req.user.email, null, null);
|
||||||
|
|
||||||
const { html, url } = req.body;
|
const { html, url } = req.body;
|
||||||
try {
|
|
||||||
const inlinedHtml = juice(html, {
|
|
||||||
applyAttributesTableElements: false,
|
|
||||||
preserveMediaQueries: false,
|
|
||||||
applyWidthAttributes: false
|
|
||||||
});
|
|
||||||
res.send(inlinedHtml);
|
|
||||||
} catch (error) {
|
|
||||||
logger.log("email-inline-css-error", "ERROR", req.user.email, null, {
|
|
||||||
error
|
|
||||||
});
|
|
||||||
res.send(error.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// inlineCssTool(html, { url: url })
|
inlineCssTool(html, { url: url })
|
||||||
// .then((inlinedHtml) => {
|
.then((inlinedHtml) => {
|
||||||
// res.send(inlinedHtml);
|
res.send(inlinedHtml);
|
||||||
// })
|
})
|
||||||
// .catch((error) => {
|
.catch((error) => {
|
||||||
// logger.log("email-inline-css-error", "ERROR", req.user.email, null, {
|
logger.log("email-inline-css-error", "ERROR", req.user.email, null, {
|
||||||
// error
|
error
|
||||||
// });
|
});
|
||||||
|
|
||||||
// });
|
res.send(error);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ const express = require("express");
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { inlinecss } = require("../render/inlinecss");
|
const { inlinecss } = require("../render/inlinecss");
|
||||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||||
const validateCanvasRequestMiddleware = require("../middleware/validateCanvasRequestMiddleware");
|
|
||||||
const { canvas } = require("../render/canvas-handler");
|
const { canvas } = require("../render/canvas-handler");
|
||||||
|
|
||||||
// Define the route for inline CSS rendering
|
// Define the route for inline CSS rendering
|
||||||
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
|
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
|
||||||
router.post("/canvas", [validateFirebaseIdTokenMiddleware, validateCanvasRequestMiddleware], canvas);
|
router.post("/canvas", validateFirebaseIdTokenMiddleware, canvas);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user