first
This commit is contained in:
commit
64cb27eb45
28 changed files with 1965 additions and 0 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
dist
|
||||||
|
.env
|
||||||
|
data
|
||||||
|
node_modules
|
||||||
|
tmp
|
3
api/.env.example
Normal file
3
api/.env.example
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
REDIS_CONNECTION_URL="redis://localhost:6379"
|
||||||
|
SERVER_PORT=3000
|
||||||
|
MONGO_CONNECTION_URL="mongodb://{example_user}:{example_password}@localhost:27017/{DB}"
|
82
api/README.md
Normal file
82
api/README.md
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# Dario API
|
||||||
|
|
||||||
|
The Dario API is a RESTful API built using Express.js, MongoDB, and Redis, designed to provide endpoints for user eligibility verification and registration.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Prerequisites](#prerequisites)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Starting the Server](#starting-the-server)
|
||||||
|
- [Endpoints](#endpoints)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Before running the Dario API, ensure you have the following installed on your system:
|
||||||
|
|
||||||
|
- Node.js (v14 or higher)
|
||||||
|
- MongoDB
|
||||||
|
- Redis Server
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. Clone this repository to your local machine:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/yourusername/dario-api.git
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install the required npm packages:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd api
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
Create a .env file in the root directory of the project with the variables listed in .env.example.
|
||||||
|
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
|
||||||
|
# POST /verify-eligibility
|
||||||
|
|
||||||
|
Verify user eligibility based on a provided key.
|
||||||
|
Example Request:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
POST http://localhost:3000/verify-eligibility
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"key": "your_user_key"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# POST /register
|
||||||
|
Register a user after verifying eligibility.
|
||||||
|
Example Request:
|
||||||
|
```sh
|
||||||
|
POST http://localhost:3000/register
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "johndoe@example.com",
|
||||||
|
"key": "your_user_key",
|
||||||
|
"name": "John Doe",
|
||||||
|
"phone": "123-456-7890"
|
||||||
|
}
|
||||||
|
```
|
1007
api/package-lock.json
generated
Normal file
1007
api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
api/package.json
Normal file
21
api/package.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "dario",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon dist/index.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"mongoose": "^7.4.2",
|
||||||
|
"redis": "^3.1.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.17"
|
||||||
|
}
|
||||||
|
}
|
12
api/request.http
Normal file
12
api/request.http
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
### Get
|
||||||
|
GET http://localhost:3000/
|
||||||
|
|
||||||
|
|
||||||
|
### Post
|
||||||
|
POST http://localhost:3000/verify-eligibility
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "kfda89@gmail.com"
|
||||||
|
}
|
6
api/src/common/types/User.ts
Normal file
6
api/src/common/types/User.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export interface User {
|
||||||
|
Name: string;
|
||||||
|
Email: string;
|
||||||
|
Phone: string;
|
||||||
|
EmployeeID: string;
|
||||||
|
}
|
63
api/src/controllers/DarioRegisterController.ts
Normal file
63
api/src/controllers/DarioRegisterController.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import { NextFunction, Request, Response } from "express";
|
||||||
|
import { Redis } from "../db/redis/Redis";
|
||||||
|
import { RegisterModel } from "../models/DarioRegisterModel";
|
||||||
|
import { User } from "../common/types/User";
|
||||||
|
|
||||||
|
export class DarioRegisterController {
|
||||||
|
redis: Redis;
|
||||||
|
registerModel: RegisterModel;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.redis = new Redis();
|
||||||
|
this.registerModel = new RegisterModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectToRedis() {
|
||||||
|
await this.redis.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async verifyEligibility(
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) {
|
||||||
|
const { key } = req.body;
|
||||||
|
try {
|
||||||
|
const user: any = await this.redis.findUserByKey(key);
|
||||||
|
res.status(200).json(user);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
res.status(500).send("Internal Server Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async register(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const { email, key, name, phone } = req.body;
|
||||||
|
try {
|
||||||
|
const user: any = await this.redis.findUserByKey(key);
|
||||||
|
if (!user) return res.status(404).send("User not found");
|
||||||
|
|
||||||
|
const isUserVerified: boolean = this.verifyUser(user, email, name, phone);
|
||||||
|
|
||||||
|
if (isUserVerified) {
|
||||||
|
await this.registerModel.create(user);
|
||||||
|
await this.redis.deleteUserByKey(key);
|
||||||
|
res.status(200).send("User registered");
|
||||||
|
} else {
|
||||||
|
res.status(404).send("User not found");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
res.status(500).send("Internal Server Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private verifyUser(
|
||||||
|
user: User,
|
||||||
|
email: string,
|
||||||
|
name: string,
|
||||||
|
phone: string
|
||||||
|
): boolean {
|
||||||
|
console.log(user.Phone !== phone);
|
||||||
|
return user.Email === email && user.Name.toLocaleLowerCase() === name.toLocaleLowerCase() && user.Phone === phone;
|
||||||
|
}
|
||||||
|
}
|
46
api/src/db/mongodb/schema.ts
Normal file
46
api/src/db/mongodb/schema.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import mongoose, { Document, Schema } from 'mongoose';
|
||||||
|
|
||||||
|
interface IUser extends Document {
|
||||||
|
Name: string;
|
||||||
|
Email: string;
|
||||||
|
Phone: string;
|
||||||
|
EmployeeID: string;
|
||||||
|
created_at: Date;
|
||||||
|
active: boolean;
|
||||||
|
updated_at: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userSchema = new Schema<IUser>({
|
||||||
|
Name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
Email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
Phone: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
EmployeeID: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const UserModel = mongoose.model<IUser>('User', userSchema);
|
||||||
|
|
||||||
|
export default UserModel;
|
78
api/src/db/redis/Redis.ts
Normal file
78
api/src/db/redis/Redis.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import * as redis from "redis";
|
||||||
|
import { User } from "../../common/types/User";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
const envVars = dotenv.config().parsed;
|
||||||
|
|
||||||
|
|
||||||
|
class Redis {
|
||||||
|
client: any;
|
||||||
|
|
||||||
|
async connect(): Promise<void> {
|
||||||
|
this.client = redis.createClient({ url: envVars.REDIS_CONNECTION_URL });
|
||||||
|
this.client.on("connect", () => {
|
||||||
|
console.log("Connected to Redis");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.on("error", (err) => {
|
||||||
|
console.log("Error connecting to Redis:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async findUserByKey(key: string) {
|
||||||
|
try {
|
||||||
|
this.connect();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.client.hget("users", key, (err: Error | null, userDataJson: string | null) => {
|
||||||
|
if (err) {
|
||||||
|
console.error("Error finding user in Redis:", err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
if (userDataJson) {
|
||||||
|
const userData: User = JSON.parse(userDataJson);
|
||||||
|
resolve(userData);
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error connecting to Redis:", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteUserByKey(key: string) {
|
||||||
|
try {
|
||||||
|
this.connect();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.client.hdel("users", key, (err: Error | null, userDataJson: string | null) => {
|
||||||
|
if (err) {
|
||||||
|
console.error("Error deleting user in Redis:", err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
console.log("User deleted")
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error connecting to Redis:", error);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
await new Promise((resolve) => this.client.quit(resolve));
|
||||||
|
console.log("Disconnected from Redis");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Redis };
|
37
api/src/index.ts
Normal file
37
api/src/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import express, { Express } from "express";
|
||||||
|
import { router } from "./routers/DarioRegisterRouter";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
|
||||||
|
class App {
|
||||||
|
app: Express;
|
||||||
|
envVars: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.envVars = dotenv.config().parsed;
|
||||||
|
this.app = express();
|
||||||
|
}
|
||||||
|
|
||||||
|
public start() {
|
||||||
|
this.setMiddlewares();
|
||||||
|
this.setRouters();
|
||||||
|
this.startServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setMiddlewares() {
|
||||||
|
this.app.use(express.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
private startServer() {
|
||||||
|
this.app.listen(this.envVars.SERVER_PORT, () => {
|
||||||
|
console.log(`Server is running on port ${this.envVars.SERVER_PORT}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setRouters() {
|
||||||
|
this.app.use(router);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App();
|
||||||
|
app.start();
|
12
api/src/middleware/register-middleware.ts
Normal file
12
api/src/middleware/register-middleware.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export const registerMiddleware = async (req, res, next) => {
|
||||||
|
const { key, name, email, phone } = req.body;
|
||||||
|
try {
|
||||||
|
if (key && name && email && phone) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next("key, name, email, and phone are required");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
12
api/src/middleware/verify-eligibility-middleware.ts
Normal file
12
api/src/middleware/verify-eligibility-middleware.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export const verifyEligibility = async (req, res, next) => {
|
||||||
|
const { key } = req.body;
|
||||||
|
try {
|
||||||
|
if (key) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
next("key is required");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
};
|
72
api/src/models/DarioRegisterModel.ts
Normal file
72
api/src/models/DarioRegisterModel.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import UserModel from '../db/mongodb/schema'; // Import the user schema/model
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { User } from '../common/types/User';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
const envVars = dotenv.config().parsed;
|
||||||
|
|
||||||
|
export class RegisterModel {
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
try {
|
||||||
|
await mongoose.connect(envVars.MONGO_CONNECTION_URL);
|
||||||
|
console.log('Connected to MongoDB');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error connecting to MongoDB:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
try {
|
||||||
|
await mongoose.disconnect();
|
||||||
|
console.log('Disconnected from MongoDB');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error disconnecting from MongoDB:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(userInput: User) {
|
||||||
|
try {
|
||||||
|
this.connect();
|
||||||
|
const user = new UserModel(userInput);
|
||||||
|
console.log(user)
|
||||||
|
return await user.save();
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Error creating user: ' + error);
|
||||||
|
} finally {
|
||||||
|
this.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByEmail(email: string) {
|
||||||
|
try {
|
||||||
|
return await UserModel.findOne({ Email: email });
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Error getting user by email: ' + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(email: string) {
|
||||||
|
try {
|
||||||
|
const user = await UserModel.findOne({ Email: email });
|
||||||
|
if (user) {
|
||||||
|
user.active = false;
|
||||||
|
return await user.save();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Error deleting user: ' + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateUpdatedAt(email: string) {
|
||||||
|
try {
|
||||||
|
const user = await UserModel.findOne({ Email: email });
|
||||||
|
if (user) {
|
||||||
|
user.updated_at = new Date();
|
||||||
|
return await user.save();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Error updating updated_at: ' + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
api/src/routers/DarioRegisterRouter.ts
Normal file
25
api/src/routers/DarioRegisterRouter.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import express, { Response, Request, NextFunction } from "express";
|
||||||
|
import { DarioRegisterController } from "../controllers/DarioRegisterController";
|
||||||
|
import { verifyEligibility } from "../middleware/verify-eligibility-middleware";
|
||||||
|
import { registerMiddleware } from "../middleware/register-middleware";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const controller = new DarioRegisterController();
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/verify-eligibility",
|
||||||
|
verifyEligibility,
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await controller.verifyEligibility(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/register",
|
||||||
|
registerMiddleware,
|
||||||
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
await controller.register(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export { router };
|
14
api/tsconfig.json
Normal file
14
api/tsconfig.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "ES2019",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
32
infra/docker/docker-compose.yaml
Normal file
32
infra/docker/docker-compose.yaml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
container_name: my-redis
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
|
mongodb:
|
||||||
|
image: arm64v8/mongo:4.0
|
||||||
|
container_name: my-mongo
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- mongodb_data_vol:/data/db
|
||||||
|
- ./mongo-init-scripts/init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
|
platform: linux/arm64/v8
|
||||||
|
environment:
|
||||||
|
- MONGO_INITDB_DATABASE=admin
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=root
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=root
|
||||||
|
expose:
|
||||||
|
- 27017
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis_data:
|
||||||
|
mongodb_data_vol:
|
12
infra/docker/mongo-init-scripts/init.js
Normal file
12
infra/docker/mongo-init-scripts/init.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
const username = 'example_user';
|
||||||
|
const password = 'example_password';
|
||||||
|
const dbName = 'example_db';
|
||||||
|
const userRole = 'readWrite';
|
||||||
|
|
||||||
|
db = db.getSiblingDB(dbName);
|
||||||
|
|
||||||
|
db.createUser({
|
||||||
|
user: username,
|
||||||
|
pwd: password,
|
||||||
|
roles: [{ role: userRole, db: dbName }],
|
||||||
|
});
|
5
inserter/.env.example
Normal file
5
inserter/.env.example
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
## Redis
|
||||||
|
REDIS_CONNECTION_URL="redis://localhost:6379"
|
||||||
|
|
||||||
|
## SQS
|
||||||
|
SQS_URL=""
|
47
inserter/README.md
Normal file
47
inserter/README.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
### Inserter
|
||||||
|
|
||||||
|
This is a program that inserts a data.json file into a redis database.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use this program, you must have a redis database running on your machine. You can download redis [here](https://redis.io/download).
|
||||||
|
|
||||||
|
To run the program, you must have a data.json file in the same directory as the inserter.py file. The data.json file must be in the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"user1@example.com": {
|
||||||
|
"Name": "John Doe",
|
||||||
|
"Email": "user1@example.com",
|
||||||
|
"Phone": "555-1234",
|
||||||
|
"EmployeeID": "EMP001"
|
||||||
|
},
|
||||||
|
"user2@example.com": {
|
||||||
|
"Name": "Jane Smith",
|
||||||
|
"Email": "user2@example.com",
|
||||||
|
"Phone": "444-5678",
|
||||||
|
"EmployeeID": "EMP002"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
## ENV
|
||||||
|
```
|
||||||
|
export REDIS_CONNECTION_URL=redis://localhost:6379
|
||||||
|
```
|
||||||
|
Or use the .env.example file
|
||||||
|
|
||||||
|
## Run
|
||||||
|
```
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
This program will remove all in redis and insert new data by the data.json file.
|
||||||
|
the users collection will be inserted in a transaction mode with the redis database, with expiration of 4 days.
|
||||||
|
|
||||||
|
|
122
inserter/data.json
Normal file
122
inserter/data.json
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
{
|
||||||
|
"user1@example.com": {
|
||||||
|
"Name": "John Doe",
|
||||||
|
"Email": "user1@example.com",
|
||||||
|
"Phone": "555-1234",
|
||||||
|
"EmployeeID": "EMP001"
|
||||||
|
},
|
||||||
|
"user2@example.com": {
|
||||||
|
"Name": "Jane Smith",
|
||||||
|
"Email": "user2@example.com",
|
||||||
|
"Phone": "444-5678",
|
||||||
|
"EmployeeID": "EMP002"
|
||||||
|
},
|
||||||
|
"user3@example.com": {
|
||||||
|
"Name": "Michael Johnson",
|
||||||
|
"Email": "user3@example.com",
|
||||||
|
"Phone": "777-9876",
|
||||||
|
"EmployeeID": "EMP003"
|
||||||
|
},
|
||||||
|
"user4@example.com": {
|
||||||
|
"Name": "Emily Wilson",
|
||||||
|
"Email": "user4@example.com",
|
||||||
|
"Phone": "999-5432",
|
||||||
|
"EmployeeID": "EMP004"
|
||||||
|
},
|
||||||
|
"user5@example.com": {
|
||||||
|
"Name": "David Lee",
|
||||||
|
"Email": "user5@example.com",
|
||||||
|
"Phone": "111-2222",
|
||||||
|
"EmployeeID": "EMP005"
|
||||||
|
},
|
||||||
|
"user6@example.com": {
|
||||||
|
"Name": "Olivia Martinez",
|
||||||
|
"Email": "user6@example.com",
|
||||||
|
"Phone": "888-7654",
|
||||||
|
"EmployeeID": "EMP006"
|
||||||
|
},
|
||||||
|
"user7@example.com": {
|
||||||
|
"Name": "William Taylor",
|
||||||
|
"Email": "user7@example.com",
|
||||||
|
"Phone": "777-8888",
|
||||||
|
"EmployeeID": "EMP007"
|
||||||
|
},
|
||||||
|
"user8@example.com": {
|
||||||
|
"Name": "Sophia Anderson",
|
||||||
|
"Email": "user8@example.com",
|
||||||
|
"Phone": "333-4444",
|
||||||
|
"EmployeeID": "EMP008"
|
||||||
|
},
|
||||||
|
"user9@example.com": {
|
||||||
|
"Name": "James Brown",
|
||||||
|
"Email": "user9@example.com",
|
||||||
|
"Phone": "222-1111",
|
||||||
|
"EmployeeID": "EMP009"
|
||||||
|
},
|
||||||
|
"user10@example.com": {
|
||||||
|
"Name": "Isabella Garcia",
|
||||||
|
"Email": "user10@example.com",
|
||||||
|
"Phone": "111-9999",
|
||||||
|
"EmployeeID": "EMP010"
|
||||||
|
},
|
||||||
|
"user11@example.com": {
|
||||||
|
"Name": "Ethan Clark",
|
||||||
|
"Email": "user11@example.com",
|
||||||
|
"Phone": "666-7777",
|
||||||
|
"EmployeeID": "EMP011"
|
||||||
|
},
|
||||||
|
"user12@example.com": {
|
||||||
|
"Name": "Ava Hernandez",
|
||||||
|
"Email": "user12@example.com",
|
||||||
|
"Phone": "777-4444",
|
||||||
|
"EmployeeID": "EMP012"
|
||||||
|
},
|
||||||
|
"user13@example.com": {
|
||||||
|
"Name": "Alexander Allen",
|
||||||
|
"Email": "user13@example.com",
|
||||||
|
"Phone": "999-2222",
|
||||||
|
"EmployeeID": "EMP013"
|
||||||
|
},
|
||||||
|
"user14@example.com": {
|
||||||
|
"Name": "Mia Turner",
|
||||||
|
"Email": "user14@example.com",
|
||||||
|
"Phone": "333-5555",
|
||||||
|
"EmployeeID": "EMP014"
|
||||||
|
},
|
||||||
|
"user15@example.com": {
|
||||||
|
"Name": "Benjamin Scott",
|
||||||
|
"Email": "user15@example.com",
|
||||||
|
"Phone": "222-7777",
|
||||||
|
"EmployeeID": "EMP015"
|
||||||
|
},
|
||||||
|
"user16@example.com": {
|
||||||
|
"Name": "Amelia Adams",
|
||||||
|
"Email": "user16@example.com",
|
||||||
|
"Phone": "777-3333",
|
||||||
|
"EmployeeID": "EMP016"
|
||||||
|
},
|
||||||
|
"user17@example.com": {
|
||||||
|
"Name": "Daniel Evans",
|
||||||
|
"Email": "user17@example.com",
|
||||||
|
"Phone": "444-1111",
|
||||||
|
"EmployeeID": "EMP017"
|
||||||
|
},
|
||||||
|
"user18@example.com": {
|
||||||
|
"Name": "Charlotte Stewart",
|
||||||
|
"Email": "user18@example.com",
|
||||||
|
"Phone": "888-6666",
|
||||||
|
"EmployeeID": "EMP018"
|
||||||
|
},
|
||||||
|
"user19@example.com": {
|
||||||
|
"Name": "Logan Hernandez",
|
||||||
|
"Email": "user19@example.com",
|
||||||
|
"Phone": "666-4444",
|
||||||
|
"EmployeeID": "EMP019"
|
||||||
|
},
|
||||||
|
"user20@example.com": {
|
||||||
|
"Name": "Scarlett Parker",
|
||||||
|
"Email": "user20@example.com",
|
||||||
|
"Phone": "222-8888",
|
||||||
|
"EmployeeID": "EMP020"
|
||||||
|
}
|
||||||
|
}
|
97
inserter/package-lock.json
generated
Normal file
97
inserter/package-lock.json
generated
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
{
|
||||||
|
"name": "inserter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "inserter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/redis": "^4.0.11",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"redis": "^3.1.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.4.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.8.tgz",
|
||||||
|
"integrity": "sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/redis": {
|
||||||
|
"version": "4.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-4.0.11.tgz",
|
||||||
|
"integrity": "sha512-bI+gth8La8Wg/QCR1+V1fhrL9+LZUSWfcqpOj2Kc80ZQ4ffbdL173vQd5wovmoV9i071FU9oP2g6etLuEwb6Rg==",
|
||||||
|
"deprecated": "This is a stub types definition. redis provides its own type definitions, so you do not need this installed.",
|
||||||
|
"dependencies": {
|
||||||
|
"redis": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/denque": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
|
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"denque": "^1.5.0",
|
||||||
|
"redis-commands": "^1.7.0",
|
||||||
|
"redis-errors": "^1.2.0",
|
||||||
|
"redis-parser": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/node-redis"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis-commands": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
|
||||||
|
},
|
||||||
|
"node_modules/redis-errors": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis-parser": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||||
|
"dependencies": {
|
||||||
|
"redis-errors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
inserter/package.json
Normal file
19
inserter/package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "inserter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon dist/index.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.4.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/redis": "^4.0.11",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"redis": "^3.1.2"
|
||||||
|
}
|
||||||
|
}
|
7
inserter/src/common/types/User.ts
Normal file
7
inserter/src/common/types/User.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export interface User {
|
||||||
|
Name: string;
|
||||||
|
Email: string;
|
||||||
|
Phone: string;
|
||||||
|
EmployeeID: string;
|
||||||
|
SecureKey?: string;
|
||||||
|
}
|
37
inserter/src/index.ts
Normal file
37
inserter/src/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Redis } from "./redis/Redis";
|
||||||
|
import fs from "fs";
|
||||||
|
import { User } from "./common/types/User";
|
||||||
|
|
||||||
|
class Inserter {
|
||||||
|
redis: Redis;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.log("Inserter class is created");
|
||||||
|
this.redis = new Redis();
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectToRedis() {
|
||||||
|
await this.redis.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
try {
|
||||||
|
await this.connectToRedis();
|
||||||
|
const data = await this.readFile("./data.json");
|
||||||
|
await this.redis.transactionToRedis(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
} finally {
|
||||||
|
this.redis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFile(filePath: string): Promise<User[]> {
|
||||||
|
const data = fs.readFileSync(filePath, "utf8");
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const inserter = new Inserter();
|
||||||
|
inserter.start();
|
78
inserter/src/redis/Redis.ts
Normal file
78
inserter/src/redis/Redis.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import * as redis from "redis";
|
||||||
|
import { User } from "../common/types/User";
|
||||||
|
import { randomUUID } from "crypto";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
|
||||||
|
class Redis {
|
||||||
|
client: any;
|
||||||
|
envVars: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.envVars = dotenv.config().parsed;
|
||||||
|
console.log("Redis class is created");
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(): Promise<void> {
|
||||||
|
this.client = redis.createClient({ url: this.envVars.REDIS_CONNECTION_URL });
|
||||||
|
this.client.on("connect", () => {
|
||||||
|
console.log("Connected to Redis");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.on("error", (err) => {
|
||||||
|
console.log("Error connecting to Redis:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async transactionToRedis(data: User[]): Promise<void> {
|
||||||
|
const EXPIRATION_TIME = 60 * 60 * 24 * 4;
|
||||||
|
|
||||||
|
const multi = this.client.multi();
|
||||||
|
multi.flushall();
|
||||||
|
|
||||||
|
for (const user in data) {
|
||||||
|
if (!data[user].EmployeeID || !data[user].Email || !data[user].Name || !data[user].Phone) {
|
||||||
|
this.rollback("Invalid user data", user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[user].SecureKey = this.randomUUID()
|
||||||
|
data[user].Email = this.encrypt(data[user].Email)
|
||||||
|
multi.expire("users", EXPIRATION_TIME);
|
||||||
|
multi.hset("users", data[user].SecureKey, JSON.stringify(data[user]));
|
||||||
|
};
|
||||||
|
|
||||||
|
multi.exec(async (err: any, replies: any) => {
|
||||||
|
if (err) {
|
||||||
|
console.log("Error inserting data in Redis:", err.message);
|
||||||
|
} else {
|
||||||
|
console.log("Data inserted in Redis:", replies);
|
||||||
|
await this.pushToSqs(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback(reason: string, session: any, data = null): void {
|
||||||
|
console.error(reason, data);
|
||||||
|
session.discard();
|
||||||
|
}
|
||||||
|
|
||||||
|
randomUUID(): string {
|
||||||
|
return randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
private encrypt(email: string): string {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
async close(): Promise<void> {
|
||||||
|
await new Promise((resolve) => this.client.quit(resolve));
|
||||||
|
console.log("Disconnected from Redis");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async pushToSqs(data: User[]): Promise<void> {
|
||||||
|
console.log("Pushing to SQS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Redis };
|
14
inserter/tsconfig.json
Normal file
14
inserter/tsconfig.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "ES2019",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue