Added required authorization for all API calls. Added template stylesheet.

This commit is contained in:
Patrick Fic
2020-08-19 15:03:55 -07:00
parent ef2e015347
commit 88858e6789
7 changed files with 252 additions and 110 deletions

View File

@@ -1,112 +1,95 @@
unlayer.registerPropertyEditor({ // unlayer.registerPropertyEditor({
name: "my_color_picker", // name: "field_name",
layout: "bottom", // layout: "bottom",
Widget: unlayer.createWidget({ // Widget: unlayer.createWidget({
render(value) { // render(value) {
return ` // return `
<input class="color-value" value=${value} /> // <input class="field" value=${value} />
<button class="red">Red</button> // `;
<button class="green">Green</button> // },
<button class="blue">Blue</button> // mount(node, value, updateValue) {
`; // var input = node.getElementsByClassName("field")[0];
}, // input.onchange = function (event) {
mount(node, value, updateValue) { // updateValue(event.target.value);
var input = node.getElementsByClassName("color-value")[0]; // };
input.onchange = function (event) { // },
updateValue(event.target.value); // }),
}; // });
var redButton = node.getElementsByClassName("red")[0]; // unlayer.registerTool({
redButton.onclick = function () { // type: "whatever",
updateValue("#f00"); // category: "contents",
}; // label: "Begin Repeat",
// icon: "fa-smile",
// values: {},
// options: {
// default: {
// title: null,
// },
// text: {
// title: "Field",
// position: 1,
// options: {
// field: {
// label: "Field",
// defaultValue: "",
// widget: "field_name",
// },
// },
// },
// },
// renderer: {
// Viewer: unlayer.createViewer({
// render(values) {
// console.log(values);
// return `
// <div style="display: none;">{{#each ${values.field}}}</div>
// `;
// },
// }),
// exporters: {
// web: function () {},
// email: function () {},
// },
// },
// });
var greenButton = node.getElementsByClassName("green")[0]; // unlayer.registerTool({
greenButton.onclick = function () { // type: "whatever",
updateValue("#0f0"); // category: "contents",
}; // label: "End Repeat",
// icon: "fa-smile",
var blueButton = node.getElementsByClassName("blue")[0]; // values: {},
blueButton.onclick = function () { // options: {
updateValue("#00f"); // default: {
}; // title: null,
}, // },
}), // text: {
}); // title: "Field",
// position: 1,
unlayer.registerTool({ // options: {
type: "whatever", // field: {
category: "contents", // label: "Field",
label: "My Tool", // defaultValue: "",
icon: "fa-smile", // widget: "field_name",
values: {}, // },
options: { // },
default: { // },
title: null, // },
}, // renderer: {
text: { // Viewer: unlayer.createViewer({
title: "Text", // render(values) {
position: 1, // return `
options: { // <div style="display: none;">{{ /each }}</div>
color: { // `;
label: "Color", // },
defaultValue: "#000", // }),
widget: "my_color_picker", // exporters: {
}, // web: function () {},
}, // email: function () {},
}, // },
}, // },
renderer: { // });
Viewer: unlayer.createViewer({
render(values) {
return `
<div style="color: ${values.color};">I am a custom tool.</div>
`;
},
}),
exporters: {
web: function () {},
email: function () {},
},
},
});
unlayer.registerColumns([2, 2, 2, 2, 2, 2]); unlayer.registerColumns([2, 2, 2, 2, 2, 2]);
unlayer.registerColumns([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); unlayer.registerColumns([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
unlayer.registerTool({
type: "whatever",
category: "contents",
label: "Begin Repeater",
icon: "fa-smile",
values: {},
options: {
default: {
title: "BeginRepeater",
},
text: {
title: "Text",
position: 1,
options: {
field: {
label: "Field",
defaultValue: "Field Name...",
widget: "my_color_picker",
},
},
},
},
renderer: {
Viewer: unlayer.createViewer({
render(values) {
return `
<div style="color: ${values.color};">{{${values.field}}}</div>
`;
},
}),
exporters: {
web: function () {},
email: function () {},
},
},
});

View File

@@ -0,0 +1,85 @@
/* body {
font-family: "Open Sans", sans-serif;
line-height: 1.25;
} */
table {
border: 1px solid #ccc;
border-collapse: collapse;
margin: 0;
padding: 0;
width: 100%;
table-layout: fixed;
}
table caption {
font-size: 1.5em;
margin: 0.5em 0 0.75em;
}
table tr {
background-color: #f8f8f8;
border: 1px solid #ddd;
padding: 0.35em;
}
table th,
table td {
padding: 0.625em;
text-align: center;
}
table th {
font-size: 0.85em;
letter-spacing: 0.1em;
text-transform: uppercase;
}
@media screen and (max-width: 600px) {
table {
border: 0;
}
table caption {
font-size: 1.3em;
}
table thead {
border: none;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
table tr {
border-bottom: 3px solid #ddd;
display: block;
margin-bottom: 0.625em;
}
table td {
border-bottom: 1px solid #ddd;
display: block;
font-size: 0.8em;
text-align: right;
}
table td::before {
/*
* aria-label has no advantage, it won't be read inside a table
content: attr(aria-label);
*/
content: attr(data-label);
float: left;
font-weight: bold;
text-transform: uppercase;
}
table td:last-child {
border-bottom: 0;
}
}

View File

@@ -18,9 +18,25 @@ import App from "./App";
import { ConfigProvider } from "antd"; import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US"; import enLocale from "antd/es/locale/en_US";
import moment from "moment"; import moment from "moment";
import axios from "axios";
moment.locale("en-US"); moment.locale("en-US");
axios.interceptors.request.use(
async (config) => {
if (!config.headers.Authorization) {
const token =
auth.currentUser && (await auth.currentUser.getIdToken(true));
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
},
(error) => Promise.reject(error)
);
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp"); if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
const httpLink = new HttpLink({ const httpLink = new HttpLink({

View File

@@ -18,9 +18,11 @@ export default function ShopTemplateSaveButton({
logImEXEvent("shop_template_update"); logImEXEvent("shop_template_update");
emailEditorRef.current.exportHtml(async (data) => { emailEditorRef.current.exportHtml(async (data) => {
console.log("RAW", data.html);
inlineCss(data.html, { inlineCss(data.html, {
url: `${window.location.protocol}://${window.location.host}`, url: `${window.location.protocol}://${window.location.host}/`,
}).then(async function (inlineHtml) { }).then(async function (inlineHtml) {
console.log("Inline :>> ", inlineHtml);
const result = await updateTemplate({ const result = await updateTemplate({
variables: { variables: {
templateId: templateId, templateId: templateId,

View File

@@ -14,8 +14,9 @@ export default function ShopTemplateEditorComponent({
const emailEditorRef = useRef(null); const emailEditorRef = useRef(null);
useEffect(() => { useEffect(() => {
if (json && Object.keys(json).length > 0) if (json && Object.keys(json).length > 0 && emailEditorRef.current) {
emailEditorRef.current.loadDesign(json); emailEditorRef.current.loadDesign(json);
}
}, [json, emailEditorRef]); }, [json, emailEditorRef]);
useEffect(() => { useEffect(() => {
@@ -46,6 +47,12 @@ export default function ShopTemplateEditorComponent({
<EmailEditor <EmailEditor
ref={emailEditorRef} ref={emailEditorRef}
options={{ options={{
// customCSS: [
// window.location.protocol +
// "//" +
// window.location.host +
// "/render-styles.css",
// ],
customJS: [ customJS: [
window.location.protocol + window.location.protocol +
"//" + "//" +

View File

@@ -5,6 +5,8 @@ const path = require("path");
const compression = require("compression"); const compression = require("compression");
const twilio = require("twilio"); const twilio = require("twilio");
global.fetch = require("node-fetch"); global.fetch = require("node-fetch");
var fb = require("./server/firebase/firebase-handler");
//var enforce = require("express-sslify"); //var enforce = require("express-sslify");
require("dotenv").config({ require("dotenv").config({
@@ -21,6 +23,7 @@ const app = express();
const port = process.env.PORT || 5000; const port = process.env.PORT || 5000;
//const port = 5000; //const port = 5000;
app.use(fb.validateFirebaseIdToken);
app.use(compression()); app.use(compression());
app.use(bodyParser.json({ limit: "50mb" })); app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true })); app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
@@ -77,7 +80,6 @@ app.post("/scheduling/job", scheduling.job);
var renderHandlebars = require("./server/render/renderHandlebars"); var renderHandlebars = require("./server/render/renderHandlebars");
app.post("/render", renderHandlebars.render); app.post("/render", renderHandlebars.render);
var fb = require("./server/firebase/firebase-handler");
app.post("/notifications/send", fb.sendNotification); app.post("/notifications/send", fb.sendNotification);
//Stripe Processing //Stripe Processing

View File

@@ -1,4 +1,5 @@
var admin = require("firebase-admin"); var admin = require("firebase-admin");
const path = require("path"); const path = require("path");
require("dotenv").config({ require("dotenv").config({
path: path.resolve( path: path.resolve(
@@ -42,3 +43,49 @@ exports.sendNotification = (req, res) => {
res.sendStatus(200); res.sendStatus(200);
}; };
exports.validateFirebaseIdToken = async (req, res, next) => {
console.log("Check if request is authorized with Firebase ID token");
if (
(!req.headers.authorization ||
!req.headers.authorization.startsWith("Bearer ")) &&
!(req.cookies && req.cookies.__session)
) {
console.error("Unauthorized attempt. No authorization provided.");
res.status(403).send("Unauthorized");
return;
}
let idToken;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer ")
) {
// console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split("Bearer ")[1];
} else if (req.cookies) {
//console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
console.error("Unauthorized attempt. No cookie provided.");
res.status(403).send("Unauthorized");
return;
}
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
//console.log("ID Token correctly decoded", decodedIdToken);
req.user = decodedIdToken;
next();
return;
} catch (error) {
console.error("Error while verifying Firebase ID token:", error);
res.status(403).send("Unauthorized");
return;
}
};