Compare commits
2 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83a1952880 | ||
|
|
a885bdec74 |
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DD_API_KEY=58d91898a70c6fd659f6eea768a57976 DD_SITE="us3.datadoghq.com" bash -c "$(curl -L https://install.datadoghq.com/scripts/install_script_agent7.sh)"
|
||||
|
||||
echo "Datadog agent installed."
|
||||
@@ -334,75 +334,29 @@ export const registerMessagingHandlers = ({ socket, client }) => {
|
||||
break;
|
||||
|
||||
case "tag-added":
|
||||
// Ensure `job_conversations` is properly formatted
|
||||
const formattedJobConversations = job_conversations.map((jc) => ({
|
||||
__typename: "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
|
||||
}
|
||||
}));
|
||||
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
||||
fields: {
|
||||
job_conversations: (existing = []) => {
|
||||
// Ensure no duplicates based on both `conversationid` and `jobid`
|
||||
const existingLinks = new Set(
|
||||
existing.map((jc) => {
|
||||
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];
|
||||
}
|
||||
job_conversations: (existing = []) => [...existing, ...job_conversations]
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
client.cache.modify({
|
||||
id: client.cache.identify({ __typename: "conversations", id: conversationId }),
|
||||
fields: {
|
||||
job_conversations: (existing = [], { readField }) => {
|
||||
return existing.filter((jobRef) => {
|
||||
// Read the `jobid` field safely, even if the structure is normalized
|
||||
const jobId = readField("jobid", jobRef);
|
||||
return jobId !== fields.jobId;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
logLocal("handleConversationChanged - Unhandled type", { type });
|
||||
client.cache.modify({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { renderMessage } from "./renderMessage";
|
||||
import "./chat-message-list.styles.scss";
|
||||
@@ -16,7 +16,7 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
loadedImagesRef.current = 0;
|
||||
};
|
||||
|
||||
const preloadImages = useCallback((imagePaths, onComplete) => {
|
||||
const preloadImages = (imagePaths, onComplete) => {
|
||||
resetImageLoadState();
|
||||
|
||||
if (imagePaths.length === 0) {
|
||||
@@ -34,7 +34,7 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
}
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Ensure all images are loaded on initial render
|
||||
useEffect(() => {
|
||||
@@ -51,7 +51,7 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [messages, preloadImages]);
|
||||
}, [messages]);
|
||||
|
||||
// Handle scrolling when new messages are added
|
||||
useEffect(() => {
|
||||
@@ -69,7 +69,7 @@ export default function ChatMessageListComponent({ messages }) {
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [messages, atBottom, preloadImages]);
|
||||
}, [messages, atBottom]);
|
||||
|
||||
return (
|
||||
<div className="chat">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined } from "@ant-design/icons";
|
||||
import { useApolloClient, useLazyQuery, useQuery } from "@apollo/client";
|
||||
import { useApolloClient, useLazyQuery } from "@apollo/client";
|
||||
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CONVERSATION_LIST_QUERY, UNREAD_CONVERSATION_COUNT } from "../../graphql/conversations.queries";
|
||||
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
|
||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||
import { selectChatVisible, selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
||||
@@ -38,14 +38,6 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
|
||||
...(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
|
||||
useEffect(() => {
|
||||
const handleSocketStatus = () => {
|
||||
@@ -85,29 +77,23 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh
|
||||
|
||||
// Get unread count from the cache
|
||||
const unreadCount = (() => {
|
||||
if (chatVisible) {
|
||||
try {
|
||||
const cachedData = client.readQuery({
|
||||
query: CONVERSATION_LIST_QUERY,
|
||||
variables: { offset: 0 }
|
||||
});
|
||||
try {
|
||||
const cachedData = client.readQuery({
|
||||
query: CONVERSATION_LIST_QUERY,
|
||||
variables: { offset: 0 }
|
||||
});
|
||||
|
||||
if (!cachedData?.conversations) return 0;
|
||||
if (!cachedData?.conversations) return 0;
|
||||
|
||||
// Aggregate unread message count
|
||||
return cachedData.conversations.reduce((total, conversation) => {
|
||||
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
|
||||
return total + unread;
|
||||
}, 0);
|
||||
} catch (error) {
|
||||
console.warn("Unread count not found in cache:", error);
|
||||
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;
|
||||
// Aggregate unread message count
|
||||
return cachedData.conversations.reduce((total, conversation) => {
|
||||
const unread = conversation.messages_aggregate?.aggregate?.count || 0;
|
||||
return total + unread;
|
||||
}, 0);
|
||||
} catch (error) {
|
||||
console.warn("Unread count not found in cache:", error);
|
||||
return 0; // Fallback if not in cache
|
||||
}
|
||||
return 0;
|
||||
})();
|
||||
|
||||
return (
|
||||
|
||||
@@ -52,26 +52,20 @@ export function ChatTagRoContainer({ conversation, bodyshop }) {
|
||||
// Find the job details from the search data
|
||||
const selectedJob = data?.search_jobs.find((job) => job.id === option.key);
|
||||
if (!selectedJob) return;
|
||||
const newJobConversation = {
|
||||
__typename: "job_conversations",
|
||||
jobid: selectedJob.id,
|
||||
conversationid: conversation.id,
|
||||
job: {
|
||||
__typename: "jobs",
|
||||
...selectedJob
|
||||
}
|
||||
};
|
||||
socket.emit("conversation-modified", {
|
||||
conversationId: conversation.id,
|
||||
bodyshopId: bodyshop.id,
|
||||
type: "tag-added",
|
||||
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
|
||||
}
|
||||
}
|
||||
]
|
||||
job_conversations: [newJobConversation]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { setContext } from "@apollo/client/link/context";
|
||||
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
|
||||
import { RetryLink } from "@apollo/client/link/retry";
|
||||
import { WebSocketLink } from "@apollo/client/link/ws";
|
||||
import { getMainDefinition } from "@apollo/client/utilities";
|
||||
import { getMainDefinition, offsetLimitPagination } from "@apollo/client/utilities";
|
||||
//import { split } from "apollo-link";
|
||||
import apolloLogger from "apollo-link-logger";
|
||||
//import axios from "axios";
|
||||
@@ -143,7 +143,36 @@ middlewares.push(
|
||||
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({
|
||||
link: ApolloLink.from(middlewares),
|
||||
cache,
|
||||
|
||||
749
package-lock.json
generated
749
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -41,7 +41,6 @@
|
||||
"cors": "2.8.5",
|
||||
"crisp-status-reporter": "^1.2.2",
|
||||
"csrf": "^3.1.0",
|
||||
"dd-trace": "^5.28.0",
|
||||
"dinero.js": "^1.9.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.21.1",
|
||||
@@ -52,6 +51,7 @@
|
||||
"intuit-oauth": "^4.1.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"json-2-csv": "^5.5.6",
|
||||
"juice": "^11.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.46",
|
||||
|
||||
@@ -4,14 +4,6 @@ require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV) {
|
||||
const tracer = require("dd-trace").init({
|
||||
profiling: true,
|
||||
env: process.env.NODE_ENV,
|
||||
service: "bodyshop-api"
|
||||
});
|
||||
}
|
||||
|
||||
const cors = require("cors");
|
||||
const http = require("http");
|
||||
const Redis = require("ioredis");
|
||||
|
||||
20
server/middleware/validateCanvasRequestMiddleware.js
Normal file
20
server/middleware/validateCanvasRequestMiddleware.js
Normal file
@@ -0,0 +1,20 @@
|
||||
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,89 +5,120 @@ const logger = require("../utils/logger");
|
||||
const { backgroundColors, borderColors } = require("./canvas-colors");
|
||||
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) {
|
||||
//console.log("Incoming test request.", req);
|
||||
res.status(200).send("OK");
|
||||
};
|
||||
|
||||
exports.canvas = function (req, res) {
|
||||
const { w, h, values, keys, override } = req.body;
|
||||
//console.log("Incoming Canvas Request:", w, h, values, keys, override);
|
||||
logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override });
|
||||
// Gate required values
|
||||
if (!values || !keys) {
|
||||
res.status(400).send("Missing required data");
|
||||
exports.canvas = async function (req, res) {
|
||||
if (isProcessing) {
|
||||
if (requestQueue.length >= 100) {
|
||||
// Set a maximum queue size
|
||||
return res.status(503).send("Server is busy. Please try again later.");
|
||||
}
|
||||
requestQueue.push({ req, res });
|
||||
logger.log("inbound-canvas-creation-queue", "debug", "jsr", null, { queue: requestQueue.length });
|
||||
return;
|
||||
}
|
||||
|
||||
// Override must be an object if it exists
|
||||
if (override && !isObject(override)) {
|
||||
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();
|
||||
})()
|
||||
);
|
||||
isProcessing = true;
|
||||
await processCanvasRequest(req, res);
|
||||
processNextInQueue();
|
||||
};
|
||||
|
||||
@@ -3,24 +3,36 @@ require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
const logger = require("../utils/logger");
|
||||
const inlineCssTool = require("inline-css");
|
||||
//const inlineCssTool = require("inline-css");
|
||||
const juice = require("juice");
|
||||
|
||||
exports.inlinecss = (req, res) => {
|
||||
exports.inlinecss = async (req, res) => {
|
||||
//Perform request validation
|
||||
|
||||
logger.log("email-inline-css", "DEBUG", req.user.email, null, null);
|
||||
|
||||
const { html, url } = req.body;
|
||||
|
||||
inlineCssTool(html, { url: url })
|
||||
.then((inlinedHtml) => {
|
||||
res.send(inlinedHtml);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("email-inline-css-error", "ERROR", req.user.email, null, {
|
||||
error
|
||||
});
|
||||
|
||||
res.send(error);
|
||||
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 })
|
||||
// .then((inlinedHtml) => {
|
||||
// res.send(inlinedHtml);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// logger.log("email-inline-css-error", "ERROR", req.user.email, null, {
|
||||
// error
|
||||
// });
|
||||
|
||||
// });
|
||||
};
|
||||
|
||||
@@ -2,10 +2,11 @@ const express = require("express");
|
||||
const router = express.Router();
|
||||
const { inlinecss } = require("../render/inlinecss");
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const validateCanvasRequestMiddleware = require("../middleware/validateCanvasRequestMiddleware");
|
||||
const { canvas } = require("../render/canvas-handler");
|
||||
|
||||
// Define the route for inline CSS rendering
|
||||
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
|
||||
router.post("/canvas", validateFirebaseIdTokenMiddleware, canvas);
|
||||
router.post("/canvas", [validateFirebaseIdTokenMiddleware, validateCanvasRequestMiddleware], canvas);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user