This commit is contained in:
mark H 2024-08-27 08:37:01 +08:00
commit f06bbd17ef
16 changed files with 5586 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.env
/node_modules

134
controllers/auth.js Normal file
View File

@ -0,0 +1,134 @@
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import User from "../models/User.js";
import cloudinary from "cloudinary";
import streamifier from "streamifier";
import DatauriParser from "datauri/parser.js";
import path from "path";
const parser = new DatauriParser();
/* REGISTER USER */
export const register = async (req, res) => {
try {
const {
fName,
lName,
email,
password,
phone,
address1,
address2,
city,
province,
country,
zip,
username,
type,
status,
transactions,
} = req.body;
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
const salt = await bcrypt.genSalt();
const passwordHash = await bcrypt.hash(password, salt);
const emailTaken = await User.findOne({ email: email });
const usernameTaken = await User.findOne({ username: username });
// const phoneTaken = await User.findOne({ phone: phone });
const pic = null;
if (req.file) {
const extName = path.extname(req.file.originalname).toString();
const file64 = parser.format(extName, req.file.buffer);
pic = await cloudinary.uploader.upload(file64.content, {
folder: "obPayUserPhoto",
transformation: [{ width: 500, height: 500, crop: "limit" }],
});
}
if (!emailTaken && !usernameTaken) {
const newUser = new User({
fName,
lName,
email,
password: passwordHash,
phone,
address1,
address2,
city,
province,
country,
zip,
username,
type,
status,
transactions,
photo: pic ? pic : "",
});
const usersaved = await newUser.save();
const user = await User.findOne({ email: email });
res.status(201).json({ usersaved, user });
} else if (emailTaken) {
res.status(400).json({ error: "email taken" });
} else if (usernameTaken) {
res.status(404).json({ error: "username taken" });
}
} catch (err) {
res.status(500).json({ error: err.message });
console.log(err.message);
}
};
/* LOGGING IN */
// export const login = async (req, res) => {
// try {
// const { users, password } = req.body;
// // const user = await User.findOne({ email: users });
// const user = await User.findOne().or([{ email: users }, { phone: users }]);
// if (user) {
// const isMatch = await bcrypt.compare(password, user.password);
// if (isMatch) {
// const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
// delete user.password;
// res.status(200).json({token, user});
// } else {
// return res.status(400).json({ msg: "Invalid credentials. " });
// }
// } else if (!user)
// console.log(users,password)
// return res.status(404).json({ msg: "User does not exist. " });
// } catch (err) {
// res.status(500).json({ error: err.message });
export const login = async (req, res) => {
try {
const { users, password } = req.body;
const user = await User.findOne().or([
{ email: users },
{ phone: users },
{ username: users },
]);
if (!user) {
console.log("does not exist");
return res.status(404).json({ msg: "User does not exist. " });
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: "Invalid credentials. " });
}
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
delete user.password;
res.status(200).json({ token, user });
} catch (err) {
res.status(500).json({ error: err.message });
}
};

95
controllers/collection.js Normal file
View File

@ -0,0 +1,95 @@
import Collection from "../models/Collection.js";
export const createCollection = async (req, res, next) => {
const newCollection = new Collection({
full_name: req.body.full_name,
date_of_birth: req.body.date_of_birth,
userRef: req.body.userRef,
id_num: req.body.id_num,
address: req.body.address,
images: req.body.images,
selfie: req.body.selfie,
status: req.body.status,
});
console.log(req.body);
try {
const savedCollection = await newCollection.save();
res.status(200).json(savedCollection);
console.log(savedCollection);
} catch (err) {
// console.log(req.body);
res.status(400).json({ message: err.message });
next(err);
}
};
export const updateCollection = async (req, res, next) => {
try {
console.log(req.params.id);
const updatedCollection = await Collection.findByIdAndUpdate(
req.params.id,
{ $set: req.body },
{ new: true }
);
if (updatedCollection) {
res.status(200).json(updatedCollection);
} else {
res.status(200).json("Collection does not exist.");
}
} catch (err) {
next(err);
}
};
export const deleteCollection = async (req, res, next) => {
try {
const getCollection = await Collection.findByIdAndDelete(req.params.id);
if (getCollection) {
res.status(200).json("Collection has been deleted.");
} else {
res.status(404).json("Collection does not exist.");
}
} catch (err) {
res.status(404).json({ message: err.message });
next(err);
}
};
export const getCollections = async (req, res, next) => {
try {
const getCollection = await Collection.find();
// const Collection = getCollection.reverse();
if (getCollection) {
res.status(200).json(getCollection);
} else {
res.status(404).json("Collection does not exist.");
}
} catch (err) {
res.status(404).json({ message: err.message });
next(err);
}
};
export const getCollection = async (req, res, next) => {
try {
const foundCollection = await Collection.findById(req.params.id)
.populate({
path: "userRef",
model: "User",
// select: "fName lName username photo",
})
.exec();
if (foundCollection) {
res.status(200).json(foundCollection);
} else {
res.status(404).json("Collection does not exist.");
}
} catch (err) {
res.status(500).json({ message: err.message });
next(err);
}
};

