upd
This commit is contained in:
+19
-6
@@ -2,11 +2,14 @@ import "dotenv/config";
|
||||
import express, { json } from "express";
|
||||
import connectDB from "./config/db.js";
|
||||
import cors from "cors";
|
||||
import loginRouter from "./routes/login.js";
|
||||
import registrationRouter from "./routes/registration.js";
|
||||
import cookieParser from "cookie-parser";
|
||||
import authMiddleware from "./middlewares/auth.js";
|
||||
import companiesRouter from "./routes/companies.js";
|
||||
import registerRoute from "./routes/register.js";
|
||||
import refreshRoute from "./routes/refresh.js";
|
||||
import checkRoute from "./routes/check.js";
|
||||
import loginRoute from "./routes/login.js";
|
||||
import usersRouter from "./routes/users.js";
|
||||
import companiesRouter from "./routes/companies.js";
|
||||
import buildsRouter from "./routes/builds.js";
|
||||
import actionsRouter from "./routes/actions.js";
|
||||
import schedulesRouter from "./routes/schedules.js";
|
||||
@@ -20,11 +23,21 @@ await connectDB();
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
origin: (origin, cb) => {
|
||||
cb(null, origin);
|
||||
},
|
||||
credentials: true,
|
||||
})
|
||||
);
|
||||
app.use(json());
|
||||
app.use(cors({ origin: "*" }));
|
||||
app.use(cookieParser());
|
||||
|
||||
app.use("/login", loginRouter);
|
||||
app.use("/registration", registrationRouter);
|
||||
app.use("/login", loginRoute);
|
||||
app.use("/check", checkRoute);
|
||||
app.use("/refresh", refreshRoute);
|
||||
app.use("/register", registerRoute);
|
||||
app.use("/actions", actionsRouter);
|
||||
app.use("/builds", buildsRouter);
|
||||
app.use("/scheduled_sessions", scheduledSessionsRoute);
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import jwt, { Secret } from "jsonwebtoken";
|
||||
import Token from "../models/Token.js";
|
||||
import User from "../models/User.js";
|
||||
import { jwtVerify } from "jose";
|
||||
import { createSecretKey } from "crypto";
|
||||
|
||||
async function authMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.headers.authorization || !req.headers.authorization.split(" ")[1]) {
|
||||
return res.status(401).json({ error: 10 });
|
||||
return res.status(401).json({ error: 1 });
|
||||
}
|
||||
|
||||
const accessToken = req.headers.authorization.split(" ")[1];
|
||||
|
||||
try {
|
||||
jwt.verify(accessToken, process.env.JWT_SECRET as Secret);
|
||||
await jwtVerify(
|
||||
accessToken,
|
||||
createSecretKey(process.env.JWT_SECRET!, "utf8")
|
||||
);
|
||||
} catch (error) {
|
||||
return res.status(401).json({ erorr: 20 });
|
||||
return res.status(401).json({ error: 2 });
|
||||
}
|
||||
|
||||
const foundAccessToken = await Token.findOne({ accessToken });
|
||||
|
||||
if (!foundAccessToken) {
|
||||
return res.status(401).json({ error: 30 });
|
||||
return res.status(401).json({ error: 3 });
|
||||
}
|
||||
|
||||
const user = await User.findById(foundAccessToken.userId);
|
||||
|
||||
res.locals = { accessToken, user };
|
||||
res.locals = { user, accessToken };
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ const tokenSchema = new Schema(
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
refreshToken: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -11,6 +11,11 @@ const userSchema = new Schema(
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
companyId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Company",
|
||||
required: true,
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
required: true,
|
||||
@@ -19,14 +24,6 @@ const userSchema = new Schema(
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
},
|
||||
companyId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Company",
|
||||
required: true,
|
||||
},
|
||||
buildIds: {
|
||||
type: [Schema.Types.ObjectId],
|
||||
ref: "Build",
|
||||
@@ -39,6 +36,12 @@ const userSchema = new Schema(
|
||||
}
|
||||
);
|
||||
|
||||
userSchema.virtual("tokens", {
|
||||
ref: "Token",
|
||||
foreignField: "userId",
|
||||
localField: "_id",
|
||||
});
|
||||
|
||||
const User = model("User", userSchema);
|
||||
|
||||
export default User;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
res.json({ ok: 1 });
|
||||
});
|
||||
|
||||
const checkRoute = router;
|
||||
|
||||
export default checkRoute;
|
||||
+25
-19
@@ -1,43 +1,49 @@
|
||||
import { Router } from "express";
|
||||
import jwt, { Secret } from "jsonwebtoken";
|
||||
import bcrypt from "bcrypt";
|
||||
import User from "../models/User.js";
|
||||
import Token from "../models/Token.js";
|
||||
import { decodeJwt, SignJWT } from "jose";
|
||||
import { createSecretKey } from "crypto";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
let { username, password } = req.body;
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username || !password) {
|
||||
return res.json({
|
||||
error: "You must pass the 'username' and 'password' parameters",
|
||||
});
|
||||
return res.json({ error: 1 });
|
||||
}
|
||||
|
||||
username = username.toLowerCase();
|
||||
|
||||
const user = await User.findOne({ username });
|
||||
const user = await User.findOne({ username }).lean();
|
||||
|
||||
if (!user) {
|
||||
return res.json({ error: "A user with this name was not found" });
|
||||
return res.json({ error: 2 });
|
||||
}
|
||||
|
||||
if (!bcrypt.compareSync(password, user.password)) {
|
||||
return res.json({ error: "Invalid username or password" });
|
||||
if (!bcrypt.compareSync(password, user.password!)) {
|
||||
return res.json({ error: 3 });
|
||||
}
|
||||
|
||||
const accessToken = jwt.sign({ username }, process.env.JWT_SECRET as Secret, {
|
||||
expiresIn: "365d",
|
||||
});
|
||||
const accessToken = await new SignJWT({ username })
|
||||
.setProtectedHeader({ alg: "HS256" })
|
||||
.setExpirationTime(process.env.JWT_ACCESS_EXP)
|
||||
.sign(createSecretKey(process.env.JWT_SECRET, "utf8"));
|
||||
|
||||
const userId = user.id;
|
||||
const refreshToken = await new SignJWT({ username })
|
||||
.setProtectedHeader({ alg: "HS256" })
|
||||
.setExpirationTime(process.env.JWT_REFRESH_EXP)
|
||||
.sign(createSecretKey(process.env.JWT_SECRET, "utf8"));
|
||||
|
||||
await Token.create({ userId, accessToken });
|
||||
await Token.create({ userId: user._id, accessToken, refreshToken });
|
||||
|
||||
res.json({ accessToken, user });
|
||||
res
|
||||
.cookie("refreshToken", refreshToken, {
|
||||
httpOnly: true,
|
||||
expires: new Date(decodeJwt(refreshToken).exp! * 1000),
|
||||
})
|
||||
.json({ accessToken, refreshToken, ...user });
|
||||
});
|
||||
|
||||
const loginRouter = router;
|
||||
const loginRoute = router;
|
||||
|
||||
export default loginRouter;
|
||||
export default loginRoute;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { createSecretKey } from "crypto";
|
||||
import { Router } from "express";
|
||||
import { jwtVerify, SignJWT } from "jose";
|
||||
import Token from "../models/Token.js";
|
||||
import User from "../models/User.js";
|
||||
import IToken from "../types/IToken.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
let _username;
|
||||
|
||||
if (req.cookies.refreshToken) {
|
||||
try {
|
||||
const {
|
||||
payload: { username },
|
||||
} = await jwtVerify(
|
||||
req.cookies.refreshToken,
|
||||
createSecretKey(process.env.JWT_SECRET!, "utf8")
|
||||
);
|
||||
|
||||
_username = username;
|
||||
} catch (error) {
|
||||
return res.json({
|
||||
error: `refreshToken jwtVerify - ${(error as Error).message}`,
|
||||
});
|
||||
}
|
||||
|
||||
const userWithTokens = await User.findOne({ username: _username }).populate(
|
||||
{
|
||||
path: "tokens",
|
||||
}
|
||||
);
|
||||
|
||||
if (!userWithTokens) {
|
||||
return res.json({ error: "userWithTokens is null" });
|
||||
}
|
||||
|
||||
if (!("tokens" in userWithTokens)) {
|
||||
return res.json({ error: "tokens not found in userWithTokens" });
|
||||
}
|
||||
|
||||
const foundRefreshToken = userWithTokens.tokens as IToken[];
|
||||
|
||||
if (
|
||||
!foundRefreshToken.find(
|
||||
(token) => token.refreshToken === req.cookies.refreshToken
|
||||
)?.refreshToken
|
||||
) {
|
||||
console.log("refreshToken not found in DB");
|
||||
return res.json({ error: "refreshToken not found in DB" });
|
||||
}
|
||||
|
||||
const accessToken = await new SignJWT({ username: _username })
|
||||
.setProtectedHeader({ alg: "HS256" })
|
||||
.setExpirationTime(process.env.JWT_ACCESS_EXP)
|
||||
.sign(createSecretKey(process.env.JWT_SECRET!, "utf8"));
|
||||
|
||||
await Token.findOneAndUpdate(
|
||||
{ refreshToken: req.cookies.refreshToken },
|
||||
{ accessToken }
|
||||
);
|
||||
|
||||
return res.json({ accessToken });
|
||||
}
|
||||
|
||||
// if (req.body.refreshToken) {
|
||||
// try {
|
||||
// jwtVerify(
|
||||
// req.body.refreshToken,
|
||||
// createSecretKey(process.env.JWT_SECRET!, "utf8")
|
||||
// );
|
||||
// } catch (error) {
|
||||
// return res.json({ error: `refreshToken - ${(error as Error).message}` });
|
||||
// }
|
||||
// }
|
||||
|
||||
return res.json({ error: "refreshToken not found" });
|
||||
});
|
||||
|
||||
const refreshRoute = router;
|
||||
|
||||
export default refreshRoute;
|
||||
@@ -0,0 +1,22 @@
|
||||
import bcrypt from "bcrypt";
|
||||
import { Router } from "express";
|
||||
import User from "../models/User.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
try {
|
||||
const passwordHash = bcrypt.hashSync(password, 12);
|
||||
const result = await User.create({ username, password: passwordHash });
|
||||
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
return res.json({ error: (error as Error).message });
|
||||
}
|
||||
});
|
||||
|
||||
const registerRoute = router;
|
||||
|
||||
export default registerRoute;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { Router } from "express";
|
||||
import jwt, { Secret } from "jsonwebtoken";
|
||||
import bcrypt from "bcrypt";
|
||||
import User from "../models/User.js";
|
||||
import Token from "../models/Token.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username || !password) {
|
||||
return res.json({ error: 1 });
|
||||
}
|
||||
|
||||
if (await User.exists({ username })) {
|
||||
return res.json({ error: 2 });
|
||||
}
|
||||
|
||||
const passwordHash = bcrypt.hashSync(password, 12);
|
||||
|
||||
if (!passwordHash) {
|
||||
return res.json({ error: 3 });
|
||||
}
|
||||
|
||||
const accessToken = jwt.sign({ username }, process.env.JWT_SECRET as Secret, {
|
||||
expiresIn: "365d",
|
||||
});
|
||||
|
||||
const user = await User.create({ username, password: passwordHash });
|
||||
const userId = user.id;
|
||||
|
||||
await Token.create({ userId, accessToken });
|
||||
|
||||
res.json({ accessToken, user });
|
||||
});
|
||||
|
||||
const registerRouter = router;
|
||||
|
||||
export default registerRouter;
|
||||
@@ -77,6 +77,8 @@ router.get("/:buildId", async (req, res) => {
|
||||
router.post("/", async (req, res) => {
|
||||
const { companyId, buildId, slot, startAt, client, duration } = req.body;
|
||||
|
||||
console.log("client", client);
|
||||
|
||||
if (!companyId || !buildId || !startAt || !slot) {
|
||||
return res.json({
|
||||
status: "error",
|
||||
@@ -148,109 +150,47 @@ router.post("/", async (req, res) => {
|
||||
startAt: startAtISO,
|
||||
duration,
|
||||
endAt: endAtISO,
|
||||
client,
|
||||
});
|
||||
|
||||
const url = `https://stream.graff.tech/scheduled/${scheduledSession.id}`;
|
||||
|
||||
// <-- Send an mail
|
||||
|
||||
if (client?.email) {
|
||||
console.log("client?.email", client?.email);
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
let transporter = createTransport({
|
||||
host: "mail.netangels.ru",
|
||||
port: 587,
|
||||
secure: false, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: "stream@graff.tech", // generated ethereal user
|
||||
pass: "zLUbt8Io7dh2F9KT", // generated ethereal password
|
||||
},
|
||||
});
|
||||
|
||||
// send mail with defined transport object
|
||||
try {
|
||||
await transporter.sendMail({
|
||||
from: "stream@graff.tech", // sender address
|
||||
to: client.email, // list of receivers
|
||||
subject: "Приглашение на демонстрацию - stream.graff.tech", // Subject line
|
||||
html: `<div>
|
||||
Ссылка для подключения к демонстрации: <a href="${url}" target="_blank">${url}</a>
|
||||
</div>`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error", (error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
return res.json({
|
||||
status: "success",
|
||||
scheduledSessionId: scheduledSession.id,
|
||||
url: `https://stream.graff.tech/scheduled/${scheduledSession.id}`,
|
||||
});
|
||||
}
|
||||
|
||||
const schedule = await Schedule.findOne({
|
||||
buildId,
|
||||
startDate: { $lte: startAtISO },
|
||||
// endDate: { $gte: startAtISO },
|
||||
});
|
||||
|
||||
if (!schedule) {
|
||||
return res.json({
|
||||
status: "error",
|
||||
message: "No matching schedule found", // Подходящего расписания не найдено
|
||||
});
|
||||
}
|
||||
|
||||
const scheduledSessions = await ScheduledSession.find({
|
||||
buildId,
|
||||
startAt: {
|
||||
$gte: startOfDay(startAtISO),
|
||||
$lte: endOfDay(startAtISO),
|
||||
},
|
||||
});
|
||||
|
||||
const endAtISO = addMinutes(startAtISO, schedule.sessionDuration);
|
||||
|
||||
if (scheduledSessions.length) {
|
||||
const overlappingSessions = [];
|
||||
|
||||
for (const session of scheduledSessions) {
|
||||
if (
|
||||
areIntervalsOverlapping(
|
||||
{
|
||||
start: session.startAt,
|
||||
end: addMinutes(session.endAt, schedule.sessionBreak),
|
||||
},
|
||||
{ start: startAtISO, end: endAtISO }
|
||||
)
|
||||
) {
|
||||
overlappingSessions.push(session);
|
||||
}
|
||||
}
|
||||
|
||||
if (overlappingSessions.length >= build.sessionLimit) {
|
||||
return res.json({
|
||||
status: "error",
|
||||
message:
|
||||
"It is not possible to create a session because it overlaps with the time of another session", //Невозможно создать сеанс, поскольку он перекрывается со временем другого сеанса.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const scheduledSession = await ScheduledSession.create({
|
||||
buildId,
|
||||
client,
|
||||
startAt: startAtISO,
|
||||
endAt: endAtISO,
|
||||
});
|
||||
|
||||
const url = `https://stream.graff.tech/scheduled/${scheduledSession.id}`;
|
||||
|
||||
// <-- Send an mail
|
||||
|
||||
if (client?.email) {
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
let transporter = createTransport({
|
||||
host: "mail.netangels.ru",
|
||||
port: 587,
|
||||
secure: false, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: "stream@graff.tech", // generated ethereal user
|
||||
pass: "zLUbt8Io7dh2F9KT", // generated ethereal password
|
||||
},
|
||||
});
|
||||
|
||||
// send mail with defined transport object
|
||||
try {
|
||||
await transporter.sendMail({
|
||||
from: "stream@graff.tech", // sender address
|
||||
to: client.email, // list of receivers
|
||||
subject: "Приглашение на демонстрацию - stream.graff.tech", // Subject line
|
||||
html: `<div>
|
||||
Ссылка для подключения к демонстрации: <a href="${url}" target="_blank">${url}</a>
|
||||
</div>`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error", (error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
// Send an mail -->
|
||||
|
||||
res.json({
|
||||
status: "success",
|
||||
scheduledSessionId: scheduledSession.id,
|
||||
url,
|
||||
});
|
||||
});
|
||||
|
||||
router.put("/:id", async (req, res) => {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
interface IToken {
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export default IToken;
|
||||
@@ -0,0 +1,8 @@
|
||||
interface IUser {
|
||||
id: string;
|
||||
accessToken: string;
|
||||
username: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export default IUser;
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
PORT?: string;
|
||||
NODE_ENV: "development" | "production";
|
||||
MONGO_URI: string;
|
||||
JWT_SECRET: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user