updated primary to userId

main
Netcup Gituser 2023-12-17 22:53:58 +01:00
parent 822c07aff2
commit 52fc971305
12 changed files with 153 additions and 50 deletions

View File

@ -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));

22
package-lock.json generated
View File

@ -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",

View File

@ -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"

View File

@ -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" });
}
}

View File

@ -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) {

View File

@ -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" });

View File

@ -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),

View File

@ -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>;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;
}