385
controllers/users.js Normal file
View File

@ -0,0 +1,385 @@
import User from "../models/User.js";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import cloudinary from "cloudinary";
import multer from "multer";
import streamifier from "streamifier";
import DatauriParser from "datauri/parser.js";
import path from "path";
import axios from "axios";
/* READ */
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
// Configure Multer middleware to handle file uploads
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 10 * 1024 * 1024, // 10 MB
},
});
export const getUsers = async (req, res, next) => {
try {
const users = await User.find();
res.status(200).json(users);
} catch (err) {
res.status(404).json({ message: err.message });
next(err);
}
};
export const getUser = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findOne().or([
{ email: id },
{ phone: id },
{ username: id },
]);
if (user) {
res.status(200).json(user);
} else {
res.status(404).json({ message: "user not found" });
}
} catch (err) {
res.status(400).json({ message: err.message });
}
};
export const getUserId = async (req, res) => {
try {
const { id } = req.params;
const user = await User.findById(id);
if (user) {
res.status(200).json(user);
} else {
res.status(404).json({ message: "user not found" });
}
} catch (err) {
res.status(404).json({ message: err.message });
}
};
export const updateUser = async (req, res, next) => {
try {
const {
fName,
lName,
email,
phone,
address1,
address2,
city,
province,
country,
zip,
type,
status,
transactions,
} = req.body;
const emailTaken = await User.findOne({ email: email });
const phoneTaken = await User.findOne({ phone: phone });
let taken = false;
if (phoneTaken._id.toString().includes(req.params.id)) {
taken = false;
} else {
taken = true;
}
console.log(taken);
if (req.file) {
const extName = path.extname(req.file.originalname).toString();
const file64 = parser.format(extName, req.file.buffer);
const result = await cloudinary.uploader.upload(file64.content, {
folder: "obPayUserPhoto",
transformation: [{ width: 500, height: 500, crop: "limit" }],
});
if (!emailTaken && !taken) {
const newUser = {
fName,
lName,
email,
phone,
address1,
address2,
city,
province,
country,
zip,
type,
status,
transactions,
photo: result.secure_url,
};
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{ $set: newUser },
{ new: true }
);
res.status(200).json(updatedUser);
} else if (emailTaken) {
res.status(500).json({ error: "email taken" });
} else if (taken) {
res.status(500).json({ error: "phone taken" });
}
} else if (!req.file) {
if (!emailTaken && !taken) {
const newUser = {
fName,
lName,
email,
phone,
address1,
address2,
city,
province,
country,
zip,
transactions,
status,
};
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{ $set: newUser },
{ new: true }
);
res.status(200).json(updatedUser);
} else if (emailTaken) {
res.status(500).json({ error: "email taken" });
} else if (taken) {
const newUser = {
fName,
lName,
email,
phone,
address1,
address2,
city,
province,
country,
zip,
transactions,
status,
};
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{ $set: newUser },
{ new: true }
);
res.status(500).json({ error: updatedUser });
}
}
} catch (err) {
res.status(404).json({ message: err.message });
next(err);
}
};
export const updateUserSub = async (req, res, next) => {
try {
console.log(req.params.id);
const emailTaken = await User.findOne({ email: req.body.email });
const usernameTaken = await User.findOne({ username: req.body.username });
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{ $set: req.body },
{ new: true }
);
if (updatedUser) {
res.status(200).json(updatedUser);
} else {
res.status(400).json("User does not exist.");
}
} catch (err) {
next(err);
res.status(400).json(err);
console.log(err);
}
};
export const updateUserPass = async (req, res, next) => {
try {
console.log(req.params.id);
if (req.body.password) {
const salt = await bcrypt.genSalt();
const passwordHash = await bcrypt.hash(req.body.password, salt);
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{ $set: { password: passwordHash } },
{ new: true }
);
if (updatedUser) {
res.status(200).json(updatedUser);
} else {
res.status(200).json("User does not exist.");
}
}
} catch (err) {
next(err);
}
};
export const updateUserPhoto = async (req, res, next) => {
try {
console.log(req.params.id);
if (req.file) {
const extName = path.extname(req.file.originalname).toString();
const file64 = parser.format(extName, req.file.buffer);
const result = await cloudinary.uploader.upload(file64.content, {
folder: "obPayUserPhoto",
transformation: [{ width: 500, height: 500, crop: "limit" }],
});
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{ $set: { photo: result } },
{ new: true }
);
if (updatedUser) {
res.status(200).json(updatedUser);
} else {
res.status(200).json("User does not exist.");
}
}
} catch (err) {
next(err);
res.status(400).json(err);
}
};
export const updateUserFriends = async (req, res, next) => {
try {
console.log(req.params.id);
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json("User does not exist.");
}
const { number, favorite, nickname } = req.body;
const newFriend = { number, favorite, nickname };
user.friends.push(newFriend);
const updatedUser = await user.save();
res.status(200).json(updatedUser);
} catch (err) {
next(err);
}
};
export const updateUserFriendData = async (req, res, next) => {
try {
const userId = req.params.id;
const friendId = req.params.friendId;
const { number, favorite, nickname } = req.body;
const updatedUser = await User.findOneAndUpdate(
{ _id: userId, "friends._id": friendId },
{
$set: {
"friends.$.number": number,
"friends.$.favorite": favorite,
"friends.$.nickname": nickname,
},
},
{ new: true }
);
if (updatedUser) {
res.status(200).json(updatedUser);
} else {
res.status(404).json("User or friend not found.");
console.log("heyy" + friendId + " " + userId);
}
} catch (err) {
next(err);
}
};
export const deleteUserFriend = async (req, res, next) => {
try {
const userId = req.params.id;
const friendId = req.params.friendId;
const updatedUser = await User.findOneAndUpdate(
{ _id: userId },
{ $pull: { friends: { _id: friendId } } },
{ new: true }
);
if (updatedUser) {
res.status(200).json(updatedUser);
} else {
res.status(404).json("User or friend not found.");
}
} catch (err) {
next(err);
}
};
export const deleteUser = async (req, res, next) => {
try {
await User.findByIdAndDelete(req.params.id);
res.status(200).json("User has been deleted.");
} catch (err) {
res.status(404).json({ message: err.message });
next(err);
}
};
export const sendOtpEmail = async (req, res) => {
const { username } = req.body;
try {
const user = await User.findOne({ email: username });
if (!user) {
return res.status(404).json({ message: "User not found" });
}
// Generate OTP (simplified for demonstration)
const otp = Math.floor(100000 + Math.random() * 900000);
const otpExpiration = new Date(Date.now() + 600000); // OTP expires in 10 minutes
const updated = await User.findOneAndUpdate(
{ email: username },
{ otp, otpExpiration }
);
const requestData = {
email: username,
html: "<p>Your One-Time Pin (OTP) is: <strong>" + otp + "</strong></p>",
};
const responseFromExternalAPI = await axios.post(
"http://192.168.50.15:4038/api/send-email",
requestData
);
// console.log(responseFromExternalAPI);
// Process the response from the external API
const responseData = responseFromExternalAPI.data;
return res.json({ message: responseData });
} catch (error) {
console.error(error);
return res.status(500).json({ message: "Internal Server Error" });
}
};
export const confirmOtp = async (req, res) => {
const { username, otp } = req.body;
// Verify OTP and its expiration time
const user = await User.findOne({
email: username,
otp,
otpExpiration: { $gt: Date.now() },
});
if (!user) {
return res.status(400).send("Invalid OTP or expired");
}
user.otp = null;
user.otpExpiration = null;
await user.save();
return res.status(200).json({ message: "OTP success" });
};

