added client role and related code
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
import { getFilterObject, getSortObject, PageQueryParams } from '../pagination';
|
import { getFilterObject, getSortObject, PageQueryParams } from "../pagination";
|
||||||
import { ChangeEvent, dbEvents } from '../realtime';
|
import { ChangeEvent, dbEvents } from "../realtime";
|
||||||
import { generateId } from '../utils/id';
|
import { generateId } from "../utils/id";
|
||||||
import {
|
import {
|
||||||
CreateOrgInput,
|
CreateOrgInput,
|
||||||
orgFields,
|
orgFields,
|
||||||
orgModel,
|
orgModel,
|
||||||
UpdateOrgInput,
|
UpdateOrgInput,
|
||||||
} from './organization.schema';
|
} from "./organization.schema";
|
||||||
|
|
||||||
export async function createOrg(input: CreateOrgInput, tenantId: string) {
|
export async function createOrg(input: CreateOrgInput, tenantId: string) {
|
||||||
const org = await orgModel.create({
|
const org = await orgModel.create({
|
||||||
@@ -17,13 +17,13 @@ export async function createOrg(input: CreateOrgInput, tenantId: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dbEvents.emit(
|
dbEvents.emit(
|
||||||
'change',
|
"change",
|
||||||
{
|
{
|
||||||
type: 'insert',
|
type: "insert",
|
||||||
collection: 'orgs',
|
collection: "orgs",
|
||||||
document: org,
|
document: org,
|
||||||
} as ChangeEvent,
|
} as ChangeEvent,
|
||||||
['org:read']
|
["org:read"]
|
||||||
);
|
);
|
||||||
|
|
||||||
return org;
|
return org;
|
||||||
@@ -45,7 +45,7 @@ export async function listOrgs(params: PageQueryParams, tenantId: string) {
|
|||||||
{ $match: { $and: [{ tenantId: tenantId }, { ...filterObj }] } },
|
{ $match: { $and: [{ tenantId: tenantId }, { ...filterObj }] } },
|
||||||
{
|
{
|
||||||
$facet: {
|
$facet: {
|
||||||
metadata: [{ $count: 'count' }],
|
metadata: [{ $count: "count" }],
|
||||||
data: [
|
data: [
|
||||||
{ $sort: sortObj },
|
{ $sort: sortObj },
|
||||||
{ $skip: (page - 1) * pageSize },
|
{ $skip: (page - 1) * pageSize },
|
||||||
@@ -83,13 +83,13 @@ export async function updateOrg(
|
|||||||
|
|
||||||
if (updateOrgResult) {
|
if (updateOrgResult) {
|
||||||
dbEvents.emit(
|
dbEvents.emit(
|
||||||
'change',
|
"change",
|
||||||
{
|
{
|
||||||
type: 'update',
|
type: "update",
|
||||||
collection: 'orgs',
|
collection: "orgs",
|
||||||
document: updateOrgResult,
|
document: updateOrgResult,
|
||||||
} as ChangeEvent,
|
} as ChangeEvent,
|
||||||
['org:read']
|
["org:read"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,15 +103,15 @@ export async function deleteOrg(orgId: string, tenantId: string) {
|
|||||||
|
|
||||||
if (res.deletedCount > 0) {
|
if (res.deletedCount > 0) {
|
||||||
dbEvents.emit(
|
dbEvents.emit(
|
||||||
'change',
|
"change",
|
||||||
{
|
{
|
||||||
type: 'delete',
|
type: "delete",
|
||||||
collection: 'orgs',
|
collection: "orgs",
|
||||||
document: {
|
document: {
|
||||||
pid: orgId,
|
pid: orgId,
|
||||||
},
|
},
|
||||||
} as ChangeEvent,
|
} as ChangeEvent,
|
||||||
['org:read']
|
["org:read"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ export async function searchOrgs(params: PageQueryParams, tenantId: string) {
|
|||||||
if (!params.searchToken)
|
if (!params.searchToken)
|
||||||
return { orgs: [], metadata: { count: 0, page, pageSize } };
|
return { orgs: [], metadata: { count: 0, page, pageSize } };
|
||||||
|
|
||||||
const regex = new RegExp(params.searchToken, 'i');
|
const regex = new RegExp(params.searchToken, "i");
|
||||||
|
|
||||||
const orgs = await orgModel.aggregate([
|
const orgs = await orgModel.aggregate([
|
||||||
{ $match: { $and: [{ tenantId: tenantId }, { ...filterObj }] } },
|
{ $match: { $and: [{ tenantId: tenantId }, { ...filterObj }] } },
|
||||||
@@ -138,7 +138,7 @@ export async function searchOrgs(params: PageQueryParams, tenantId: string) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
$facet: {
|
$facet: {
|
||||||
metadata: [{ $count: 'count' }],
|
metadata: [{ $count: "count" }],
|
||||||
data: [
|
data: [
|
||||||
{ $sort: sortObj },
|
{ $sort: sortObj },
|
||||||
{ $skip: (page - 1) * pageSize },
|
{ $skip: (page - 1) * pageSize },
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export async function listPermitsHandler(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const authUser = req.user;
|
const authUser = req.user;
|
||||||
const orgList = await listPermits(queryParams, authUser.tenantId);
|
const orgList = await listPermits(queryParams, authUser);
|
||||||
return res.code(200).send(orgList);
|
return res.code(200).send(orgList);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return err;
|
return err;
|
||||||
@@ -97,7 +97,7 @@ export async function searchPermitHandler(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const authUser = req.user;
|
const authUser = req.user;
|
||||||
const permitList = await searchPermit(queryParams, authUser.tenantId);
|
const permitList = await searchPermit(queryParams, authUser);
|
||||||
return res.code(200).send(permitList);
|
return res.code(200).send(permitList);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { orgModel } from '../organization/organization.schema';
|
import { orgModel } from "../organization/organization.schema";
|
||||||
import { getFilterObject, getSortObject, PageQueryParams } from '../pagination';
|
import { getFilterObject, getSortObject, PageQueryParams } from "../pagination";
|
||||||
import { userModel } from '../user/user.schema';
|
import { userModel } from "../user/user.schema";
|
||||||
import { generateId } from '../utils/id';
|
import { generateId } from "../utils/id";
|
||||||
import {
|
import {
|
||||||
CreatePermitInput,
|
CreatePermitInput,
|
||||||
permitFields,
|
permitFields,
|
||||||
permitModel,
|
permitModel,
|
||||||
UpdatePermitInput,
|
UpdatePermitInput,
|
||||||
} from './permit.schema';
|
} from "./permit.schema";
|
||||||
import { ChangeEvent, dbEvents } from '../realtime';
|
import { ChangeEvent, dbEvents } from "../realtime";
|
||||||
import { permitPipeline } from '../utils/pipeline';
|
import { permitPipeline } from "../utils/pipeline";
|
||||||
import { AuthenticatedUser } from '../auth';
|
import { AuthenticatedUser } from "../auth";
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
export async function createPermit(
|
export async function createPermit(
|
||||||
input: CreatePermitInput,
|
input: CreatePermitInput,
|
||||||
@@ -32,13 +33,13 @@ export async function createPermit(
|
|||||||
});
|
});
|
||||||
|
|
||||||
dbEvents.emit(
|
dbEvents.emit(
|
||||||
'change',
|
"change",
|
||||||
{
|
{
|
||||||
type: 'insert',
|
type: "insert",
|
||||||
collection: 'permits',
|
collection: "permits",
|
||||||
document: permit,
|
document: permit,
|
||||||
} as ChangeEvent,
|
} as ChangeEvent,
|
||||||
['permit:read']
|
["permit:read"]
|
||||||
);
|
);
|
||||||
|
|
||||||
return permit;
|
return permit;
|
||||||
@@ -51,26 +52,33 @@ export async function getPermit(permitId: string, tenantId: string) {
|
|||||||
})
|
})
|
||||||
//.populate({ path: "county", select: "pid name avatar" })
|
//.populate({ path: "county", select: "pid name avatar" })
|
||||||
//.populate({ path: "client", select: "pid name avatar" })
|
//.populate({ path: "client", select: "pid name avatar" })
|
||||||
.populate({ path: 'assignedTo', select: 'pid name avatar' })
|
.populate({ path: "assignedTo", select: "pid name avatar" })
|
||||||
.populate({ path: 'createdBy', select: 'pid name avatar' });
|
.populate({ path: "createdBy", select: "pid name avatar" });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listPermits(params: PageQueryParams, tenantId: string) {
|
export async function listPermits(
|
||||||
|
params: PageQueryParams,
|
||||||
|
user: AuthenticatedUser
|
||||||
|
) {
|
||||||
const page = params.page || 1;
|
const page = params.page || 1;
|
||||||
const pageSize = params.pageSize || 10;
|
const pageSize = params.pageSize || 10;
|
||||||
const sortObj = getSortObject(params, permitFields);
|
const sortObj = getSortObject(params, permitFields);
|
||||||
const filterObj = getFilterObject(params);
|
const filterObj = getFilterObject(params) || {};
|
||||||
|
|
||||||
|
if (user.role == "client") {
|
||||||
|
filterObj["client"] = new mongoose.Types.ObjectId(user.orgId);
|
||||||
|
}
|
||||||
|
|
||||||
const permitsList = await permitModel.aggregate([
|
const permitsList = await permitModel.aggregate([
|
||||||
{
|
{
|
||||||
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
|
$match: { $and: [{ tenantId: user.tenantId }, { ...filterObj }] },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$lookup: {
|
$lookup: {
|
||||||
from: 'users',
|
from: "users",
|
||||||
localField: 'assignedTo',
|
localField: "assignedTo",
|
||||||
foreignField: '_id',
|
foreignField: "_id",
|
||||||
as: 'assignedRec',
|
as: "assignedRec",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,12 +120,12 @@ export async function listPermits(params: PageQueryParams, tenantId: string) {
|
|||||||
statusUpdated: 1,
|
statusUpdated: 1,
|
||||||
assignedTo: {
|
assignedTo: {
|
||||||
$let: {
|
$let: {
|
||||||
vars: { assigned: { $arrayElemAt: ['$assignedRec', 0] } },
|
vars: { assigned: { $arrayElemAt: ["$assignedRec", 0] } },
|
||||||
in: {
|
in: {
|
||||||
_id: '$$assigned._id',
|
_id: "$$assigned._id",
|
||||||
pid: '$$assigned.pid',
|
pid: "$$assigned.pid",
|
||||||
name: '$$assigned.name',
|
name: "$$assigned.name",
|
||||||
avatar: '$$assigned.avatar',
|
avatar: "$$assigned.avatar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -125,7 +133,7 @@ export async function listPermits(params: PageQueryParams, tenantId: string) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
$facet: {
|
$facet: {
|
||||||
metadata: [{ $count: 'count' }],
|
metadata: [{ $count: "count" }],
|
||||||
data: [
|
data: [
|
||||||
{ $sort: sortObj },
|
{ $sort: sortObj },
|
||||||
{ $skip: (page - 1) * pageSize },
|
{ $skip: (page - 1) * pageSize },
|
||||||
@@ -161,20 +169,20 @@ export async function updatePermit(
|
|||||||
{ ...input, lastUpdateDate: new Date() },
|
{ ...input, lastUpdateDate: new Date() },
|
||||||
{ new: true }
|
{ new: true }
|
||||||
)
|
)
|
||||||
.populate({ path: 'county', select: 'pid name avatar' })
|
.populate({ path: "county", select: "pid name avatar" })
|
||||||
.populate({ path: 'client', select: 'pid name avatar' })
|
.populate({ path: "client", select: "pid name avatar" })
|
||||||
.populate({ path: 'assignedTo', select: 'pid name avatar' })
|
.populate({ path: "assignedTo", select: "pid name avatar" })
|
||||||
.populate({ path: 'createdBy', select: 'pid name avatar' });
|
.populate({ path: "createdBy", select: "pid name avatar" });
|
||||||
|
|
||||||
if (updatePermitResult) {
|
if (updatePermitResult) {
|
||||||
dbEvents.emit(
|
dbEvents.emit(
|
||||||
'change',
|
"change",
|
||||||
{
|
{
|
||||||
type: 'update',
|
type: "update",
|
||||||
collection: 'permits',
|
collection: "permits",
|
||||||
document: updatePermitResult,
|
document: updatePermitResult,
|
||||||
} as ChangeEvent,
|
} as ChangeEvent,
|
||||||
['permit:read']
|
["permit:read"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,50 +195,57 @@ export async function deletePermit(permitId: string, tenantId: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dbEvents.emit(
|
dbEvents.emit(
|
||||||
'change',
|
"change",
|
||||||
{
|
{
|
||||||
type: 'delete',
|
type: "delete",
|
||||||
collection: 'permits',
|
collection: "permits",
|
||||||
document: {
|
document: {
|
||||||
pid: permitId,
|
pid: permitId,
|
||||||
},
|
},
|
||||||
} as ChangeEvent,
|
} as ChangeEvent,
|
||||||
['permit:read']
|
["permit:read"]
|
||||||
);
|
);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchPermit(params: PageQueryParams, tenantId: string) {
|
export async function searchPermit(
|
||||||
|
params: PageQueryParams,
|
||||||
|
user: AuthenticatedUser
|
||||||
|
) {
|
||||||
const page = params.page || 1;
|
const page = params.page || 1;
|
||||||
const pageSize = params.pageSize || 10;
|
const pageSize = params.pageSize || 10;
|
||||||
const sortObj = getSortObject(params, permitFields);
|
const sortObj = getSortObject(params, permitFields);
|
||||||
const filterObj = getFilterObject(params);
|
const filterObj = getFilterObject(params) || {};
|
||||||
|
|
||||||
|
if (user.role == "client") {
|
||||||
|
filterObj["client"] = new mongoose.Types.ObjectId(user.orgId);
|
||||||
|
}
|
||||||
|
|
||||||
if (!params.searchToken)
|
if (!params.searchToken)
|
||||||
return { permits: [], metadata: { count: 0, page, pageSize } };
|
return { permits: [], metadata: { count: 0, page, pageSize } };
|
||||||
|
|
||||||
const regex = new RegExp(params.searchToken, 'i');
|
const regex = new RegExp(params.searchToken, "i");
|
||||||
|
|
||||||
const permitsList = await permitModel.aggregate([
|
const permitsList = await permitModel.aggregate([
|
||||||
{
|
{
|
||||||
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
|
$match: { $and: [{ tenantId: user.tenantId }, { ...filterObj }] },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
$or: [
|
$or: [
|
||||||
{ permitNumber: { $regex: regex } },
|
{ permitNumber: { $regex: regex } },
|
||||||
{ link: { $regex: regex } },
|
{ link: { $regex: regex } },
|
||||||
{ 'address.full_address': { $regex: regex } },
|
{ "address.full_address": { $regex: regex } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$lookup: {
|
$lookup: {
|
||||||
from: 'users',
|
from: "users",
|
||||||
localField: 'assignedTo',
|
localField: "assignedTo",
|
||||||
foreignField: '_id',
|
foreignField: "_id",
|
||||||
as: 'assignedRec',
|
as: "assignedRec",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -272,12 +287,12 @@ export async function searchPermit(params: PageQueryParams, tenantId: string) {
|
|||||||
statusUpdated: 1,
|
statusUpdated: 1,
|
||||||
assignedTo: {
|
assignedTo: {
|
||||||
$let: {
|
$let: {
|
||||||
vars: { assigned: { $arrayElemAt: ['$assignedRec', 0] } },
|
vars: { assigned: { $arrayElemAt: ["$assignedRec", 0] } },
|
||||||
in: {
|
in: {
|
||||||
_id: '$$assigned._id',
|
_id: "$$assigned._id",
|
||||||
pid: '$$assigned.pid',
|
pid: "$$assigned.pid",
|
||||||
name: '$$assigned.name',
|
name: "$$assigned.name",
|
||||||
avatar: '$$assigned.avatar',
|
avatar: "$$assigned.avatar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -285,7 +300,7 @@ export async function searchPermit(params: PageQueryParams, tenantId: string) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
$facet: {
|
$facet: {
|
||||||
metadata: [{ $count: 'count' }],
|
metadata: [{ $count: "count" }],
|
||||||
data: [
|
data: [
|
||||||
{ $sort: sortObj },
|
{ $sort: sortObj },
|
||||||
{ $skip: (page - 1) * pageSize },
|
{ $skip: (page - 1) * pageSize },
|
||||||
@@ -312,12 +327,12 @@ export async function getUniqueValuesPermit(field: string, tenenatId: string) {
|
|||||||
let values = await permitModel.distinct(field, { tenantId: tenenatId });
|
let values = await permitModel.distinct(field, { tenantId: tenenatId });
|
||||||
|
|
||||||
let matchedValues = [];
|
let matchedValues = [];
|
||||||
if (field === 'county.name') {
|
if (field === "county.name") {
|
||||||
matchedValues = await orgModel.find().where('name').in(values).exec();
|
matchedValues = await orgModel.find().where("name").in(values).exec();
|
||||||
} else if (field === 'client') {
|
} else if (field === "client") {
|
||||||
matchedValues = await orgModel.find().where('_id').in(values).exec();
|
matchedValues = await orgModel.find().where("_id").in(values).exec();
|
||||||
} else if (field === 'assignedTo') {
|
} else if (field === "assignedTo") {
|
||||||
matchedValues = await userModel.find().where('name').in(values).exec();
|
matchedValues = await userModel.find().where("name").in(values).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedValues.length > 0) {
|
if (matchedValues.length > 0) {
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||||
import { PageQueryParams } from '../pagination';
|
import { PageQueryParams } from "../pagination";
|
||||||
import {
|
import {
|
||||||
getProcessedPermit,
|
getProcessedPermit,
|
||||||
getUniqueValuesProcessed,
|
getUniqueValuesProcessed,
|
||||||
listProcessedPermits,
|
listProcessedPermits,
|
||||||
updateProcessed,
|
updateProcessed,
|
||||||
} from './processed.service';
|
} from "./processed.service";
|
||||||
import { $processed, UpdateProcessedInput } from './processed.schema';
|
import { $processed, UpdateProcessedInput } from "./processed.schema";
|
||||||
import { noteRoutes } from '../note/note.route';
|
import { noteRoutes } from "../note/note.route";
|
||||||
|
|
||||||
export async function processedRoutes(fastify: FastifyInstance) {
|
export async function processedRoutes(fastify: FastifyInstance) {
|
||||||
fastify.get(
|
fastify.get(
|
||||||
'/',
|
"/",
|
||||||
{
|
{
|
||||||
schema: {
|
schema: {
|
||||||
querystring: $processed('pageQueryParams'),
|
querystring: $processed("pageQueryParams"),
|
||||||
},
|
},
|
||||||
config: { requiredClaims: ['permit:read'] },
|
config: { requiredClaims: ["permit:read"] },
|
||||||
preHandler: [fastify.authorize],
|
preHandler: [fastify.authorize],
|
||||||
},
|
},
|
||||||
async (req: FastifyRequest, res: FastifyReply) => {
|
async (req: FastifyRequest, res: FastifyReply) => {
|
||||||
const params = req.query as PageQueryParams;
|
const params = req.query as PageQueryParams;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const permits = await listProcessedPermits(params, req.user.tenantId);
|
const permits = await listProcessedPermits(params, req.user);
|
||||||
return res.code(200).send(permits);
|
return res.code(200).send(permits);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return err;
|
return err;
|
||||||
@@ -32,19 +32,19 @@ export async function processedRoutes(fastify: FastifyInstance) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fastify.get(
|
fastify.get(
|
||||||
'/search',
|
"/search",
|
||||||
{
|
{
|
||||||
schema: {
|
schema: {
|
||||||
querystring: $processed('pageQueryParams'),
|
querystring: $processed("pageQueryParams"),
|
||||||
},
|
},
|
||||||
config: { requiredClaims: ['permit:read'] },
|
config: { requiredClaims: ["permit:read"] },
|
||||||
preHandler: [fastify.authorize],
|
preHandler: [fastify.authorize],
|
||||||
},
|
},
|
||||||
async (req: FastifyRequest, res: FastifyReply) => {
|
async (req: FastifyRequest, res: FastifyReply) => {
|
||||||
const params = req.query as PageQueryParams;
|
const params = req.query as PageQueryParams;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const permits = await listProcessedPermits(params, req.user.tenantId);
|
const permits = await listProcessedPermits(params, req.user);
|
||||||
return res.code(200).send(permits);
|
return res.code(200).send(permits);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return err;
|
return err;
|
||||||
@@ -53,15 +53,15 @@ export async function processedRoutes(fastify: FastifyInstance) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fastify.get(
|
fastify.get(
|
||||||
'/:permitId',
|
"/:permitId",
|
||||||
{
|
{
|
||||||
schema: {
|
schema: {
|
||||||
params: {
|
params: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: { permitId: { type: 'string' } },
|
properties: { permitId: { type: "string" } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config: { requiredClaims: ['permit:read'] },
|
config: { requiredClaims: ["permit:read"] },
|
||||||
preHandler: [fastify.authorize],
|
preHandler: [fastify.authorize],
|
||||||
},
|
},
|
||||||
async (req: FastifyRequest, res: FastifyReply) => {
|
async (req: FastifyRequest, res: FastifyReply) => {
|
||||||
@@ -77,16 +77,16 @@ export async function processedRoutes(fastify: FastifyInstance) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fastify.patch(
|
fastify.patch(
|
||||||
'/:permitId',
|
"/:permitId",
|
||||||
{
|
{
|
||||||
schema: {
|
schema: {
|
||||||
params: {
|
params: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: { permitId: { type: 'string' } },
|
properties: { permitId: { type: "string" } },
|
||||||
},
|
},
|
||||||
body: $processed('updateProcessedInput'),
|
body: $processed("updateProcessedInput"),
|
||||||
},
|
},
|
||||||
config: { requiredClaims: ['permit:write'] },
|
config: { requiredClaims: ["permit:write"] },
|
||||||
preHandler: [fastify.authorize],
|
preHandler: [fastify.authorize],
|
||||||
},
|
},
|
||||||
async (req: FastifyRequest, res: FastifyReply) => {
|
async (req: FastifyRequest, res: FastifyReply) => {
|
||||||
@@ -103,17 +103,17 @@ export async function processedRoutes(fastify: FastifyInstance) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fastify.get(
|
fastify.get(
|
||||||
'/fields/:field',
|
"/fields/:field",
|
||||||
{
|
{
|
||||||
schema: {
|
schema: {
|
||||||
params: {
|
params: {
|
||||||
type: 'object',
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
field: { type: 'string' },
|
field: { type: "string" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config: { requiredClaims: ['permit:read'] },
|
config: { requiredClaims: ["permit:read"] },
|
||||||
preHandler: [fastify.authorize],
|
preHandler: [fastify.authorize],
|
||||||
},
|
},
|
||||||
async (req: FastifyRequest, res: FastifyReply) => {
|
async (req: FastifyRequest, res: FastifyReply) => {
|
||||||
@@ -132,8 +132,8 @@ export async function processedRoutes(fastify: FastifyInstance) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await noteRoutes(fastify, {
|
await noteRoutes(fastify, {
|
||||||
read: 'permit:read',
|
read: "permit:read",
|
||||||
write: 'permit:write',
|
write: "permit:write",
|
||||||
delete: 'permit:delete',
|
delete: "permit:delete",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { AuthenticatedUser } from '../auth';
|
import mongoose from "mongoose";
|
||||||
import { orgModel } from '../organization/organization.schema';
|
import { AuthenticatedUser } from "../auth";
|
||||||
import { getFilterObject, getSortObject, PageQueryParams } from '../pagination';
|
import { orgModel } from "../organization/organization.schema";
|
||||||
import { userModel } from '../user/user.schema';
|
import { getFilterObject, getSortObject, PageQueryParams } from "../pagination";
|
||||||
|
import { userModel } from "../user/user.schema";
|
||||||
import {
|
import {
|
||||||
processedFields,
|
processedFields,
|
||||||
processedModel,
|
processedModel,
|
||||||
UpdateProcessedInput,
|
UpdateProcessedInput,
|
||||||
} from './processed.schema';
|
} from "./processed.schema";
|
||||||
|
|
||||||
export async function getProcessedPermit(permitId: String, tenantId: String) {
|
export async function getProcessedPermit(permitId: String, tenantId: String) {
|
||||||
return await processedModel.findOne({
|
return await processedModel.findOne({
|
||||||
@@ -27,35 +28,39 @@ export async function updateProcessed(
|
|||||||
{ ...input, lastUpdateDate: new Date() },
|
{ ...input, lastUpdateDate: new Date() },
|
||||||
{ new: true }
|
{ new: true }
|
||||||
)
|
)
|
||||||
.populate({ path: 'county', select: 'pid name avatar' })
|
.populate({ path: "county", select: "pid name avatar" })
|
||||||
.populate({ path: 'client', select: 'pid name avatar' })
|
.populate({ path: "client", select: "pid name avatar" })
|
||||||
.populate({ path: 'assignedTo', select: 'pid name avatar' })
|
.populate({ path: "assignedTo", select: "pid name avatar" })
|
||||||
.populate({ path: 'createdBy', select: 'pid name avatar' });
|
.populate({ path: "createdBy", select: "pid name avatar" });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listProcessedPermits(
|
export async function listProcessedPermits(
|
||||||
params: PageQueryParams,
|
params: PageQueryParams,
|
||||||
tenantId: string
|
user: AuthenticatedUser
|
||||||
) {
|
) {
|
||||||
const page = params.page || 1;
|
const page = params.page || 1;
|
||||||
const pageSize = params.pageSize || 10;
|
const pageSize = params.pageSize || 10;
|
||||||
const sortObj = getSortObject(params, processedFields);
|
const sortObj = getSortObject(params, processedFields);
|
||||||
const filterObj = getFilterObject(params);
|
const filterObj = getFilterObject(params) || {};
|
||||||
|
|
||||||
|
if (user.role == "client") {
|
||||||
|
filterObj["client"] = new mongoose.Types.ObjectId(user.orgId);
|
||||||
|
}
|
||||||
|
|
||||||
const pipeline: any = [
|
const pipeline: any = [
|
||||||
{
|
{
|
||||||
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
|
$match: { $and: [{ tenantId: user.tenantId }, { ...filterObj }] },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (params.searchToken && params.searchToken != '') {
|
if (params.searchToken && params.searchToken != "") {
|
||||||
const regex = new RegExp(params.searchToken, 'i');
|
const regex = new RegExp(params.searchToken, "i");
|
||||||
pipeline.push({
|
pipeline.push({
|
||||||
$match: {
|
$match: {
|
||||||
$or: [
|
$or: [
|
||||||
{ permitNumber: { $regex: regex } },
|
{ permitNumber: { $regex: regex } },
|
||||||
{ link: { $regex: regex } },
|
{ link: { $regex: regex } },
|
||||||
{ 'address.full_address': { $regex: regex } },
|
{ "address.full_address": { $regex: regex } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -65,10 +70,10 @@ export async function listProcessedPermits(
|
|||||||
...[
|
...[
|
||||||
{
|
{
|
||||||
$lookup: {
|
$lookup: {
|
||||||
from: 'users',
|
from: "users",
|
||||||
localField: 'assignedTo',
|
localField: "assignedTo",
|
||||||
foreignField: '_id',
|
foreignField: "_id",
|
||||||
as: 'assignedRec',
|
as: "assignedRec",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -111,12 +116,12 @@ export async function listProcessedPermits(
|
|||||||
transferDate: 1,
|
transferDate: 1,
|
||||||
assignedTo: {
|
assignedTo: {
|
||||||
$let: {
|
$let: {
|
||||||
vars: { assigned: { $arrayElemAt: ['$assignedRec', 0] } },
|
vars: { assigned: { $arrayElemAt: ["$assignedRec", 0] } },
|
||||||
in: {
|
in: {
|
||||||
_id: '$$assigned._id',
|
_id: "$$assigned._id",
|
||||||
pid: '$$assigned.pid',
|
pid: "$$assigned.pid",
|
||||||
name: '$$assigned.name',
|
name: "$$assigned.name",
|
||||||
avatar: '$$assigned.avatar',
|
avatar: "$$assigned.avatar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -124,7 +129,7 @@ export async function listProcessedPermits(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
$facet: {
|
$facet: {
|
||||||
metadata: [{ $count: 'count' }],
|
metadata: [{ $count: "count" }],
|
||||||
data: [
|
data: [
|
||||||
{ $sort: sortObj },
|
{ $sort: sortObj },
|
||||||
{ $skip: (page - 1) * pageSize },
|
{ $skip: (page - 1) * pageSize },
|
||||||
@@ -157,12 +162,12 @@ export async function getUniqueValuesProcessed(
|
|||||||
let values = await processedModel.distinct(field, { tenantId: tenenatId });
|
let values = await processedModel.distinct(field, { tenantId: tenenatId });
|
||||||
|
|
||||||
let matchedValues = [];
|
let matchedValues = [];
|
||||||
if (field === 'county.name') {
|
if (field === "county.name") {
|
||||||
matchedValues = await orgModel.find().where('name').in(values).exec();
|
matchedValues = await orgModel.find().where("name").in(values).exec();
|
||||||
} else if (field === 'client') {
|
} else if (field === "client") {
|
||||||
matchedValues = await orgModel.find().where('_id').in(values).exec();
|
matchedValues = await orgModel.find().where("_id").in(values).exec();
|
||||||
} else if (field === 'assignedTo') {
|
} else if (field === "assignedTo") {
|
||||||
matchedValues = await userModel.find().where('name').in(values).exec();
|
matchedValues = await userModel.find().where("name").in(values).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedValues.length > 0) {
|
if (matchedValues.length > 0) {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyReply, FastifyRequest } from "fastify";
|
||||||
import {
|
import {
|
||||||
createUser,
|
createUser,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
|
ErrMissingOrdId,
|
||||||
ErrOpNotValid,
|
ErrOpNotValid,
|
||||||
getUser,
|
getUser,
|
||||||
listUsers,
|
listUsers,
|
||||||
updateUser,
|
updateUser,
|
||||||
} from './user.service';
|
} from "./user.service";
|
||||||
import { CreateUserInput, UpdateUserInput } from './user.schema';
|
import { CreateUserInput, UpdateUserInput } from "./user.schema";
|
||||||
|
|
||||||
export async function createUserHandler(
|
export async function createUserHandler(
|
||||||
req: FastifyRequest,
|
req: FastifyRequest,
|
||||||
@@ -19,8 +20,12 @@ export async function createUserHandler(
|
|||||||
const user = await createUser(body, req.user);
|
const user = await createUser(body, req.user);
|
||||||
return res.code(201).send(user);
|
return res.code(201).send(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error && err.message == ErrOpNotValid.message)
|
if (
|
||||||
return res.code(400).send(err.message);
|
err instanceof Error &&
|
||||||
|
(err.message == ErrOpNotValid.message ||
|
||||||
|
err.message == ErrMissingOrdId.message)
|
||||||
|
)
|
||||||
|
return res.code(400).send({ error: err.message });
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,14 +34,14 @@ export async function getCurrentUserHandler(
|
|||||||
req: FastifyRequest,
|
req: FastifyRequest,
|
||||||
res: FastifyReply
|
res: FastifyReply
|
||||||
) {
|
) {
|
||||||
if (req.user.type !== 'user') {
|
if (req.user.type !== "user") {
|
||||||
return res.code(400).send();
|
return res.code(400).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await getUser(req.user.userId);
|
const user = await getUser(req.user.userId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
return res.code(404).send({ error: 'resource not found' });
|
return res.code(404).send({ error: "resource not found" });
|
||||||
|
|
||||||
return res.code(200).send(user);
|
return res.code(200).send(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -50,7 +55,7 @@ export async function getUserHandler(req: FastifyRequest, res: FastifyReply) {
|
|||||||
try {
|
try {
|
||||||
const user = await getUser(userId);
|
const user = await getUser(userId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
return res.code(404).send({ error: 'resource not found' });
|
return res.code(404).send({ error: "resource not found" });
|
||||||
|
|
||||||
return res.code(200).send(user);
|
return res.code(200).send(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -76,7 +81,7 @@ export async function updateUserHandler(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedUser = await updateUser(userId, input);
|
const updatedUser = await updateUser(userId, input);
|
||||||
if (!updateUser) return res.code(404).send({ error: 'resource not found' });
|
if (!updateUser) return res.code(404).send({ error: "resource not found" });
|
||||||
|
|
||||||
return res.code(200).send(updatedUser);
|
return res.code(200).send(updatedUser);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -93,7 +98,7 @@ export async function deleteUserHandler(
|
|||||||
try {
|
try {
|
||||||
const deleteResult = await deleteUser(userId, req.user.tenantId);
|
const deleteResult = await deleteUser(userId, req.user.tenantId);
|
||||||
if (deleteResult.deletedCount == 0)
|
if (deleteResult.deletedCount == 0)
|
||||||
return res.code(404).send({ error: 'resource not found' });
|
return res.code(404).send({ error: "resource not found" });
|
||||||
|
|
||||||
return res.code(204).send();
|
return res.code(204).send();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { buildJsonSchemas } from 'fastify-zod';
|
import { buildJsonSchemas } from "fastify-zod";
|
||||||
import mongoose, { InferSchemaType } from 'mongoose';
|
import mongoose, { InferSchemaType } from "mongoose";
|
||||||
import { z } from 'zod';
|
import { z } from "zod";
|
||||||
import { roles } from '../utils/roles';
|
import { roles } from "../utils/roles";
|
||||||
|
|
||||||
const userSchema = new mongoose.Schema({
|
const userSchema = new mongoose.Schema({
|
||||||
tenantId: {
|
tenantId: {
|
||||||
@@ -30,7 +30,7 @@ const userSchema = new mongoose.Schema({
|
|||||||
},
|
},
|
||||||
defaultClient: {
|
defaultClient: {
|
||||||
type: mongoose.Types.ObjectId,
|
type: mongoose.Types.ObjectId,
|
||||||
ref: 'organization',
|
ref: "organization",
|
||||||
},
|
},
|
||||||
passKeys: [new mongoose.Schema({}, { _id: false, strict: false })],
|
passKeys: [new mongoose.Schema({}, { _id: false, strict: false })],
|
||||||
challenge: new mongoose.Schema(
|
challenge: new mongoose.Schema(
|
||||||
@@ -53,7 +53,7 @@ const userSchema = new mongoose.Schema({
|
|||||||
dev: Boolean,
|
dev: Boolean,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userModel = mongoose.model('user', userSchema);
|
export const userModel = mongoose.model("user", userSchema);
|
||||||
|
|
||||||
export type User = InferSchemaType<typeof userSchema>;
|
export type User = InferSchemaType<typeof userSchema>;
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ const userCore = {
|
|||||||
lastName: z.string().max(30),
|
lastName: z.string().max(30),
|
||||||
email: z
|
email: z
|
||||||
.string({
|
.string({
|
||||||
required_error: 'Email is required',
|
required_error: "Email is required",
|
||||||
invalid_type_error: 'Email must be a valid string',
|
invalid_type_error: "Email must be a valid string",
|
||||||
})
|
})
|
||||||
.email(),
|
.email(),
|
||||||
avatar: z.string().optional(),
|
avatar: z.string().optional(),
|
||||||
@@ -75,14 +75,9 @@ const createUserInput = z
|
|||||||
.object({
|
.object({
|
||||||
...userCore,
|
...userCore,
|
||||||
})
|
})
|
||||||
.superRefine((data, ctx) => {
|
.refine((data) => data.role !== "client" || data.orgId, {
|
||||||
if (data.role == 'builder' && !data.orgId) {
|
message: 'orgId is required when role is "client"',
|
||||||
ctx.addIssue({
|
path: ["orgId"],
|
||||||
path: ['orgId'],
|
|
||||||
message: 'orgId is required when role is "builder"',
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateUserInput = z.object({
|
const updateUserInput = z.object({
|
||||||
@@ -90,8 +85,8 @@ const updateUserInput = z.object({
|
|||||||
lastName: z.string().max(30).optional(),
|
lastName: z.string().max(30).optional(),
|
||||||
email: z
|
email: z
|
||||||
.string({
|
.string({
|
||||||
required_error: 'Email is required',
|
required_error: "Email is required",
|
||||||
invalid_type_error: 'Email must be a valid string',
|
invalid_type_error: "Email must be a valid string",
|
||||||
})
|
})
|
||||||
.email()
|
.email()
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -125,5 +120,5 @@ export const { schemas: userSchemas, $ref: $user } = buildJsonSchemas(
|
|||||||
updateUserInput,
|
updateUserInput,
|
||||||
userResponse,
|
userResponse,
|
||||||
},
|
},
|
||||||
{ $id: 'user' }
|
{ $id: "user" }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,43 +1,50 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from "mongoose";
|
||||||
import { generateId, generateToken } from '../utils/id';
|
import { generateId, generateToken } from "../utils/id";
|
||||||
import { CreateUserInput, UpdateUserInput, userModel } from './user.schema';
|
import { CreateUserInput, UpdateUserInput, userModel } from "./user.schema";
|
||||||
import { sendMail } from '../utils/mail';
|
import { sendMail } from "../utils/mail";
|
||||||
import { AuthenticatedUser } from '../auth';
|
import { AuthenticatedUser } from "../auth";
|
||||||
|
|
||||||
export const ErrOpNotValid = new Error('operation is not valid');
|
export const ErrOpNotValid = new Error("operation is not valid");
|
||||||
|
export const ErrMissingOrdId = new Error(
|
||||||
|
"orgId is required when role is client"
|
||||||
|
);
|
||||||
|
|
||||||
export async function createUser(
|
export async function createUser(
|
||||||
input: CreateUserInput,
|
input: CreateUserInput,
|
||||||
user: AuthenticatedUser
|
user: AuthenticatedUser
|
||||||
) {
|
) {
|
||||||
if (input.role == 'admin' && user.role != 'superAdmin') {
|
if (input.role == "admin" && user.role != "superAdmin") {
|
||||||
throw ErrOpNotValid;
|
throw ErrOpNotValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input.role == "client" && !input.orgId) {
|
||||||
|
throw ErrMissingOrdId;
|
||||||
|
}
|
||||||
|
|
||||||
const token = await generateToken();
|
const token = await generateToken();
|
||||||
|
|
||||||
const newUser = await userModel.create({
|
const newUser = await userModel.create({
|
||||||
tenantId: user.tenantId,
|
tenantId: user.tenantId,
|
||||||
pid: generateId(),
|
pid: generateId(),
|
||||||
name: input.firstName + ' ' + input.lastName,
|
name: input.firstName + " " + input.lastName,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
createdBy: user.userId,
|
createdBy: user.userId,
|
||||||
token: {
|
token: {
|
||||||
value: token,
|
value: token,
|
||||||
expiry: new Date(Date.now() + 3600 * 48 * 1000),
|
expiry: new Date(Date.now() + 3600 * 48 * 1000),
|
||||||
},
|
},
|
||||||
status: 'invited',
|
status: "invited",
|
||||||
...input,
|
...input,
|
||||||
});
|
});
|
||||||
|
|
||||||
const sent = await sendMail(
|
const sent = await sendMail(
|
||||||
input.email,
|
input.email,
|
||||||
'You have been invited to Quicker Permtis.',
|
"You have been invited to Quicker Permtis.",
|
||||||
`Click <a href="${
|
`Click <a href="${
|
||||||
process.env.SERVER_DOMAIN +
|
process.env.SERVER_DOMAIN +
|
||||||
'/auth/webauthn/register?token=' +
|
"/auth/webauthn/register?token=" +
|
||||||
token +
|
token +
|
||||||
'&email=' +
|
"&email=" +
|
||||||
newUser.email
|
newUser.email
|
||||||
}">here</a> to register.`
|
}">here</a> to register.`
|
||||||
);
|
);
|
||||||
@@ -56,7 +63,7 @@ export async function getUser(userId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserByToken(token: string) {
|
export async function getUserByToken(token: string) {
|
||||||
return await userModel.findOne({ 'token.value': token });
|
return await userModel.findOne({ "token.value": token });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserByEmail(email: string) {
|
export async function getUserByEmail(email: string) {
|
||||||
@@ -67,7 +74,7 @@ export async function listUsers(tenantId: string) {
|
|||||||
return await userModel
|
return await userModel
|
||||||
.find({ $and: [{ tenantId: tenantId }, { dev: { $ne: true } }] })
|
.find({ $and: [{ tenantId: tenantId }, { dev: { $ne: true } }] })
|
||||||
.select(
|
.select(
|
||||||
'_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin'
|
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +84,7 @@ export async function updateUser(userId: string, input: UpdateUserInput) {
|
|||||||
new: true,
|
new: true,
|
||||||
})
|
})
|
||||||
.select(
|
.select(
|
||||||
'_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin'
|
"_id pid orgId firstName lastName name email role avatar status createdAt createdBy lastLogin"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,23 @@ export const rules: Record<
|
|||||||
users: ["__v"],
|
users: ["__v"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
client: {
|
||||||
|
claims: [
|
||||||
|
"permit:read",
|
||||||
|
"file:upload",
|
||||||
|
"file:download",
|
||||||
|
"view:read",
|
||||||
|
"view:write",
|
||||||
|
"view:delete",
|
||||||
|
],
|
||||||
|
hiddenFields: {
|
||||||
|
orgs: ["__v"],
|
||||||
|
permits: ["__v"],
|
||||||
|
rts: ["__v"],
|
||||||
|
tasks: ["__v"],
|
||||||
|
users: ["__v"],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const roles = Object.keys(rules) as [
|
export const roles = Object.keys(rules) as [
|
||||||
|
|||||||
Reference in New Issue
Block a user