Merged in release/2022-06-17 (pull request #518)

release/2022-06-17

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2022-06-20 17:48:16 +00:00
28 changed files with 5768 additions and 6731 deletions

View File

@@ -15374,6 +15374,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>sizelimit</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node> <folder_node>
@@ -44929,6 +44950,27 @@
<folder_node> <folder_node>
<name>signinerror</name> <name>signinerror</name>
<children> <children>
<concept_node>
<name>auth/user-disabled</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>auth/user-not-found</name> <name>auth/user-not-found</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -54,7 +54,7 @@ export const uploadToCloudinary = async (
//Set variables for getting the signed URL. //Set variables for getting the signed URL.
let timestamp = Math.floor(Date.now() / 1000); let timestamp = Math.floor(Date.now() / 1000);
let public_id = key; let public_id = key;
let tags = `${bodyshop.textid},${ let tags = `${bodyshop.imexshopid},${
tagsArray ? tagsArray.map((tag) => `${tag},`) : "" tagsArray ? tagsArray.map((tag) => `${tag},`) : ""
}`; }`;
// let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS; // let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;

View File

@@ -38,6 +38,12 @@ export function EmailDocumentsComponent({
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
console.log(
selectedMedia &&
selectedMedia
.filter((s) => s.isSelected)
.reduce((acc, val) => (acc = acc + val.size), 0)
);
return ( return (
<div> <div>
{loading && <LoadingSpinner />} {loading && <LoadingSpinner />}
@@ -45,6 +51,12 @@ export function EmailDocumentsComponent({
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? ( {selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
<div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div> <div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div>
) : null} ) : null}
{selectedMedia &&
selectedMedia
.filter((s) => s.isSelected)
.reduce((acc, val) => (acc = acc + val.size), 0) >= 9961472 ? (
<div style={{ color: "red" }}>{t("general.errors.sizelimit")}</div>
) : null}
{data && ( {data && (
<JobDocumentsGalleryExternal <JobDocumentsGalleryExternal
data={data ? data.documents : []} data={data ? data.documents : []}

View File

@@ -180,6 +180,23 @@ export function EmailOverlayComponent({
} }
return e && e.fileList; return e && e.fileList;
}} }}
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
const totalSize = value.reduce(
(acc, val) => (acc = acc + val.size),
0
);
const limit = 9961472;
if (totalSize > limit) {
return Promise.reject(t("general.errors.sizelimit"));
}
return Promise.resolve();
},
}),
]}
> >
<Upload.Dragger <Upload.Dragger
beforeUpload={Upload.LIST_IGNORE} beforeUpload={Upload.LIST_IGNORE}

View File

@@ -30,6 +30,7 @@ class ErrorBoundary extends React.Component {
static getDerivedStateFromError(error) { static getDerivedStateFromError(error) {
console.log("ErrorBoundary -> getDerivedStateFromError -> error", error); console.log("ErrorBoundary -> getDerivedStateFromError -> error", error);
return { hasErrored: true, error: error }; return { hasErrored: true, error: error };
} }

View File

@@ -25,6 +25,7 @@ function JobsDocumentGalleryExternal({
id: value.id, id: value.id,
type: value.type, type: value.type,
tags: [{ value: value.type, title: value.type }], tags: [{ value: value.type, title: value.type }],
size: value.size,
}); });
} }

View File

