Skip to content

Commit cee92ba

Browse files
Merge pull request #1299 from Code-A2Z/avdhesh/postman-docs
Creating Code A2Z API Postman Docs
2 parents 1cc73a1 + e124ae1 commit cee92ba

90 files changed

Lines changed: 8811 additions & 1491 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/package-lock.json

Lines changed: 0 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/Code A2Z.postman_collection.json

Lines changed: 6854 additions & 0 deletions
Large diffs are not rendered by default.

server/.env.example

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
# Server Environment Variables
22
PORT=8000
3-
NODE_ENV=production # Change to 'development' for local development
3+
NODE_ENV=production # Change to 'development' for development environment
44
VITE_SERVER_DOMAIN=https://code-a2z-server.vercel.app
5+
6+
# Database Configuration
57
MONGODB_URL=mongodb://localhost:27017/code-a2z
8+
9+
# Authentication Configuration
610
JWT_SECRET_ACCESS_KEY=
7-
JWT_EXPIRES_IN=7D
11+
JWT_ACCESS_EXPIRES_IN=15m
12+
JWT_ACCESS_EXPIRES_IN_NUM=900000
13+
JWT_SECRET_REFRESH_KEY=
14+
JWT_REFRESH_EXPIRES_IN=7D
15+
JWT_REFRESH_EXPIRES_IN_NUM=604800000
816

917
# Cloudinary Configuration (for media uploads)
1018
CLOUDINARY_CLOUD_NAME=
1119
CLOUDINARY_API_KEY=
1220
CLOUDINARY_API_SECRET=
1321

14-
# Email Configuration (Gmail)
22+
# Admin Credentials (for sending emails)
1523
ADMIN_EMAIL=
16-
ADMIN_PASSWORD=
24+
RESEND_API_KEY=

server/package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/config/cloudinary.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { v2 as cloudinary } from 'cloudinary';
22

3+
import { NodeEnv } from '../typings/index.js';
34
import {
45
CLOUDINARY_CLOUD_NAME,
56
CLOUDINARY_API_KEY,
67
CLOUDINARY_API_SECRET,
8+
NODE_ENV,
79
} from '../config/env.js';
810

911
cloudinary.config({
1012
cloud_name: CLOUDINARY_CLOUD_NAME,
1113
api_key: CLOUDINARY_API_KEY,
1214
api_secret: CLOUDINARY_API_SECRET,
13-
secure: true,
15+
secure: NODE_ENV === NodeEnv.PRODUCTION,
1416
});
1517

1618
export default cloudinary;

server/src/config/env.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,34 @@ dotenv.config();
77
export const PORT = process.env.PORT || 8000;
88
export const NODE_ENV = process.env.NODE_ENV || 'development';
99
export const VITE_SERVER_DOMAIN =
10-
process.env.VITE_SERVER_DOMAIN || 'https://code-a2z-server.vercel.app';
10+
process.env.VITE_SERVER_DOMAIN || 'https://code-a2z-server.vercel.app';
1111

1212
// MongoDB Configuration
1313
export const MONGODB_URL =
14-
process.env.MONGODB_URL || 'mongodb://localhost:27017/code-a2z';
14+
process.env.MONGODB_URL || 'mongodb://localhost:27017/code-a2z';
1515

1616
// JWT Configuration
1717
export const JWT_SECRET_ACCESS_KEY =
18-
process.env.JWT_SECRET_ACCESS_KEY || 'default_secret_key';
19-
export const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7D';
18+
process.env.JWT_SECRET_ACCESS_KEY || 'default_secret_key';
19+
export const JWT_ACCESS_EXPIRES_IN = process.env.JWT_ACCESS_EXPIRES_IN || '15m';
20+
export const JWT_ACCESS_EXPIRES_IN_NUM =
21+
process.env.JWT_ACCESS_EXPIRES_IN_NUM || 15 * 60 * 1000; // 15 minutes in ms
22+
23+
export const JWT_SECRET_REFRESH_KEY =
24+
process.env.JWT_SECRET_REFRESH_KEY || 'default_refresh_secret_key';
25+
export const JWT_REFRESH_EXPIRES_IN =
26+
process.env.JWT_REFRESH_EXPIRES_IN || '7d';
27+
export const JWT_REFRESH_EXPIRES_IN_NUM =
28+
process.env.JWT_REFRESH_EXPIRES_IN_NUM || 7 * 24 * 60 * 60 * 1000; // 7 days in ms
2029

2130
// Cloudinary Configuration (for media uploads)
2231
export const CLOUDINARY_CLOUD_NAME =
23-
process.env.CLOUDINARY_CLOUD_NAME || 'admin';
32+
process.env.CLOUDINARY_CLOUD_NAME || 'admin';
2433
export const CLOUDINARY_API_KEY = process.env.CLOUDINARY_API_KEY || 'admin';
2534
export const CLOUDINARY_API_SECRET =
26-
process.env.CLOUDINARY_API_SECRET || 'admin';
35+
process.env.CLOUDINARY_API_SECRET || 'admin';
2736

2837
// Resend / Email Configuration
29-
export const ADMIN_EMAIL =
30-
process.env.ADMIN_EMAIL || "dev.admin@example.com";
38+
export const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'dev.admin@example.com';
3139
export const RESEND_API_KEY =
32-
process.env.RESEND_API_KEY || "dev_resend_key_abc123";
40+
process.env.RESEND_API_KEY || 'dev_resend_key_abc123';

server/src/config/resend.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Resend } from "resend";
2-
import { RESEND_API_KEY } from "./env.js";
1+
import { Resend } from 'resend';
2+
import { RESEND_API_KEY } from './env.js';
33

44
if (!RESEND_API_KEY) {
5-
throw new Error("Resend API key is not set in environment variables.");
5+
throw new Error('Resend API key is not set in environment variables.');
66
}
77

