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({
name: "my_color_picker",
layout: "bottom",
Widget: unlayer.createWidget({
render(value) {
return `
<input class="color-value" 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("color-value")[0];
input.onchange = function (event) {
updateValue(event.target.value);
};
// unlayer.registerPropertyEditor({
// name: "field_name",
// layout: "bottom",
// Widget: unlayer.createWidget({
// render(value) {
// return `
// <input class="field" value=${value} />
// `;
// },
// mount(node, value, updateValue) {
// var input = node.getElementsByClassName("field")[0];
// input.onchange = function (event) {
// updateValue(event.target.value);
// };
// },
// }),
// });
var redButton = node.getElementsByClassName("red")[0];
redButton.onclick = function () {
updateValue("#f00");
};
// unlayer.registerTool({
// type: "whatever",
// 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];
greenButton.onclick = function () {
updateValue("#0f0");
};
var blueButton = node.getElementsByClassName("blue")[0];
blueButton.onclick = function () {
updateValue("#00f");
};
},
}),
});
unlayer.registerTool({
type: "whatever",
category: "contents",
label: "My Tool",
icon: "fa-smile",
values: {},
options: {
default: {
title: null,
},
text: {
title: "Text",
position: 1,
options: {
color: {
label: "Color",
defaultValue: "#000",
widget: "my_color_picker",
},
},
},
},
renderer: {
Viewer: unlayer.createViewer({
render(values) {
return `
<div style="color: ${values.color};">I am a custom tool.</div>
`;
},
}),
exporters: {
web: function () {},
email: function () {},
},
},
});
// unlayer.registerTool({
// type: "whatever",
// category: "contents",
// label: "End 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) {
// return `
// <div style="display: none;">{{ /each }}</div>
// `;
// },
// }),
// exporters: {
// web: function () {},
// email: function () {},
// },
// },
// });
unlayer.registerColumns([2, 2, 2, 2, 2, 2]);
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 enLocale from "antd/es/locale/en_US";
import moment from "moment";
import axios from "axios";
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");
const httpLink = new HttpLink({

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
var admin = require("firebase-admin");
const path = require("path");
require("dotenv").config({
path: path.resolve(
@@ -42,3 +43,49 @@ exports.sendNotification = (req, res) => {
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;
}
};