location update optimized with schema changes and geoJson Compatability testing endpoint with encrypted key authorization
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
createLocation,
|
||||
getLocationByQuery,
|
||||
getLocationByRadius,
|
||||
} from "../models/Location/operations";
|
||||
|
||||
export const createLocationController = async (req, res) => {
|
||||
try {
|
||||
const { user } = res.locals;
|
||||
console.log("BG LOCATIONREQUEST BODY", req.body, user);
|
||||
// const location = await createLocation({ ...req.body, user: user?.id });
|
||||
res.status(200).json({ location: "location" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
export const getLocationController = async (req, res) => {
|
||||
try {
|
||||
const location = await getLocationByQuery(req.query);
|
||||
res.status(200).json(location);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import dayjs from "dayjs";
|
||||
import {
|
||||
getAccessToken,
|
||||
getRefreshToken,
|
||||
getEncryptedPayload,
|
||||
} from "../middlewares/jwt.middleware.js";
|
||||
|
||||
export const createUserController = async (req, res, next) => {
|
||||
@@ -45,14 +46,20 @@ export const loginController = async (req, res, next) => {
|
||||
}
|
||||
|
||||
const accessToken = getAccessToken({ id: userExists._id });
|
||||
const encryptedKey = getEncryptedPayload({ id: userExists._id });
|
||||
const refreshToken = getRefreshToken();
|
||||
|
||||
userExists.refreshToken = refreshToken;
|
||||
await userExists.save();
|
||||
|
||||
res
|
||||
.status(200)
|
||||
.json({ result: { accessToken, refreshToken, name: userExists?.name } });
|
||||
res.status(200).json({
|
||||
result: {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
encryptedKey,
|
||||
name: userExists?.name,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("loginController Error", error);
|
||||
next(error);
|
||||
@@ -73,4 +80,3 @@ export const renewAccessTokenController = async (req, res, next) => {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -196,6 +196,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/location/create": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Location"
|
||||
],
|
||||
"summary": "Create New Location",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "key",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"coordinates": {
|
||||
"type": "array",
|
||||
"required": true
|
||||
},
|
||||
"venue": {
|
||||
"type": "string",
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/uploads/single/{folder}": {
|
||||
"post": {
|
||||
"tags": [
|
||||
|
||||
@@ -4,6 +4,7 @@ import upload from "./upload.js";
|
||||
import user from "./user.js";
|
||||
import profile from "./profile.js";
|
||||
import admin from "./admin.js";
|
||||
import location from "./location.js"
|
||||
|
||||
const GENERAL_CONFIG = {
|
||||
openapi: "3.0.1",
|
||||
@@ -43,6 +44,7 @@ const GENERAL_CONFIG = {
|
||||
let paths = {
|
||||
...user,
|
||||
...profile,
|
||||
...location,
|
||||
...upload,
|
||||
...admin,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
export default {
|
||||
"/location/create": {
|
||||
post: {
|
||||
tags: ["Location"],
|
||||
summary: "Create New Location",
|
||||
parameters: [
|
||||
{
|
||||
in: "query",
|
||||
name: "key",
|
||||
required: true,
|
||||
schema: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
],
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: {
|
||||
type: "string",
|
||||
required: true,
|
||||
},
|
||||
coordinates: {
|
||||
type: "array",
|
||||
required: true,
|
||||
},
|
||||
venue: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: "OK",
|
||||
content: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Schema } from "mongoose";
|
||||
|
||||
export default new Schema({
|
||||
type: { type: String, default: "Point", enum: ["Point"] },
|
||||
coordinates: [Number],
|
||||
});
|
||||
|
||||
export const coordinatesToGeoJson = (coords) => {
|
||||
return {
|
||||
type: "Point",
|
||||
coordinates: [coords.latitude, coords.longitude],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as locationSchema } from "./geoJson.helper";
|
||||
export * from "./geoJson.helper";
|
||||
|
||||
@@ -11,7 +11,7 @@ import swaggerUi from "swagger-ui-express";
|
||||
import swaggerDoc from "./documentation/documentation.json";
|
||||
import getSwaggerDocument from "./documentation";
|
||||
// import socketState from "./state/socketState.js";
|
||||
import { AdminRoutes, UserRoutes } from "./routes";
|
||||
import { AdminRoutes, UserRoutes, LocationRoutes } from "./routes";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -43,6 +43,7 @@ app.use(express.json());
|
||||
app.use("/v0/docs", swaggerUi.serve, swaggerUi.setup(swaggerDoc));
|
||||
app.use("/v0/user", UserRoutes);
|
||||
app.use("/v0/admin", AdminRoutes);
|
||||
app.use("/v0/location", LocationRoutes);
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
const statusCode = err.status || 500;
|
||||
|
||||
@@ -2,14 +2,42 @@ import jwt from "jsonwebtoken";
|
||||
import crypto from "node:crypto";
|
||||
import User from "../models/User/model.js";
|
||||
import Access from "../models/Access/model.js";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const { ECT_KEY, ECT_IV, ECT_METHOD } = process.env;
|
||||
|
||||
export const getAccessToken = (payload) => {
|
||||
return jwt.sign(payload, process.env.ECT_KEY, {
|
||||
return jwt.sign(payload, ECT_KEY, {
|
||||
algorithm: "HS256",
|
||||
expiresIn: "7m",
|
||||
});
|
||||
};
|
||||
|
||||
export const getEncryptedPayload = (payload) => {
|
||||
const cipher = crypto.createCipheriv(ECT_METHOD, ECT_KEY, ECT_IV);
|
||||
let encrypted = cipher.update(JSON.stringify(payload), "utf8", "hex");
|
||||
encrypted += cipher.final("hex");
|
||||
const tag = cipher.getAuthTag().toString("hex");
|
||||
return `${ECT_IV}:${tag}:${encrypted}`;
|
||||
};
|
||||
|
||||
export const getDecryptedPayload = (payload) => {
|
||||
const [ivHex, authTagHex, encryptedHex] = payload.split(":");
|
||||
|
||||
const iv = Buffer.from(ivHex, "hex");
|
||||
const authTag = Buffer.from(authTagHex, "hex");
|
||||
const decipher = crypto.createDecipheriv(ECT_METHOD, ECT_KEY, ECT_IV);
|
||||
|
||||
decipher.setAuthTag(authTag); // Verify integrity
|
||||
|
||||
let decrypted = decipher.update(encryptedHex, "hex", "utf8");
|
||||
decrypted += decipher.final("utf8");
|
||||
|
||||
return JSON.parse(decrypted);
|
||||
};
|
||||
|
||||
export const getRefreshToken = () => {
|
||||
return crypto.randomBytes(32).toString("hex");
|
||||
};
|
||||
@@ -68,3 +96,31 @@ export const authorize = (requiredModule, requiredPermission) => {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const authorizeWithEncryptedKey = (
|
||||
requiredModule,
|
||||
requiredPermission,
|
||||
) => {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
const { key } = req.query;
|
||||
|
||||
if (!key) {
|
||||
throw { code: 401, message: "Encrypted Auth Key Missing" };
|
||||
}
|
||||
|
||||
const verifiedPayload = getDecryptedPayload(key);
|
||||
|
||||
const userExists = await User.exists({ _id: verifiedPayload.id });
|
||||
if (!userExists) {
|
||||
throw { code: 404, message: "User not found" };
|
||||
}
|
||||
|
||||
res.locals.user = verifiedPayload;
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error("AUTH_ERROR", error);
|
||||
res.status(error.code || 401).json(error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import { model, Schema, Types } from "mongoose";
|
||||
|
||||
const coordinatesSchema = new Schema({
|
||||
latitude: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
longitude: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const schema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
@@ -41,10 +30,6 @@ const schema = new Schema({
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
coordinates: {
|
||||
type: coordinatesSchema,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import { model, Schema, Types } from "mongoose";
|
||||
import { locationSchema } from "../../helpers";
|
||||
|
||||
const schema = new Schema({});
|
||||
const schema = new Schema({
|
||||
user: {
|
||||
type: Types.ObjectId,
|
||||
required: true,
|
||||
ref: "user",
|
||||
},
|
||||
coordinates: locationSchema,
|
||||
createdOn: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
venue: {
|
||||
type: Types.ObjectId,
|
||||
required: false,
|
||||
ref: "venue",
|
||||
},
|
||||
});
|
||||
|
||||
schema.index({ coordinates: "2dsphere" });
|
||||
|
||||
export default model("location", schema);
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import Model from "./model.js";
|
||||
|
||||
import { coordinatesToGeoJson } from "../../helpers/geoJson.helper.js";
|
||||
import dayjs from "dayjs";
|
||||
// Create
|
||||
export const createLocation = async (data) => {
|
||||
return await Model.create(data);
|
||||
const geoJson = coordinatesToGeoJson(data.coordinates);
|
||||
return await Model.create({
|
||||
...data,
|
||||
coordinates: geoJson,
|
||||
createdOn: dayjs().toISOString(),
|
||||
});
|
||||
};
|
||||
|
||||
// Read (Get by ID)
|
||||
@@ -13,7 +19,12 @@ export const getLocationById = async (id) => {
|
||||
// Read (Get by Query)
|
||||
export const getLocationByQuery = async (query) => {
|
||||
return await Model.findOne(query);
|
||||
}
|
||||
};
|
||||
|
||||
// Read (Get by Radius)
|
||||
export const getLocationByRadius = async (radius) => {
|
||||
return await Model.find(query);
|
||||
};
|
||||
|
||||
// Read (Get all)
|
||||
export const getAllLocations = async () => {
|
||||
|
||||
+63
-1
@@ -1,5 +1,67 @@
|
||||
import { model, Schema, Types } from "mongoose";
|
||||
|
||||
const schema = new Schema({});
|
||||
const contactPersonSchema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
mobile: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
designation: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const schema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createdBy: {
|
||||
type: Types.ObjectId,
|
||||
required: true,
|
||||
ref: "user",
|
||||
},
|
||||
createdOn: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
mobile: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
address: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
city: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
state: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
pincode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
contactPersons: [contactPersonSchema],
|
||||
});
|
||||
|
||||
export default model("partner", schema);
|
||||
|
||||
Vendored
+63
-1
@@ -1,5 +1,67 @@
|
||||
import { model, Schema, Types } from "mongoose";
|
||||
|
||||
const schema = new Schema({});
|
||||
const contactPersonSchema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
mobile: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
designation: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const schema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createdBy: {
|
||||
type: Types.ObjectId,
|
||||
required: true,
|
||||
ref: "user",
|
||||
},
|
||||
createdOn: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
mobile: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
address: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
city: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
state: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
pincode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
contactPersons: [contactPersonSchema],
|
||||
});
|
||||
|
||||
export default model("vendor", schema);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { model, Schema, Types } from "mongoose";
|
||||
import { locationSchema } from "../../helpers";
|
||||
|
||||
const schema = new Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createdBy: {
|
||||
type: Types.ObjectId,
|
||||
required: true,
|
||||
ref: "user",
|
||||
},
|
||||
createdOn: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
location: locationSchema,
|
||||
category: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "OWN",
|
||||
enum: ["OWN", "PARTNER", "VENDOR", "CLIENT", "DISTRIBUTOR"],
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: [
|
||||
"HQ",
|
||||
"BRANCH",
|
||||
"WAREHOUSE",
|
||||
"VENDOR",
|
||||
"PARTNER",
|
||||
"DISTRIBUTOR",
|
||||
"CLIENT",
|
||||
],
|
||||
},
|
||||
entity: {
|
||||
type: Types.ObjectId,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
schema.index({ location: "2dsphere" });
|
||||
|
||||
export default model("venue", schema);
|
||||
@@ -0,0 +1,31 @@
|
||||
import Model from "./model.js";
|
||||
|
||||
// Create
|
||||
export const createVenue = async (data) => {
|
||||
return await Model.create(data);
|
||||
};
|
||||
|
||||
// Read (Get by ID)
|
||||
export const getVenueById = async (id) => {
|
||||
return await Model.findById(id);
|
||||
};
|
||||
|
||||
// Read (Get by Query)
|
||||
export const getVenueByQuery = async (query) => {
|
||||
return await Model.findOne(query);
|
||||
}
|
||||
|
||||
// Read (Get all)
|
||||
export const getAllVenues = async () => {
|
||||
return await Model.find();
|
||||
};
|
||||
|
||||
// Update
|
||||
export const updateVenue = async (id, data) => {
|
||||
return await Model.findByIdAndUpdate(id, data, { new: true });
|
||||
};
|
||||
|
||||
// Aggregate
|
||||
export const aggregateVenue = async (pipeline) => {
|
||||
return await Model.aggregate(pipeline);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as UserRoutes } from "./user.route";
|
||||
export { default as ProfileRoutes } from "./profile.route";
|
||||
export { default as AdminRoutes } from "./admin.route";
|
||||
export { default as LocationRoutes } from "./location.route";
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Router } from "express";
|
||||
import { authorizeWithEncryptedKey } from "../middlewares/jwt.middleware";
|
||||
import { createLocationController } from "../controllers/location.controller";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
router.post(
|
||||
"/create",
|
||||
authorizeWithEncryptedKey("location", "write"),
|
||||
createLocationController,
|
||||
);
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user