88
const resend = new Resend(RESEND_API_KEY);

server/src/constants/db.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ export const COLLECTION_NAMES = {
44
PROJECTS: 'projects',
55
COMMENTS: 'comments',
66
NOTIFICATIONS: 'notifications',
7-
COLLABORATORS: 'collaborators',
7+
COLLABS: 'collabs',
88
COLLECTIONS: 'collections',
99
};

server/src/controllers/auth/login.js

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,69 @@ import jwt from 'jsonwebtoken';
33

44
import User from '../../models/user.model.js';
55
import Subscriber from '../../models/subscriber.model.js';
6-
76
import { sendResponse } from '../../utils/response.js';
7+
import { CookieToken, NodeEnv } from '../../typings/index.js';
88
import {
99
JWT_SECRET_ACCESS_KEY,
10-
JWT_EXPIRES_IN,
10+
JWT_SECRET_REFRESH_KEY,
11+
JWT_ACCESS_EXPIRES_IN,
12+
JWT_REFRESH_EXPIRES_IN,
13+
JWT_ACCESS_EXPIRES_IN_NUM,
14+
JWT_REFRESH_EXPIRES_IN_NUM,
1115
NODE_ENV,
1216
} from '../../config/env.js';
13-
import { NodeEnv } from '../../typings/index.js';
17+
18+
// Helper function to generate both tokens
19+
const generateTokens = payload => {
20+
const accessToken = jwt.sign(payload, JWT_SECRET_ACCESS_KEY, {
21+
expiresIn: JWT_ACCESS_EXPIRES_IN || '15m',
22+
});
23+
const refreshToken = jwt.sign(payload, JWT_SECRET_REFRESH_KEY, {
24+
expiresIn: JWT_REFRESH_EXPIRES_IN || '7d',
25+
});
26+
return { accessToken, refreshToken };
27+
};
1428

1529
const login = async (req, res) => {
1630
const { email, password } = req.body;
1731

1832
try {
33+
// Check subscriber existence
1934
const isSubscriber = await Subscriber.exists({ email });
2035
if (!isSubscriber) {
21-
return sendResponse(res, 404, 'error', 'Email not found', null);
36+
return sendResponse(res, 404, 'error', 'Email not found');
2237
}
2338

2439
const user = await User.findOne({
25-
'personal_info.email': isSubscriber?._id,
40+
'personal_info.email': isSubscriber._id,
2641
});
2742
if (!user) {
28-
return sendResponse(res, 404, 'error', 'User not found', null);
43+
return sendResponse(res, 404, 'error', 'User not found');
2944
}
3045

31-
if (!user.personal_info || !user.personal_info.password) {
32-
return sendResponse(res, 500, 'error', 'User data is incomplete', null);
46+
if (!user.personal_info?.password) {
47+
return sendResponse(res, 500, 'error', 'User data is incomplete');
3348
}
3449

3550
const isMatch = await bcrypt.compare(password, user.personal_info.password);
36-
if (!isMatch)
37-
return sendResponse(res, 401, 'error', 'Incorrect password', null);
51+
if (!isMatch) return sendResponse(res, 401, 'error', 'Incorrect password');
3852

39-
const payload = { userId: user._id, email: user?.personal_info?.email };
40-
const secret = JWT_SECRET_ACCESS_KEY;
41-
const options = { expiresIn: JWT_EXPIRES_IN };
53+
const payload = { userId: user._id, email: user.personal_info.email };
54+
const { accessToken, refreshToken } = generateTokens(payload);
4255

43-
const token = jwt.sign(payload, secret, options);
56+
// Set secure cookies
57+
res.cookie(CookieToken.ACCESS_TOKEN, accessToken, {
58+
httpOnly: true,
59+
secure: NODE_ENV === NodeEnv.PRODUCTION,
60+
sameSite: 'strict',
61+
maxAge: JWT_ACCESS_EXPIRES_IN_NUM,
62+
});
4463

45-
res.cookie('token', token, {
64+
res.cookie(CookieToken.REFRESH_TOKEN, refreshToken, {
4665
httpOnly: true,
4766
secure: NODE_ENV === NodeEnv.PRODUCTION,
4867
sameSite: 'strict',
49-
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
68+
maxAge: JWT_REFRESH_EXPIRES_IN_NUM,
5069
});
5170

5271
return sendResponse(res, 200, 'success', 'Login successful', user);
@@ -55,8 +74,7 @@ const login = async (req, res) => {
5574
res,
5675
500,
5776
'error',
58-
err.message || 'Internal Server Error',
59-
null
77+
err.message || 'Internal Server Error'
6078
);
6179
}
6280
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { sendResponse } from '../../utils/response.js';
2+
import { CookieToken, NodeEnv } from '../../typings/index.js';
3+
import { NODE_ENV } from '../../config/env.js';
4+
5+
const logout = async (req, res) => {
6+
try {
7+
// Clear both access and refresh cookies
8+
res.clearCookie(CookieToken.ACCESS_TOKEN, {
9+
httpOnly: true,
10+
secure: NODE_ENV === NodeEnv.PRODUCTION,
11+
sameSite: 'strict',
12+
});
13+
14+
res.clearCookie(CookieToken.REFRESH_TOKEN, {
15+
httpOnly: true,
16+
secure: NODE_ENV === NodeEnv.PRODUCTION,
17+
sameSite: 'strict',
18+
});
19+
20+
return sendResponse(res, 200, 'success', 'Logged out successfully');
21+
} catch (err) {
22+
return sendResponse(
23+
res,
24+
500,
25+
'error',
26+
err.message || 'Internal Server Error'
27+
);
28+
}
29+
};
30+
31+
export default logout;

0 commit comments

Comments
 (0)