155
index.js Normal file
View File

@ -0,0 +1,155 @@
import express from "express";
import bodyParser from "body-parser";
import mongoose from "mongoose";
import cors from "cors";
import dotenv from "dotenv";
import multer from "multer";
import helmet from "helmet";
import morgan from "morgan";
import path from "path";
import { fileURLToPath } from "url";
import authRoutes from "./routes/auth.js";
import userRoutes from "./routes/users.js";
import transactionRoutes from "./routes/collection.js";
// import webemailRoutes from "./routes/webemail.js";
import axios from "axios";
import WebSocket, { WebSocketServer } from "ws";
// import nodemailer from "nodemailer";
// export const ws = new WebSocket("ws://192.168.50.15:4028/sT1eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2NDE3OTI4MmE3M2ZlZWQ3MjgyM2ViOCIsImlhdCI6MTcxNTY2ODI4Nn0.ziEOLreXbCJRlyjRyIVLDsJNpeIvk73rf3kU7_HtO8E"
// );
// ws.onopen = () => {
// console.log("WebSocket connected------------------------------------------");
// // You can send initial messages after the connection is established if needed
// // ws.send("Hello, server!");
// // const userId = "meeeee2";
// ws.send(JSON.stringify({ type: "join", userId: "obnPay_test" }));
// // ws.send(encryptedMessage2);
// };
// ws.onclose = () => {
// console.log("Connection closed");
// };
/* CONFIGURATIONS */
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config();
const app = express();
app.use(express.json());
app.use(helmet());
app.use(helmet.crossOriginResourcePolicy({ policy: "cross-origin" }));
app.use(morgan("common"));
app.use(bodyParser.json({ limit: "30mb", extended: true }));
app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));
app.use(cors());
app.use("/assets", express.static(path.join(__dirname, "upload_files")));
/* FILE STORAGE */
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const category = req.body.category;
// if (category == "message") {
// cb(null, "message_uploads");
// } else if (category == "user") {
// cb(null, "user_uploads");
// } else {
cb(null, "upload_files");
// }
},
filename: (req, file, cb) => {
const image_id = req.body.image_id;
cb(null, image_id + "-" + file.originalname);
},
});
const upload = multer({ storage });
/* ROUTES */
app.use("/api/auth", authRoutes);
app.use("/api/users", userRoutes);
app.use("/api/transactions", transactionRoutes);
// app.use("/api/web-emails", webemailRoutes);
app.post("/api/upload_images", upload.single("image"), async (req, res) => {
try {
const { filename } = req.file;
const { image_id, category } = req.body;
res.status(201).send({ filename, category, image_id });
} catch (error) {
res.status(400).send(error);
}
});
function sendEmail(req, res, next) {
const apiKey =
"ODA4MDc4ZThjMDA4NjVhYzU4MTcyNDJjNTMxY2JlZGU6MGQ4ODg3ZTdiZjY1ZWNkMmQ0NzdiOWJhZGIyYTJhY2Q="; // Replace with your Mailjet API key
const apiUrl = "https://api.mailjet.com/v3.1/send";
// const otp = generateOTP(6); // You should have a function to generate the OTP
const email2 = "kramblooda@gmail.com";
const min = 100000; // Minimum 6-digit number
const max = 999999; // Maximum 6-digit number
const randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
const requestData = {
Messages: [
{
From: {
Email: "webdev@obanana.com",
Name: "Obanana B2B",
},
To: [
{
Email: req.body.email,
Name: "Subscriber",
},
],
Subject: "Obanana OTP",
TextPart: "Greetings from Obanana!",
HTMLPart: req.body.html,
},
],
};
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${apiKey}`,
},
};
axios
.post(apiUrl, requestData, config)
.then((response) => {
// const status = response.data.Messages[0].Status;
// console.log(response.data.Messages[0].Status);
// console.log(randomNumber);
// setotpSent(randomNumber);
res.status(200).json(response.data);
// return `${status},${randomNumber}`;
})
.catch((error) => {
res.status(404).json({ message: error });
// console.error("Error sending OTP email:", error);
// Handle the error here
});
}
app.post("/api/send-email/", sendEmail);
/* MONGOOSE SETUP */
const PORT = process.env.PORT || 3000;
mongoose
.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
app.listen(PORT, () => console.log(`Server Port: ${PORT}`));
// /* ADD DATA ONE TIME */
})
.catch((error) => console.log(`${error} did not connect`));

21
middleware/auth.js Normal file
View File

@ -0,0 +1,21 @@
import jwt from "jsonwebtoken";
export const verifyToken = async (req, res, next) => {
try {
let token = req.header("Authorization");
if (!token) {
return res.status(403).send();
}
if (token.startsWith("Bearer ")) {
token = token.slice(7, token.length).trimLeft();
}
const verified = jwt.verify(token, process.env.JWT_SECRET);
req.user = verified;
next();
} catch (err) {
res.status(500).json({ error: err.message });
}
};

28
middleware/log.js Normal file
View File

@ -0,0 +1,28 @@
import Log from "../models/Log.js";
const logApiCall = async (req, res, next) => {
try {
// Assuming verifyToken middleware attaches the user info to req.user
const userId = req.user.id;
// Create a log entry
const logEntry = new Log({
userId: userId,
method: req.method,
endpoint: req.originalUrl,
body: req.body,
});
// Save the log entry to the database
await logEntry.save();
// Proceed to the next middleware or route handler
next();
} catch (error) {
console.error("Error logging API call:", error);
// Optionally, you can handle the error here (e.g., send an error response)
next(error); // Pass the error to the error-handling middleware
}
};
export default logApiCall;

52
models/Collection.js Normal file
View File

@ -0,0 +1,52 @@
import mongoose from "mongoose";
const CollectionSchema = mongoose.Schema(
{
full_name: {
type: String,
// required: true,
},
date_of_birth: {
type: String,
// required: true,
},
userRef: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
id_num: {
type: String,
// required: true,
},
address: {
type: String,
// required: true,
},
images: [
{
link: {
type: String,
},
//required: true,
},
],
selfie: {
type: String,
//required: true,
},
reason: {
type: String,
//required: true,
},
status: {
type: String,
default: "new",
// required: true,
},
},
{ timestamps: true }
);
const Collection = mongoose.model("Collection", CollectionSchema);
export default Collection;

20
models/Log.js Normal file
View File

@ -0,0 +1,20 @@
import mongoose from "mongoose";
const LogSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
method: { type: String, required: true },
body: [],
endpoint: { type: String, required: true },
timestamp: { type: Date, default: Date.now },
},
{ timestamps: true }
);
const Log = mongoose.model("Log", LogSchema);
export default Log;

124
models/User.js Normal file
View File

@ -0,0 +1,124 @@
import mongoose from "mongoose";
const UserSchema = new mongoose.Schema(
{
fName: {
type: String,
// required: true,
min: 2,
max: 50,
},
lName: {
type: String,
// required: true,
min: 2,
max: 50,
},
email: {
type: String,
required: true,
max: 50,
unique: true,
},
username: {
type: String,
// required: true,
max: 30,
unique: true,
},
password: {
type: String,
required: true,
min: 5,
},
phone: {
type: String,
// required: true,
default: "",
},
photo: {
type: String,
},
address1: {
type: String,
default: "",
},
address2: {
type: String,
default: "",
},
city: {
type: String,
default: "",
},
province: {
type: String,
default: "",
},
country: {
type: String,
default: "",
},
zip: {
type: String,
default: "",
},
type: {
type: String,
default: "client",
},
status: {
type: String,
default: "new",
required: true,
},
phoneStatus: {
type: String,
default: "new",
// required: true,
},
kycStatus: {
type: String,
default: "new",
// required: true,
},
kycRef: {
type: mongoose.Schema.Types.ObjectId,
ref: "Kyc",
},
friends: [
{
number: {
type: String,
required: false,
},
favorite: {
type: Boolean,
required: false,
default: false,
},
nickname: {
type: String,
required: false,
},
},
],
otp: {
type: String,
default: null,
},
otpExpiration: {
type: Date,
default: null,
},
// transactions: {
// type: Array,
// default: [],
// },
},
{ timestamps: true }
);
const User = mongoose.model("User", UserSchema);
export default User;

3867
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "obanana_postman_node",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.7.4",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cloudinary": "^2.4.0",
"cors": "^2.8.5",
"datauri": "^4.1.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.5.3",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"nodemon": "^3.1.4",
"streamifier": "^0.1.1",
"ws": "^8.18.0"
}
}

587
readme.md Normal file
View File

@ -0,0 +1,587 @@
# OBANANA WALLET API
This API allows you to:
### USER:
- AUTHENTICATE via LOGIN
- REGISTER A USER
- GET A USER
- GET ALL USERS
- UPDATE A USER
- UPDATE USER DETAILS WITH ANY PARAM EXCEPT PHOTO AND PASSWORD
- UPDATE USER PASSWORD
- UPDATE USER PHOTO
- ADD FRIEND TO USER
- UPDATE A FRIEND DETAILS
- DELETE A USER
- DELETE A FRIEND
### KYCS
- CREATE A KYC
- GET ALL KYCS
- GET A SINGLE KYC
- DELETE A KYC
- UPDATE KYC
### EMAIL
- SEND EMAIL
### UPLOAD IMAGE
- UPLOAD AN IMAGE
The API is available at `http://obpay.online`
## Endpoints
## API Authentication
### LOGIN
POST `/api/auth/login`
The request body needs to be in JSON format and include the following properties:
- `email` - String || `phone` - String
- `password` - String
Example
```
{
"users": "meme1@gmail.com" or "09123456789",
"password": "123456"
}
```
The response body will contain the access token. The access token is valid for 7 days.
### REGISTER
POST `/api/auth/register`
The request body needs to be in JSON format and include the following properties:
- `fName` - String - Required
- `lName` - String - Required
- `email` - String - Required
- `password` - String - Required
- `phone` - String - Required
- `photo` - String --NEW--
- `address1` - String
- `address2` - String
- `city` - String
- `province` - String
- `country` - String
- `zip` - String
- `type` - String - Default client
Automatically generating ID
The photo param is `picture`
send the photo on the input type file and name is picture
Example
```
POST /api/auth/register
{
"fName":"Sana",
"lName":"Di",
"email":"meme1@gmail.com",
"password":"123456",
"phone":"123456",
"address1":"Blk 123 Makati City",
"address2":"Blk 123 Makati City",
"city":"Makati",
"province":"Metro Manila",
"country":"Philippines",
"zip":"1300",
"type":"Admin",
}
```
## Access Users Data
### List of all Users
GET `/api/users/`
Authorization: Bearer <YOUR TOKEN>
Returns detailed list of All Users.
### Get a single User by PHONE and EMAIL
GET `/api/users/:id`
Retrieve detailed information about a User.
Example
```
GET /api/users/615a9f6d236abe2dbb2e424c
Authorization: Bearer <YOUR TOKEN>
Output:
{
"user": {
"_id": "642a1b830ec9ced62dfbc778",
"fName": "Sana",
"lName": "Di",
"email": "meme1@gmail.com",
"password": "$2b$10$al2mB9HCdwaUEuK5ODfVqe/IpsAq8tfT4xDLxCduSkpD7dM8KvWjW",
"phone": "09112344556",
"photo": "",
"address1": "",
"address2": "",
"city": "",
"province": "",
"country": "",
"zip": "",
"type": "client",
"createdAt": "2023-04-03T00:19:15.051Z",
"updatedAt": "2023-04-03T00:19:15.051Z",
"__v": 0
},
"balance": 0
------- The User balance is automatically retireved by calculating the transaction involving the user by comparing the userID with the to_id and from_id from the Transaction then after sum when the user is the receiver and deduct when the user is the sender
}
```
### Get a single User by ID
GET `/api/users/id/:id`
Retrieve detailed information about a User.
Example
```
GET /api/users/615a9f6d236abe2dbb2e424c
Authorization: Bearer <YOUR TOKEN>
Output:
{
"user": {
"_id": "642a1b830ec9ced62dfbc778",
"fName": "Sana",
"lName": "Di",
"email": "meme1@gmail.com",
"password": "$2b$10$al2mB9HCdwaUEuK5ODfVqe/IpsAq8tfT4xDLxCduSkpD7dM8KvWjW",
"phone": "09112344556",
"photo": "",
"address1": "",
"address2": "",
"city": "",
"province": "",
"country": "",
"zip": "",
"type": "client",
"createdAt": "2023-04-03T00:19:15.051Z",
"updatedAt": "2023-04-03T00:19:15.051Z",
"__v": 0
},
"balance": 0
------- The User balance is automatically retireved by calculating the transaction involving the user by comparing the userID with the to_id and from_id from the Transaction then after sum when the user is the receiver and deduct when the user is the sender
}
```
### Update a user
PATCH `/api/users/:id`
Update an existing User. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
The photo param is `picture`
send the photo on the input type file and name is picture
- `fName` - String - Required
- `lName` - String - Required
- `email` - String - Required
- `password` - String - Required
- `phone` - String - Required
- `photo` - String --NEW--
- `address1` - String
- `address2` - String
- `city` - String
- `province` - String
- `country` - String
- `zip` - String
- `type` - String - Default client
- `status` String -Default new
- `kycStatus` String -Default new
- `kycRef` String - ID of the kyc of the user that was validated
Example
```
PATCH /api/users/615a9f6d236abe2dbb2e424c
Authorization: Bearer <YOUR TOKEN>
{
"fName":"SanaNew",
"lName":"Di",
"email":"meme1@gmail.com",
"password":"123456",
"phone":"123456",
"address1":"Blk 123 Makati City",
"address2":"Blk 123 Makati City",
"city":"Makati",
"province":"Metro Manila",
"country":"Philippines",
"zip":"1300",
"type":"Admin",
}
```
### Update a user with any parameters
PATCH `/api/users/:id/sub`
You can update the user details with only few or selected parameters except photo and password. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
- `fName` - String - OPTIONAL
- `lName` - String - OPTIONAL
- `email` - String - OPTIONAL
- `phone` - String - OPTIONAL
- `address1` - String - OPTIONAL
- `address2` - String - OPTIONAL
- `city` - String - OPTIONAL
- `province` - String - OPTIONAL
- `country` - String - OPTIONAL
- `zip` - String - OPTIONAL
- `type` - String - Default client - OPTIONAL
- `status` String - OPTIONAL
- `kycStatus` String -Default new
- `kycRef` String - ID of the kyc of the user that was validated
Example
```
PATCH /api/users/615a9f6d236abe2dbb2e424c
Authorization: Bearer <YOUR TOKEN>
{
"fName":"SanaNew1",
"lName":"Di",
}
```
### Update a user Password
PATCH `/api/users/:id/password`
Update the password of an existing User. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
automatically hash the passwords
- `password` - String - Required
Example
```
PATCH /api/users/615a9f6d236abe2dbb2e424c/password
Authorization: Bearer <YOUR TOKEN>
{
"password":"123456",
}
```
### Update a user PHOTO
PATCH `/api/users/:id/photo`
Update the photo of an existing User. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
The photo param is `picture`
send the photo on the input type file and name is picture
- `photo` - String Required
Example
```
PATCH /api/users/615a9f6d236abe2dbb2e424c/photo
Authorization: Bearer <YOUR TOKEN>
{
"photo":"image link",
}
```
### Delete User
DELETE `/api/users/:id`
Delete a user by id. Requires authentication.
The request parameter requires:
-`id` - String - Required. The id of the user to be deleted.
Example
```
DELETE /api/users/615a9f6d236abe2dbb2e424c
Authorization: Bearer <YOUR TOKEN>
```
### Add Friends to User
POST /api/users/:id/friends
Add friends to a user by their ID. Requires authentication.
The request body needs to be in JSON format and allows you to add the following properties:
- `number` - String - Required
- `favorite` - Boolean
- `nickname` - String - Required
Example:
```
POST /api/users/6450c39d34c51843e1dd6852/friends
Authorization: Bearer <YOUR TOKEN>
{
"number": "09567890544",
"favorite": true,
"nickname": "Buddy"
}
```
### Update Friend of User
PATCH /api/users/:id/friends/:friendId
Updates a specific friend of a user by their ID. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
- `number` - String - Required
- `favorite` - Boolean
- `nickname` - String - Required
Example:
```
PATCH /api/users/6450c39d34c51843e1dd6852/friends/6450c39d34c51843e1dd6852
Authorization: Bearer <YOUR TOKEN>
{
"number": "09567890544",
"favorite": false,
"nickname": "Pal"
}
```
### Delete Friend of User
DELETE /api/users/:id/friends/:friendId
Deletes a specific friend of a user by their ID. Requires authentication.
Example:
```
DELETE /api/users/6450c39d34c51843e1dd6852/friends/6450c39d34c51843e1dd6852
Authorization: Bearer <YOUR TOKEN>
```
## Access Kyc Data
### List of all Kycs
GET `/api/kycs/`
Authorization: Bearer <YOUR TOKEN>
Returns detailed list of All Kyc.
### Get a single Kyc
GET `/api/kycs/:id`
Retrieve detailed information about a single Kyc base on the `id`.
Example
```
GET /api/kycs/6424d962fcb1e4f824546c89
Authorization: Bearer <YOUR TOKEN>
{
"_id": "65d7fce8637f8e8fd8e84124",
"full_name": "San Man",
"date_of_birth": "Feb 23, 2001",
"userRef": "651b7e256faf1180905d613c",
"id_num": "01-234-567",
"address": "San Man st. Makati City",
"images": [
{
"link": "https://testapi.obpay.online/assets/651b7e256faf1180905d613c-729660ff-2703-46ee-b823-3d2cedd61f90.png",
"_id": "65d7fce8637f8e8fd8e84125"
},
{
"link": "https://testapi.obpay.online/assets/651b7e256faf1180905d613c-8e91f5bc-75c2-4974-b329-d106df3aa17f.png",
"_id": "65d7fce8637f8e8fd8e84126"
}
],
"selfie": "https://testapi.obpay.online/assets/651b7e256faf1180905d613c-53cbf0fe-28fe-4f06-ad7f-0640b6db5f98.jpeg",
"status": "pending",
"createdAt": "2024-02-23T02:03:20.503Z",
"updatedAt": "2024-02-23T02:03:20.503Z",
"__v": 0
}
```
### Create a Kyc
POST `/api/kycs/create`
Create a Kyc. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
- `full_name` - String
- `date_of_birth` - String
- `userRef` - String
- `id_num` - String
- `address` - Number
- `images` - String
- `selfie` - String
- `status` - String
Example
```
Post /api/kycs/create
Authorization: Bearer <YOUR TOKEN>
{
"full_name": "San Man",
"date_of_birth": "Feb 23, 2001",
"userRef": "651b7e256faf1180905d613c",
"id_num": "01-234-567",
"address": "San Man st. Makati City",
"images": [
{
"link": "https://testapi.obpay.online/assets/651b7e256faf1180905d613c-729660ff-2703-46ee-b823-3d2cedd61f90.png",
"_id": "65d7fce8637f8e8fd8e84125"
},
{
"link": "https://testapi.obpay.online/assets/651b7e256faf1180905d613c-8e91f5bc-75c2-4974-b329-d106df3aa17f.png",
"_id": "65d7fce8637f8e8fd8e84126"
}
],
"selfie": "https://testapi.obpay.online/assets/651b7e256faf1180905d613c-53cbf0fe-28fe-4f06-ad7f-0640b6db5f98.jpeg",
"status": "pending"
}
```
### Update a Kyc
POST `/api/kycs/update/:id`
Update a Notification. Requires authentication.
The request body needs to be in JSON format and allows you to update the following properties:
- `status` - String
Example
```
Post /api/kycs/update/6424d962fcb1e4f824546c89
Authorization: Bearer <YOUR TOKEN>
{
"status":"validated",
}
```
### Delete a Kyc
DELETE `/api/kycs/:id/delete`
Delete an existing Kyc. Requires authentication.
The request body needs to be empty.
Example
```
DELETE /api/kycs/6424d962fcb1e4f824546c89/delete
Authorization: Bearer <YOUR TOKEN>
```
## Send Email
POST `/api/send-email/`
Sending Email.
The request body needs to be in JSON format and allows you to send the following properties:
- `email` - String - Required ---Receiver of the email
- `html` - String - Required
- `subject` - String - Required
Example
```
Post /api/send-email/
{
"email":"hello@example.com",
"subject":"This is your notif",
"html":"<h1>hello!</h1>"
}
```
## Upload Image
POST `/api/send-email/`
Sending Email.
The request body needs to be in Form Data format and allows you to send the following properties:
- `image` - File - Required - 1 file only
- `category` - String - Required
- `image_id` - String - Required
Example
```
Post /api/upload_images
```

16
routes/auth.js Normal file
View File

@ -0,0 +1,16 @@
import express from "express";
import multer from "multer";
import { login, register } from "../controllers/auth.js";
const router = express.Router();
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 10 * 1024 * 1024, // 10 MB
},
});
router.post("/login", login);
router.post("/register", upload.single("picture"), register);
export default router;

