updated primary to userId
parent
822c07aff2
commit
52fc971305
4
index.ts
4
index.ts
|
@ -49,10 +49,6 @@ app.use("/api/v1/user", userRoutes);
|
|||
app.use("/api/v1/users", usersRoutes);
|
||||
app.use("/api/v1/admin", adminRoutes);
|
||||
app.use("/api/v1/events", eventsRoutes);
|
||||
app.use("/api/v1/appstart", (req, res, next) => {
|
||||
console.log("appstart");
|
||||
res.status(200).json({ status: "ok" });
|
||||
});
|
||||
|
||||
const specs = swaggerJsDoc(options);
|
||||
app.use("/api-docs", swaggerUI.serve, swaggerUI.setup(specs));
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
"mongoose": "^8.0.2",
|
||||
"morgan": "^1.10.0",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.0"
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
|
@ -25,6 +26,7 @@
|
|||
"@types/node": "^20.10.2",
|
||||
"@types/swagger-jsdoc": "^6.0.4",
|
||||
"@types/swagger-ui-express": "^4.1.6",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"concurrently": "^8.2.2",
|
||||
"nodemon": "^3.0.2",
|
||||
"typescript": "^5.3.2"
|
||||
|
@ -263,6 +265,12 @@
|
|||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz",
|
||||
"integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/webidl-conversions": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||
|
@ -2373,6 +2381,18 @@
|
|||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.11.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz",
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
"mongoose": "^8.0.2",
|
||||
"morgan": "^1.10.0",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^5.0.0"
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
|
@ -28,6 +29,7 @@
|
|||
"@types/node": "^20.10.2",
|
||||
"@types/swagger-jsdoc": "^6.0.4",
|
||||
"@types/swagger-ui-express": "^4.1.6",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"concurrently": "^8.2.2",
|
||||
"nodemon": "^3.0.2",
|
||||
"typescript": "^5.3.2"
|
||||
|
|
|
@ -6,21 +6,24 @@ import {
|
|||
decodeBase64,
|
||||
matchPassword,
|
||||
getUserSession,
|
||||
getUser,
|
||||
} from "../utils/utils";
|
||||
import { isUsernameValid, isPasswordValid } from "../validation/validation";
|
||||
import { Session } from "../models/session";
|
||||
import { MONGODB_IGNORED_FIELDS } from "../utils/constants";
|
||||
import { MONGODB_IGNORED_FIELDS_USER } from "../utils/constants";
|
||||
|
||||
export async function SignUp(req: Request, res: Response) {
|
||||
try {
|
||||
const { AccountName, Username, Password } = req.body;
|
||||
let { AccountName, Username, Password } = req.body;
|
||||
|
||||
if (!AccountName || !Username || !Password) {
|
||||
return res.status(400).json({ status: "err" });
|
||||
}
|
||||
|
||||
const existingUser = await User.findOne({ AccountName })
|
||||
.select(MONGODB_IGNORED_FIELDS)
|
||||
AccountName = AccountName.toLowerCase();
|
||||
|
||||
const existingUser = await User.findOne({ accountName: AccountName })
|
||||
.select(`accountName -_id`)
|
||||
.lean();
|
||||
|
||||
if (existingUser) {
|
||||
|
@ -42,9 +45,9 @@ export async function SignUp(req: Request, res: Response) {
|
|||
|
||||
user
|
||||
.save()
|
||||
.then(() => saveSession(res, AccountName, Username))
|
||||
.then(() => saveSession(res, user.userId, Username))
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
console.error("error on signup saving session:", err);
|
||||
res.status(500).json({ status: "err" });
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -62,7 +65,7 @@ export async function Login(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
const user = await User.findOne({ accountName: AccountName })
|
||||
.select(`accountName username password -_id`)
|
||||
.select(`userId accountName username password -_id`)
|
||||
.lean();
|
||||
|
||||
if (!user) {
|
||||
|
@ -85,7 +88,7 @@ export async function Login(req: Request, res: Response) {
|
|||
return res.status(401).json({ status: "err" });
|
||||
}
|
||||
|
||||
saveSession(res, user.accountName, user.username);
|
||||
saveSession(res, user.userId, user.username);
|
||||
} catch (error) {
|
||||
console.error("error on login:", error);
|
||||
res.status(500).json({ status: "err" });
|
||||
|
@ -100,14 +103,14 @@ export async function ChangeUsername(req: Request, res: Response) {
|
|||
return res.status(400).json({ status: "err" });
|
||||
}
|
||||
|
||||
const session = await getUserSession(req, "accountName");
|
||||
const session = await getUserSession(req, "userId");
|
||||
|
||||
if (!session) {
|
||||
return res.status(401).json({ status: "err" });
|
||||
}
|
||||
|
||||
await User.updateOne(
|
||||
{ accountName: session.accountName },
|
||||
{ userId: session.userId },
|
||||
{ $set: { username: newUsername } }
|
||||
);
|
||||
|
||||
|
@ -128,52 +131,46 @@ export async function ChangePassword(req: Request, res: Response) {
|
|||
return res.status(400).json({ status: "err" });
|
||||
}
|
||||
|
||||
console.log(cp, np);
|
||||
const session = await getUserSession(req, "userId");
|
||||
|
||||
const session = await getUserSession(req, "accountName");
|
||||
|
||||
if (!session) {
|
||||
if (!session || !session.userId) {
|
||||
return res.status(401).json({ status: "err" });
|
||||
}
|
||||
|
||||
const accountName = session.accountName;
|
||||
|
||||
const decodedCurrentPassword = decodeBase64(cp);
|
||||
|
||||
const existingUser = await User.findOne({
|
||||
accountName: accountName,
|
||||
})
|
||||
.select(`password -_id`)
|
||||
.lean();
|
||||
|
||||
if (!existingUser) {
|
||||
return res.status(400).json({ status: 1 });
|
||||
}
|
||||
|
||||
const decodedNewPassword = decodeBase64(np);
|
||||
|
||||
if (!isPasswordValid(decodedCurrentPassword)) {
|
||||
return res.status(400).json({ status: "err" });
|
||||
}
|
||||
|
||||
const userId = session.userId;
|
||||
|
||||
const existingUser = await getUser(userId);
|
||||
|
||||
if (!existingUser) {
|
||||
return res.status(400).json({ status: 1 });
|
||||
}
|
||||
|
||||
const isPasswordMatching = await matchPassword(
|
||||
decodedCurrentPassword,
|
||||
existingUser.password
|
||||
);
|
||||
|
||||
const decodedNewPassword = decodeBase64(np);
|
||||
|
||||
if (!isPasswordMatching || !isPasswordValid(decodedNewPassword)) {
|
||||
return res.status(401).json({ status: "err" });
|
||||
}
|
||||
|
||||
await User.updateOne(
|
||||
{ accountName: accountName },
|
||||
{ userId: userId },
|
||||
{ $set: { password: await hashPassword(decodedNewPassword) } }
|
||||
);
|
||||
|
||||
res.status(200).json({ status: "ok" });
|
||||
|
||||
// delete all sessions
|
||||
await Session.deleteMany({ accountName: accountName });
|
||||
await Session.deleteMany({ userId: userId });
|
||||
} catch (error) {
|
||||
console.error("error on changing password", error);
|
||||
res.status(500).json({ status: "err" });
|
||||
|
@ -196,3 +193,53 @@ export async function Logout(req: Request, res: Response) {
|
|||
res.status(500).json({ status: "err" });
|
||||
}
|
||||
}
|
||||
|
||||
export async function IsAccountNameAvailable(req: Request, res: Response) {
|
||||
try {
|
||||
let { accountName } = req.params;
|
||||
|
||||
accountName = accountName.toLowerCase();
|
||||
|
||||
if (!accountName) {
|
||||
return res.status(400).json({ status: "err" });
|
||||
}
|
||||
|
||||
const existingUser = await User.findOne({ accountName: accountName })
|
||||
.select(`accountName -_id`)
|
||||
.lean();
|
||||
|
||||
if (existingUser) {
|
||||
return res.status(422).json({ status: "not found" });
|
||||
}
|
||||
|
||||
res.status(200).json({ status: "ok" });
|
||||
} catch (error) {
|
||||
console.error("error on checking account name availability", error);
|
||||
res.status(500).json({ status: "err" });
|
||||
}
|
||||
}
|
||||
|
||||
// appstart
|
||||
export async function GetUserProfile(req: Request, res: Response) {
|
||||
try {
|
||||
const session = await getUserSession(req, "userId");
|
||||
|
||||
if (!session || !session.userId) {
|
||||
return res.status(401).json({ status: "err" });
|
||||
}
|
||||
|
||||
const user = await getUser(
|
||||
session.userId,
|
||||
`${MONGODB_IGNORED_FIELDS_USER} -createdAt`
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ status: "err" });
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
console.error("error on get user profile:", error);
|
||||
res.status(500).json({ status: "err" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,16 @@ import { MONGODB_IGNORED_FIELDS_USER } from "../utils/constants";
|
|||
|
||||
export async function GetUserProfile(req: Request, res: Response) {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
|
||||
if (!userId) {
|
||||
return res.status(400).json({ status: "err" });
|
||||
}
|
||||
|
||||
const user = await User.findOne({
|
||||
accountName: req.params.accountName,
|
||||
id: userId,
|
||||
})
|
||||
.select(`-accountName ${MONGODB_IGNORED_FIELDS_USER}`)
|
||||
.select(`-id ${MONGODB_IGNORED_FIELDS_USER}`)
|
||||
.lean();
|
||||
|
||||
if (!user) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Request } from "express";
|
|||
import { Session } from "../models/session";
|
||||
import { User } from "../models/user";
|
||||
import { HEADER_X_AUTHORIZATION } from "../utils/constants";
|
||||
import { getUserSession } from "../utils/utils";
|
||||
import { getUser, getUserSession } from "../utils/utils";
|
||||
|
||||
export async function sessionProtection(req: Request, res: any, next: any) {
|
||||
const session = await getUserSession(req);
|
||||
|
@ -22,16 +22,14 @@ export async function adminProtection(req: Request, res: any, next: any) {
|
|||
}
|
||||
|
||||
const session = await Session.findOne({ sessionId: xAuthorization })
|
||||
.select("sessionId accountName -_id")
|
||||
.select("sessionId userId -_id")
|
||||
.lean();
|
||||
|
||||
if (!session) {
|
||||
if (!session || !session.userId) {
|
||||
return res.status(401).json({ status: "err" });
|
||||
}
|
||||
|
||||
const user = await User.findOne({ accountName: session.accountName })
|
||||
.select("isAdmin -_id")
|
||||
.lean();
|
||||
const user = await getUser(session.userId, "isAdmin");
|
||||
|
||||
if (!user || !user.isAdmin) {
|
||||
return res.status(401).json({ status: "err" });
|
||||
|
|
|
@ -3,7 +3,7 @@ import { DEFAULT_SESSION_EXPIRATION } from "../utils/constants";
|
|||
|
||||
export const sessionSchema = new Schema({
|
||||
sessionId: String,
|
||||
accountName: String,
|
||||
userId: String,
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
default: new Date(Date.now() + DEFAULT_SESSION_EXPIRATION),
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import mongoose, { InferSchemaType, Schema } from "mongoose";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export interface IUser extends Document {
|
||||
userId: string;
|
||||
accountName: string;
|
||||
username: string;
|
||||
password: string;
|
||||
|
@ -8,9 +10,16 @@ export interface IUser extends Document {
|
|||
following: number;
|
||||
visited: number;
|
||||
isAdmin: boolean;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export const userSchema = new Schema({
|
||||
userId: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
default: () => uuidv4(),
|
||||
},
|
||||
accountName: String,
|
||||
username: String,
|
||||
password: String,
|
||||
|
@ -30,6 +39,10 @@ export const userSchema = new Schema({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
default: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
export type User = InferSchemaType<typeof userSchema>;
|
||||
|
|
|
@ -130,5 +130,7 @@ router.post("/login", userController.Login);
|
|||
router.post("/username", sessionProtection, userController.ChangeUsername);
|
||||
router.post("/password", sessionProtection, userController.ChangePassword);
|
||||
router.post("/logout", sessionProtection, userController.Logout);
|
||||
router.get("/check/:accountName", userController.IsAccountNameAvailable);
|
||||
router.get("/", sessionProtection, userController.GetUserProfile);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -3,6 +3,6 @@ const router = express.Router();
|
|||
import * as usersController from "../controllers/usersController";
|
||||
import { sessionProtection } from "../middleware/authMiddleware";
|
||||
|
||||
router.get("/:accountName", sessionProtection, usersController.GetUserProfile);
|
||||
router.get("/:userId", sessionProtection, usersController.GetUserProfile);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -12,6 +12,7 @@ export const MONGODB_IGNORED_FIELDS_USER: string =
|
|||
// Header name for the session ID
|
||||
export const HEADER_X_AUTHORIZATION: string = "x-authorization";
|
||||
|
||||
// Regular expressions and length requirements
|
||||
export const USERNAME_MIN_LENGTH: number = 2;
|
||||
export const USERNAME_MAX_LENGTH: number = 24;
|
||||
export const USERNAME_REGEX: RegExp = /^[a-zA-Z0-9_]+$/; // Alphanumeric and underscore
|
||||
|
|
|
@ -2,11 +2,12 @@ import crypto from "crypto";
|
|||
import { Session } from "../models/session";
|
||||
import { Request, Response } from "express";
|
||||
import bcrypt from "bcrypt";
|
||||
import { HEADER_X_AUTHORIZATION } from "./constants";
|
||||
import { HEADER_X_AUTHORIZATION, MONGODB_IGNORED_FIELDS } from "./constants";
|
||||
import { User } from "../models/user";
|
||||
|
||||
export async function saveSession(
|
||||
res: Response,
|
||||
accountName: string,
|
||||
userId: string,
|
||||
username: string
|
||||
) {
|
||||
try {
|
||||
|
@ -16,14 +17,16 @@ export async function saveSession(
|
|||
// Create a new session document
|
||||
const session = new Session({
|
||||
sessionId: sessionId,
|
||||
accountName: accountName, // Assuming you have the user information in req.user
|
||||
userId: userId,
|
||||
});
|
||||
|
||||
// Save the session to MongoDB
|
||||
await session.save();
|
||||
|
||||
// Respond with the session ID
|
||||
res.status(200).json({ XAuthorization: sessionId, Username: username });
|
||||
res
|
||||
.status(200)
|
||||
.json({ XAuthorization: sessionId, UserId: userId, Username: username });
|
||||
} catch (error) {
|
||||
console.error("Error saving session:", error);
|
||||
res.status(500).json({ status: "err" });
|
||||
|
@ -43,19 +46,34 @@ export function decodeBase64(value: string) {
|
|||
}
|
||||
|
||||
export async function getUserSession(req: Request, select?: string) {
|
||||
// Get the session ID from the request headers
|
||||
const sessionId = req.get(HEADER_X_AUTHORIZATION);
|
||||
|
||||
if (!sessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the session in MongoDB
|
||||
const session = await Session.findOne({ sessionId })
|
||||
.select(`sessionId -_id ${select}`)
|
||||
.lean();
|
||||
|
||||
// Return the session
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
export async function getUser(userId: string, select?: string) {
|
||||
const user = await User.findOne({ userId: userId })
|
||||
.select(select ? select : "")
|
||||
.lean();
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
|
Reference in New Issue