@@ -31,7 +31,7 @@ export default function OwnerFindModalContainer({
useEffect(() => { useEffect(() => {
if (modalProps.visible && owner) { if (modalProps.visible && owner) {
const s = OwnerNameDisplayFunction(owner); const s = OwnerNameDisplayFunction(owner, true);
setSearchText(s.trim()); setSearchText(s.trim());
callSearchowners({ variables: { search: s.trim() } }); callSearchowners({ variables: { search: s.trim() } });

View File

@@ -27,7 +27,7 @@ export function OwnerNameDisplay({ bodyshop, ownerObject }) {
}`.trim(); }`.trim();
} }
export function OwnerNameDisplayFunction(ownerObject) { export function OwnerNameDisplayFunction(ownerObject, forceFirstLast = false) {
const emptyTest = const emptyTest =
ownerObject.ownr_fn + ownerObject.ownr_ln + ownerObject.ownr_co_nm; ownerObject.ownr_fn + ownerObject.ownr_ln + ownerObject.ownr_co_nm;
@@ -36,7 +36,7 @@ export function OwnerNameDisplayFunction(ownerObject) {
const rdxStore = store.getState(); const rdxStore = store.getState();
if (rdxStore.user.bodyshop.last_name_first) if (rdxStore.user.bodyshop.last_name_first && !forceFirstLast)
return `${ownerObject.ownr_ln || ""}, ${ownerObject.ownr_fn || ""} ${ return `${ownerObject.ownr_ln || ""}, ${ownerObject.ownr_fn || ""} ${
ownerObject.ownr_co_nm || "" ownerObject.ownr_co_nm || ""
}`.trim(); }`.trim();

View File

@@ -1,12 +1,10 @@
import { Card } from "antd"; import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { import {
Bar, Bar,
CartesianGrid, CartesianGrid,
ComposedChart, ComposedChart,
LabelList,
Legend, Legend,
ReferenceLine, ReferenceLine,
ResponsiveContainer, ResponsiveContainer,
@@ -69,7 +67,7 @@ export function ScoreboardTicketsBar({ data, bodyshop }) {
// barSize={20} // barSize={20}
fill={data.colors[idx]} fill={data.colors[idx]}
> >
<LabelList position="top" /> {/* <LabelList position="top" /> */}
</Bar> </Bar>
))} ))}
</ComposedChart> </ComposedChart>

View File

@@ -216,7 +216,10 @@ export default function ScoreboardTimeTickets() {
ret2.push(r); ret2.push(r);
}); });
roundObject(ret);
roundObject(totals);
roundObject(ret2);
console.log(ret);
return { return {
fixed: ret, fixed: ret,
timeperiod: { timeperiod: {
@@ -299,3 +302,18 @@ function getColorArray(num) {
// } // }
// return result; // return result;
} }
function roundObject(inputObj) {
for (var key of Object.keys(inputObj)) {
if (typeof inputObj[key] === "number" && inputObj[key] !== 0) {
inputObj[key] =
inputObj[key] && inputObj[key].toFixed
? inputObj[key].toFixed(1)
: inputObj[key]; //Math.round(inputObj[key] * 100) / 100;
} else if (Array.isArray(inputObj[key])) {
inputObj[key].forEach((item) => roundObject(item));
} else if (typeof inputObj[key] === "object") {
roundObject(inputObj[key]);
}
}
}

View File

@@ -73,13 +73,13 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
<Col span={12}> <Col span={12}>
<Statistic <Statistic
title={t("scoreboard.labels.lastweek")} title={t("scoreboard.labels.lastweek")}
value={data.totalLastWeek.toFixed(1)} value={data.totalLastWeek}
/> />
</Col> </Col>
<Col span={12}> <Col span={12}>
<Statistic <Statistic
title={t("scoreboard.labels.lastmonth")} title={t("scoreboard.labels.lastmonth")}
value={data.totalLastMonth.toFixed(1)} value={data.totalLastMonth}
/> />
</Col> </Col>
</Row> </Row>
@@ -87,13 +87,13 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
<Col span={12}> <Col span={12}>
<Statistic <Statistic
title={t("scoreboard.labels.thisweek")} title={t("scoreboard.labels.thisweek")}
value={data.totalThisWeek.toFixed(1)} value={data.totalThisWeek}
/> />
</Col> </Col>
<Col span={12}> <Col span={12}>
<Statistic <Statistic
title={t("scoreboard.labels.thismonth")} title={t("scoreboard.labels.thismonth")}
value={data.totalThisMonth.toFixed(1)} value={data.totalThisMonth}
/> />
</Col> </Col>
</Row> </Row>
@@ -101,7 +101,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
<Col span={12}> <Col span={12}>
<Statistic <Statistic
title={t("scoreboard.labels.totaloverperiod")} title={t("scoreboard.labels.totaloverperiod")}
value={data.totalOverPeriod.toFixed(1)} value={data.totalOverPeriod}
/> />
</Col> </Col>
</Row> </Row>

View File

@@ -146,7 +146,8 @@ const JobRelatedTicketsTable = ({
title: t("employees.labels.name"), title: t("employees.labels.name"),
dataIndex: "empname", dataIndex: "empname",
key: "empname", key: "empname",
sorter: (a, b) => alphaSort(a.empname, b.empname), sorter: (a, b) =>
alphaSort(a.item.employee.last_name, b.item.employee.last_name),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "empname" && state.sortedInfo.order, state.sortedInfo.columnKey === "empname" && state.sortedInfo.order,
render: (text, record) => render: (text, record) =>
@@ -172,7 +173,9 @@ const JobRelatedTicketsTable = ({
title: t("timetickets.fields.efficiency"), title: t("timetickets.fields.efficiency"),
dataIndex: "total", dataIndex: "total",
key: "total", key: "total",
sorter: (a, b) => a.total - b.total, sorter: (a, b) =>
(a.actHrs === 0 || !a.actHrs ? 0 : (a.prodHrs / a.actHrs) * 100) -
(b.actHrs === 0 || !b.actHrs ? 0 : (b.prodHrs / b.actHrs) * 100),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "total" && state.sortedInfo.order, state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => render: (text, record) =>

View File

@@ -24,6 +24,8 @@ import {
selectBodyshop, selectBodyshop,
selectInstanceConflict, selectInstanceConflict,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import * as Sentry from "@sentry/react";
import "./manage.page.styles.scss"; import "./manage.page.styles.scss";
const ManageRootPage = lazy(() => const ManageRootPage = lazy(() =>
@@ -407,7 +409,10 @@ export function Manage({ match, conflict, bodyshop }) {
<Content className="content-container"> <Content className="content-container">
<PartnerPingComponent /> <PartnerPingComponent />
<ErrorBoundary>{PageContent}</ErrorBoundary> <Sentry.ErrorBoundary fallback={<ErrorBoundary />} showDialog>
{PageContent}
</Sentry.ErrorBoundary>
<BackTop /> <BackTop />
<Footer> <Footer>
<div <div

View File

@@ -961,7 +961,8 @@
}, },
"errors": { "errors": {
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.", "fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
"notfound": "No record was found." "notfound": "No record was found.",
"sizelimit": "The selected items exceed the size limit."
}, },
"itemtypes": { "itemtypes": {
"contract": "CC Contract", "contract": "CC Contract",
@@ -2668,6 +2669,7 @@
"users": { "users": {
"errors": { "errors": {
"signinerror": { "signinerror": {
"auth/user-disabled": "User account disabled. ",
"auth/user-not-found": "A user with this email does not exist.", "auth/user-not-found": "A user with this email does not exist.",
"auth/wrong-password": "The email and password combination you provided is incorrect." "auth/wrong-password": "The email and password combination you provided is incorrect."
} }

View File

@@ -961,7 +961,8 @@
}, },
"errors": { "errors": {
"fcm": "", "fcm": "",
"notfound": "" "notfound": "",
"sizelimit": ""
}, },
"itemtypes": { "itemtypes": {
"contract": "", "contract": "",
@@ -2668,6 +2669,7 @@
"users": { "users": {
"errors": { "errors": {
"signinerror": { "signinerror": {
"auth/user-disabled": "",
"auth/user-not-found": "", "auth/user-not-found": "",
"auth/wrong-password": "" "auth/wrong-password": ""
} }

View File

@@ -961,7 +961,8 @@
}, },
"errors": { "errors": {
"fcm": "", "fcm": "",
"notfound": "" "notfound": "",
"sizelimit": ""
}, },
"itemtypes": { "itemtypes": {
"contract": "", "contract": "",
@@ -2668,6 +2669,7 @@
"users": { "users": {
"errors": { "errors": {
"signinerror": { "signinerror": {
"auth/user-disabled": "",
"auth/user-not-found": "", "auth/user-not-found": "",
"auth/wrong-password": "" "auth/wrong-password": ""
} }

View File

@@ -103,7 +103,7 @@ export default async function RenderTemplate(
}), }),
}, },
}; };
console.log("PDFREQ", pdfRequest);
const pdfRender = await jsreport.renderAsync(pdfRequest); const pdfRender = await jsreport.renderAsync(pdfRequest);
pdf = pdfRender.toDataURI(); pdf = pdfRender.toDataURI();
} }

View File

@@ -1,3 +1,3 @@
Must set the environment variables using: Must set the environment variables using:
firebase functions:config:set auth.graphql_endpoint="https://bodyshop-dev-db.herokuapp.com/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!" firebase functions:config:set auth.graphql_endpoint="https://db.development.bodyshop.app/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!"

View File

@@ -1,5 +1,5 @@
version: 2 version: 2
endpoint: https://bodyshop-dev-db.herokuapp.com endpoint: https://db.development.bodyshop.app
admin_secret: Dev-BodyShopApp! admin_secret: Dev-BodyShopApp!
metadata_directory: metadata metadata_directory: metadata
actions: actions:

4940
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -157,7 +157,21 @@ app.post(
fb.unsubscribe fb.unsubscribe
); );
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser); app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
app.post("/adm/getuser", fb.validateFirebaseIdToken, fb.getUser);
app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser); app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser);
const adm = require("./server/admin/adminops");
app.post(
"/adm/createassociation",
fb.validateFirebaseIdToken,
fb.validateAdmin,
adm.createAssociation
);
app.post(
"/adm/createshop",
fb.validateFirebaseIdToken,
fb.validateAdmin,
adm.createShop
);
//Stripe Processing //Stripe Processing
var stripe = require("./server/stripe/payment"); var stripe = require("./server/stripe/payment");
@@ -216,7 +230,7 @@ server.listen(port, (error) => {
if (error) throw error; if (error) throw error;
logger.log( logger.log(
`[${process.env.NODE_ENV || "DEVELOPMENT"}] Server running on port ${port}`, `[${process.env.NODE_ENV || "DEVELOPMENT"}] Server running on port ${port}`,
"DEBUG", "INFO",
"api" "api"
); );
}); });

68
server/admin/adminops.js Normal file
View File

@@ -0,0 +1,68 @@
const path = require("path");
const _ = require("lodash");
const logger = require("../utils/logger");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
const client = require("../graphql-client/graphql-client").client;
exports.createAssociation = async (req, res) => {
logger.log("admin-create-association", "ADMIN", req.user.email, null, {
request: req.body,
ioadmin: true,
});
const { shopid, authlevel, useremail } = req.body;
const result = await client.request(
`mutation INSERT_ASSOCIATION($assoc: associations_insert_input!){
insert_associations_one(object:$assoc){
id
authlevel
useremail
active
}
}`,
{
assoc: { shopid, authlevel, useremail, active: false },
}
);
res.json(result);
};
exports.createShop = async (req, res) => {
logger.log("admin-create-shop", "ADMIN", req.user.email, null, {
request: req.body,
ioadmin: true,
});
const { bodyshop, ronum } = req.body;
try {
const result = await client.request(
`mutation INSERT_BODYSHOPS($bs: bodyshops_insert_input!){
insert_bodyshops_one(object:$bs){
id
}
}`,
{
bs: {
...bodyshop,
counters: {
data: [
{ countertype: "ronum", count: ronum },
{ countertype: "ihbnum", count: 1 },
{ countertype: "paymentnum", count: 1 },
],
},
},
}
);
res.json(result);
} catch (error) {
res.status(500).json(error);
}
};

View File

@@ -665,6 +665,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
const CreateCosts = (job) => { const CreateCosts = (job) => {
//Create a mapping based on AH Requirements //Create a mapping based on AH Requirements
//For DMS, the keys in the object below are the CIECA part types.
const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => { const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => {
//At the bill level. //At the bill level.
bill_val.billlines.map((line_val) => { bill_val.billlines.map((line_val) => {
@@ -731,7 +732,7 @@ const CreateCosts = (job) => {
}).multiply(job.job_totals.rates.mash.hours) }).multiply(job.job_totals.rates.mash.hours)
); );
} }
//Uses CIECA Labor types.
const ticketTotalsByCostCenter = job.timetickets.reduce( const ticketTotalsByCostCenter = job.timetickets.reduce(
(ticket_acc, ticket_val) => { (ticket_acc, ticket_val) => {
//At the invoice level. //At the invoice level.
@@ -750,7 +751,43 @@ const CreateCosts = (job) => {
}, },
{} {}
); );
const defaultCosts = job.bodyshop.md_responsibility_centers.defaults.costs; //CIECA STANDARD MAPPING OBJECT.
const ciecaObj = {
ATS: "ATS",
LA1: "LA1",
LA2: "LA2",
LA3: "LA3",
LA4: "LA4",
LAA: "LAA",
LAB: "LAB",
LAD: "LAD",
LAE: "LAE",
LAF: "LAF",
LAG: "LAG",
LAM: "LAM",
LAR: "LAR",
LAS: "LAS",
LAU: "LAU",
PAA: "PAA",
PAC: "PAC",
PAG: "PAG",
PAL: "PAL",
PAM: "PAM",
PAN: "PAN",
PAO: "PAO",
PAP: "PAP",
PAR: "PAR",
PAS: "PAS",
TOW: "TOW",
MAPA: "MAPA",
MASH: "MASH",
PASL: "PASL",
};
const defaultCosts =
job.bodyshop.cdk_dealerid || job.bodyshop.pbs_serialnumber
? ciecaObj
: job.bodyshop.md_responsibility_centers.defaults.costs;
return { return {
PartsTotalCost: Object.keys(billTotalsByCostCenters).reduce((acc, key) => { PartsTotalCost: Object.keys(billTotalsByCostCenters).reduce((acc, key) => {

View File

@@ -10,7 +10,8 @@ let nodemailer = require("nodemailer");
let aws = require("aws-sdk"); let aws = require("aws-sdk");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
const ses = new aws.SES({ const ses = new aws.SES({
apiVersion: "2010-12-01", apiVersion: "latest",
region: "ca-central-1", region: "ca-central-1",
}); });
@@ -43,6 +44,7 @@ exports.sendServerEmail = async function ({ subject, text }) {
} catch (error) { } catch (error) {
console.log(error); console.log(error);
logger.log("server-email-failure", "error", null, null, error); logger.log("server-email-failure", "error", null, null, error);
res.status(500).json(error);
} }
}; };
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) { exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
@@ -153,7 +155,7 @@ exports.sendEmail = async (req, res) => {
error: err, error: err,
}); });
res.json({ success: false, error: err }); res.status(500).json({ success: false, error: err });
} }
} }
); );

View File

@@ -1,13 +1,14 @@
var admin = require("firebase-admin"); var admin = require("firebase-admin");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
const path = require("path"); const path = require("path");
const { auth } = require("firebase-admin");
require("dotenv").config({ require("dotenv").config({
path: path.resolve( path: path.resolve(
process.cwd(), process.cwd(),
`.env.${process.env.NODE_ENV || "development"}` `.env.${process.env.NODE_ENV || "development"}`
), ),
}); });
const client = require("../graphql-client/graphql-client").client;
var serviceAccount = require(process.env.FIREBASE_ADMINSDK_JSON); var serviceAccount = require(process.env.FIREBASE_ADMINSDK_JSON);
admin.initializeApp({ admin.initializeApp({
@@ -19,54 +20,61 @@ exports.admin = admin;
const adminEmail = [ const adminEmail = [
"patrick@imex.dev", "patrick@imex.dev",
"patrick@imex.text", //"patrick@imex.test",
"patrick@imex.prod", "patrick@imex.prod",
"patrick@imexsystems.ca", "patrick@imexsystems.ca",
"patrick@thinkimex.com", "patrick@thinkimex.com",
]; ];
exports.createUser = (req, res) => { exports.createUser = async (req, res) => {
logger.log("admin-create-user", "WARN", req.user.email, null, { logger.log("admin-create-user", "ADMIN", req.user.email, null, {
request: req.body, request: req.body,
ioadmin: true,
}); });
if (!adminEmail.includes(req.user.email)) {
logger.log( const { email, displayName, password, shopid, authlevel } = req.body;
"admin-create-user-unauthorized", try {
"ERROR", const userRecord = await admin
req.user.email, .auth()
null, .createUser({ email, displayName, password });
// See the UserRecord reference doc for the contents of userRecord.
const result = await client.request(
`
mutation INSERT_USER($user: users_insert_input!) {
insert_users_one(object: $user) {
email
}
}
`,
{ {
request: req.body, user: {
user: req.user, email,
authid: userRecord.uid,
associations: {
data: [{ shopid, authlevel, active: true }],
},
},
} }
); );
res.sendStatus(404);
}
const { email, displayName, password } = req.body;
admin
.auth()
.createUser({ email, displayName, password })
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
logger.log("admin-update-user-success", "DEBUG", req.user.email, null, { res.json({ userRecord, result });
userRecord, } catch (error) {
}); logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
res.json(userRecord); error,
})
.catch((error) => {
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
error,
});
res.status(500).json(error);
}); });
res.status(500).json(error);
}
}; };
exports.updateUser = (req, res) => { exports.updateUser = (req, res) => {
logger.log("admin-update-user", "WARN", req.user.email, null, { logger.log("admin-update-user", "ADMIN", req.user.email, null, {
request: req.body, request: req.body,
ioadmin: true,
}); });
if (!adminEmail.includes(req.user.email)) {
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
logger.log( logger.log(
"admin-update-user-unauthorized", "admin-update-user-unauthorized",
"ERROR", "ERROR",
@@ -78,6 +86,7 @@ exports.updateUser = (req, res) => {
} }
); );
res.sendStatus(404); res.sendStatus(404);
return;
} }
admin admin
@@ -98,8 +107,9 @@ exports.updateUser = (req, res) => {
.then((userRecord) => { .then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord. // See the UserRecord reference doc for the contents of userRecord.
logger.log("admin-update-user-success", "DEBUG", req.user.email, null, { logger.log("admin-update-user-success", "ADMIN", req.user.email, null, {
userRecord, userRecord,
ioadmin: true,
}); });
res.json(userRecord); res.json(userRecord);
}) })
@@ -111,6 +121,41 @@ exports.updateUser = (req, res) => {
}); });
}; };
exports.getUser = (req, res) => {
logger.log("admin-get-user", "ADMIN", req.user.email, null, {
request: req.body,
ioadmin: true,
});
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
logger.log(
"admin-update-user-unauthorized",
"ERROR",
req.user.email,
null,
{
request: req.body,
user: req.user,
}
);
res.sendStatus(404);
return;
}
admin
.auth()
.getUser(req.body.uid)
.then((userRecord) => {
res.json(userRecord);
})
.catch((error) => {
logger.log("admin-get-user-error", "ERROR", req.user.email, null, {
error,
});
res.status(500).json(error);
});
};
exports.sendNotification = async (req, res) => { exports.sendNotification = async (req, res) => {
setTimeout(() => { setTimeout(() => {
// Send a message to the device corresponding to the provided // Send a message to the device corresponding to the provided
@@ -221,3 +266,35 @@ exports.validateFirebaseIdToken = async (req, res, next) => {
return; return;
} }
}; };
exports.validateAdmin = async (req, res, next) => {
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
logger.log("admin-validation-failed", "ERROR", req.user.email, null, {
request: req.body,
user: req.user,
});
res.sendStatus(404);
return;
} else {
next();
return;
}
};
//Admin claims code.
// const uid = "JEqqYlsadwPEXIiyRBR55fflfko1";
// admin
// .auth()
// .getUser(uid)
// .then((user) => {
// console.log(user);
// admin.auth().setCustomUserClaims(uid, {
// ioadmin: true,
// "https://hasura.io/jwt/claims": {
// "x-hasura-default-role": "admin",
// "x-hasura-allowed-roles": ["admin"],
// "x-hasura-user-id": uid,
// },
// });
// });

View File

@@ -611,6 +611,8 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
autohouseid autohouseid
md_responsibility_centers md_responsibility_centers
jc_hourly_rates jc_hourly_rates
cdk_dealerid
pbs_serialnumber
timezone timezone
} }
jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) { jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) {

View File

@@ -5,7 +5,7 @@ const logger = new graylog2.graylog({
}); });
function log(message, type, user, record, object) { function log(message, type, user, record, object) {
if (type !== "ioevent") if (type !== "ioevent" && type !== "DEBUG")
console.log(message, { console.log(message, {
type, type,
env: process.env.NODE_ENV || "development", env: process.env.NODE_ENV || "development",
@@ -13,7 +13,7 @@ function log(message, type, user, record, object) {
record, record,
...object, ...object,
}); });
logger.log(message, { logger.log(message, message, {
type, type,
env: process.env.NODE_ENV || "development", env: process.env.NODE_ENV || "development",
user, user,

7132
yarn.lock

File diff suppressed because it is too large Load Diff