23
routes/collection.js Normal file
View File

@ -0,0 +1,23 @@
import express from "express";
import {
getCollection,
getCollections,
deleteCollection,
createCollection,
updateCollection,
} from "../controllers/collection.js";
import { verifyToken } from "../middleware/auth.js";
import logApiCall from "../middleware/log.js";
const router = express.Router();
/* READ */
router.get("/", verifyToken, logApiCall, getCollections);
router.get("/:id", verifyToken, logApiCall, getCollection);
router.post("/create", verifyToken, logApiCall, createCollection);
router.patch("/update/:id", verifyToken, logApiCall, updateCollection);
router.delete("/:id/delete", verifyToken, logApiCall, deleteCollection);
export default router;

46
routes/users.js Normal file
View File

@ -0,0 +1,46 @@
import express from "express";
import multer from "multer";
import {
confirmOtp,
deleteUser,
deleteUserFriend,
getUser,
getUserId,
getUsers,
sendOtpEmail,
updateUser,
updateUserFriendData,
updateUserFriends,
updateUserPass,
updateUserPhoto,
updateUserSub,
} from "../controllers/users.js";
import { verifyToken } from "../middleware/auth.js";
const router = express.Router();
const upload = multer({
storage: multer.memoryStorage(),
limits: {
fileSize: 10 * 1024 * 1024, // 10 MB
},
});
/* READ */
router.get("/", verifyToken, getUsers);
router.get("/:id", verifyToken, getUser);
router.get("/id/:id", verifyToken, getUserId);
router.post("/send-otp", sendOtpEmail);
router.post("/confirm-otp", confirmOtp);
router.patch("/:id", verifyToken, upload.single("picture"), updateUser);
router.patch("/:id/sub", verifyToken, updateUserSub);
router.patch("/:id/password", verifyToken, updateUserPass);
router.patch("/:id/photo", verifyToken, updateUserPhoto);
router.patch("/:id/friends", verifyToken, updateUserFriends);
router.patch("/:id/friends/:friendId", verifyToken, updateUserFriendData);
router.delete("/:id/friends/:friendId", verifyToken, deleteUserFriend);
router.delete("/:id", verifyToken, deleteUser);
export default router;