changed to email
parent
7a78649010
commit
bd1fedb397
15
env.example
15
env.example
|
@ -7,27 +7,22 @@ MARIADB_DATABASE=terminplanner
|
|||
MARIADB_USER=terminplanner
|
||||
MARIADB_PASSWORD=your_password
|
||||
|
||||
DASHBOARD_URL=
|
||||
DASHBOARD_API_URL=
|
||||
|
||||
GOOGLE_CLIENT_ID=your_client_id
|
||||
GOOGLE_CLIENT_SECRET=your_client_secret
|
||||
GOOGLE_CALLBACK_URL=your_callback_url
|
||||
|
||||
PASSPORT_FAILURE_REDIRECT_URL=your_failure_redirect_url
|
||||
PASSPORT_SUCCESS_REDIRECT_URL=your_success_redirect_url
|
||||
|
||||
TERMIN_PLANNER_AUTHORIZATION_PASSWORD=your_authorization_password
|
||||
TERMIN_PLANNER_URL=your_termin_planner_url
|
||||
|
||||
WEBSITE_BUILDER_TEMPLATE_REPOSITORY_URL=https://your-git-repo.com/website-template.git
|
||||
WEBSITE_BUILDER_TMP_DIR=./tmp
|
||||
WEBSITE_BUILDER_TMP_DIR_WEBSITE_TEMPLATE=./tmp/website-template
|
||||
WEBSITE_BUILDER_TMP_CUSTOMER_WEBSITES_DIR=./customer-websites
|
||||
|
||||
RABBITMQ_HOST=
|
||||
RABBITMQ_PORT=
|
||||
RABBITMQ_USERNAME=
|
||||
RABBITMQ_PASSWORD=
|
||||
RABBITMQ_MAIL_QUEUE=
|
||||
|
||||
ACCOUNT_EXPORT_URL=
|
||||
RECAPTCHA_SECRET_KEY=
|
||||
|
||||
RECAPTCHA_SECRET_KEY=
|
||||
CONSTANTS_ACCOUNT_DEMO_DAYS=
|
41
server.ts
41
server.ts
|
@ -7,14 +7,13 @@ import GoogleStrategy from "passport-google-oauth20";
|
|||
import cookieParser from "cookie-parser";
|
||||
import session from "express-session";
|
||||
import useragent from "express-useragent";
|
||||
import rabbitmq from "./src/rabbitmq/rabbitmq";
|
||||
|
||||
import calendarRoutes from "./src/routes/calendarRoutes";
|
||||
import storeRoutes from "./src/routes/storeRoutes";
|
||||
import storeServicesRoutes from "./src/routes/storeServicesRoutes";
|
||||
import userRoutes from "./src/routes/userRoutes";
|
||||
import usersRoutes from "./src/routes/usersRoutes";
|
||||
import websiteRoutes from "./src/routes/websiteRoutes";
|
||||
//import websiteRoutes from "./src/routes/websiteRoutes";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
|
@ -22,8 +21,9 @@ import swaggerJsDoc from "swagger-jsdoc";
|
|||
import syncModels from "./src/models/index";
|
||||
import logger from "./src/logger/logger";
|
||||
import passport from "passport";
|
||||
import fs from "fs-extra";
|
||||
import { cloneWebsiteTemplate } from "./src/controllers/websiteController";
|
||||
import rabbitmq from "./src/rabbitmq/rabbitmq";
|
||||
import { GOOGLE_CALLBACK_URL } from "./src/utils/constants";
|
||||
//import { cloneWebsiteTemplate } from "./src/controllers/websiteController";
|
||||
const app: Express = express();
|
||||
const host = process.env.HOST || "localhost";
|
||||
const port = Number(process.env.PORT) || 3000;
|
||||
|
@ -68,7 +68,7 @@ passport.use(
|
|||
{
|
||||
clientID: process.env.GOOGLE_CLIENT_ID as string,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
|
||||
callbackURL: process.env.GOOGLE_CALLBACK_URL as string,
|
||||
callbackURL: GOOGLE_CALLBACK_URL,
|
||||
/*scope: [
|
||||
"https://www.googleapis.com/auth/calendar.app.created",
|
||||
"https://www.googleapis.com/auth/calendar.events.freebusy",
|
||||
|
@ -117,7 +117,7 @@ app.use("/api/v1/store", storeRoutes);
|
|||
app.use("/api/v1/store/services", storeServicesRoutes);
|
||||
app.use("/api/v1/user", userRoutes);
|
||||
app.use("/api/v1/users", usersRoutes);
|
||||
app.use("/api/v1/website", websiteRoutes);
|
||||
//app.use("/api/v1/website", websiteRoutes);
|
||||
|
||||
const specs = swaggerJsDoc(options);
|
||||
app.use("/api-docs", swaggerUI.serve, swaggerUI.setup(specs));
|
||||
|
@ -135,7 +135,7 @@ app.use((err: any, req: any, res: any, next: any) => {
|
|||
syncModels();
|
||||
|
||||
// create tmp folder if not exists
|
||||
|
||||
/*
|
||||
[
|
||||
process.env.WEBSITE_BUILDER_TMP_DIR as string,
|
||||
process.env.WEBSITE_BUILDER_TMP_CUSTOMER_WEBSITES_DIR as string,
|
||||
|
@ -154,16 +154,21 @@ if (
|
|||
) {
|
||||
cloneWebsiteTemplate();
|
||||
}
|
||||
|
||||
*/
|
||||
// start server
|
||||
|
||||
rabbitmq(
|
||||
process.env.RABBITMQ_USERNAME as string,
|
||||
process.env.RABBITMQ_PASSWORD as string,
|
||||
process.env.RABBITMQ_HOST as string,
|
||||
process.env.RABBITMQ_PORT as string
|
||||
);
|
||||
|
||||
app.listen(port, host, () =>
|
||||
logger.info(`⚡️[server]: Server is running at http://${host}:${port}`)
|
||||
);
|
||||
rabbitmq
|
||||
.connect(
|
||||
process.env.RABBITMQ_USERNAME as string,
|
||||
process.env.RABBITMQ_PASSWORD as string,
|
||||
process.env.RABBITMQ_HOST as string,
|
||||
process.env.RABBITMQ_PORT as string
|
||||
)
|
||||
.then(() => {
|
||||
app.listen(port, host, () =>
|
||||
logger.info(`⚡️[server]: Server is running at http://${host}:${port}`)
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
});
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Request, Response } from "express";
|
|||
import logger from "../logger/logger";
|
||||
import User from "../models/user";
|
||||
import {
|
||||
isAccountNameValid,
|
||||
isCompanyNameValid,
|
||||
isEmailValid,
|
||||
isLanguageCodeValid,
|
||||
|
@ -15,15 +14,19 @@ import {
|
|||
CALENDAR_MAX_FUTURE_BOOKING_DAYS,
|
||||
CALENDAR_MAX_SERVICE_DURATION,
|
||||
CALENDAR_MIN_EARLIEST_BOOKING_TIME,
|
||||
DASHBOARD_URL,
|
||||
EMAIL_VERIFICATION_STATE,
|
||||
Roles,
|
||||
USER_ANALYTICS_ENABLED_DEFAULT,
|
||||
} from "../utils/constants";
|
||||
import {
|
||||
decodeBase64,
|
||||
getEmailVerificationUrl,
|
||||
getUserSession,
|
||||
hashPassword,
|
||||
matchPassword,
|
||||
newAccountExportId,
|
||||
newEmailVerificationId,
|
||||
newFeedbackId,
|
||||
newStoreId,
|
||||
newUserId,
|
||||
|
@ -33,15 +36,16 @@ import Store from "../models/store";
|
|||
import Session from "../models/session";
|
||||
import Feedback from "../models/feedback";
|
||||
import fs from "fs-extra";
|
||||
import { channel } from "../rabbitmq/rabbitmq";
|
||||
import rabbitmq from "../rabbitmq/rabbitmq";
|
||||
import verifyCaptcha from "../utils/recaptcha";
|
||||
import EmailVerification from "../models/emailVerification";
|
||||
|
||||
export async function SignUp(req: Request, res: Response) {
|
||||
try {
|
||||
let {
|
||||
companyName,
|
||||
username,
|
||||
accountName,
|
||||
email,
|
||||
password,
|
||||
language,
|
||||
rememberMe,
|
||||
|
@ -53,12 +57,12 @@ export async function SignUp(req: Request, res: Response) {
|
|||
if (
|
||||
!companyName ||
|
||||
!username ||
|
||||
!accountName ||
|
||||
!email ||
|
||||
!password ||
|
||||
!language ||
|
||||
!isCompanyNameValid(companyName) ||
|
||||
!isUsernameValid(username) ||
|
||||
!isAccountNameValid(accountName) ||
|
||||
!isEmailValid(email) ||
|
||||
!isLanguageCodeValid(language) ||
|
||||
rememberMe === undefined ||
|
||||
!recaptcha
|
||||
|
@ -77,13 +81,13 @@ export async function SignUp(req: Request, res: Response) {
|
|||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
accountName = accountName.toLowerCase();
|
||||
email = email.toLowerCase();
|
||||
|
||||
// check if user already exists
|
||||
|
||||
const existingUser = await User.findOne({
|
||||
where: {
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -107,7 +111,7 @@ export async function SignUp(req: Request, res: Response) {
|
|||
|
||||
let userId = newUserId();
|
||||
|
||||
Store.create({
|
||||
const store = await Store.create({
|
||||
store_id: newStoreId(),
|
||||
owner_user_id: userId,
|
||||
name: companyName,
|
||||
|
@ -115,34 +119,41 @@ export async function SignUp(req: Request, res: Response) {
|
|||
calendar_min_earliest_booking_time: CALENDAR_MIN_EARLIEST_BOOKING_TIME,
|
||||
calendar_max_service_duration: CALENDAR_MAX_SERVICE_DURATION,
|
||||
address: "",
|
||||
})
|
||||
.then(async (store) => {
|
||||
// create user
|
||||
});
|
||||
|
||||
await User.create({
|
||||
user_id: userId,
|
||||
store_id: store.store_id,
|
||||
role: Roles.Master,
|
||||
account_name: accountName,
|
||||
username: username,
|
||||
password: hashedPassword,
|
||||
language: language,
|
||||
analytics_enabled: USER_ANALYTICS_ENABLED_DEFAULT,
|
||||
state: ACCOUNT_STATE.ACTIVE,
|
||||
})
|
||||
.then((user) => {
|
||||
// create session
|
||||
saveSession(req, res, user.user_id, user.username, rememberMe);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
});
|
||||
// create email verification
|
||||
|
||||
const emailVerificationId = newEmailVerificationId();
|
||||
const state = EMAIL_VERIFICATION_STATE.PENDING_EMAIL_VERIFICATION;
|
||||
|
||||
await EmailVerification.create({
|
||||
email_verification_id: emailVerificationId,
|
||||
user_id: userId,
|
||||
state: state,
|
||||
});
|
||||
|
||||
rabbitmq.sendEmail(email, "dashboardSignUpEmailVerification", language, {
|
||||
emailVerificationUrl: getEmailVerificationUrl(
|
||||
String(state),
|
||||
emailVerificationId
|
||||
),
|
||||
});
|
||||
|
||||
// create user
|
||||
|
||||
const user = await User.create({
|
||||
user_id: userId,
|
||||
store_id: store.store_id,
|
||||
role: Roles.Master,
|
||||
email: email,
|
||||
username: username,
|
||||
password: hashedPassword,
|
||||
language: language,
|
||||
analytics_enabled: USER_ANALYTICS_ENABLED_DEFAULT,
|
||||
state: ACCOUNT_STATE.PENDING_EMAIL_VERIFICATION,
|
||||
});
|
||||
|
||||
saveSession(req, res, false, user.user_id, user.username, rememberMe);
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
|
@ -151,24 +162,24 @@ export async function SignUp(req: Request, res: Response) {
|
|||
|
||||
export async function Login(req: Request, res: Response) {
|
||||
try {
|
||||
let { accountName, password, rememberMe, recaptcha } = req.body;
|
||||
let { email, password, rememberMe, recaptcha } = req.body;
|
||||
|
||||
// validate request
|
||||
|
||||
if (!accountName) {
|
||||
if (!email) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
accountName = accountName.toLowerCase();
|
||||
email = email.toLowerCase();
|
||||
|
||||
if (!isAccountNameValid(accountName)) {
|
||||
if (!isEmailValid(email)) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
// check if user exists
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
},
|
||||
attributes: ["user_id", "password", "state"],
|
||||
});
|
||||
|
@ -179,7 +190,7 @@ export async function Login(req: Request, res: Response) {
|
|||
|
||||
// if password not provided, then send user state
|
||||
// user is on the login page on the first step of the login process
|
||||
// and only needs to enter their account name to get the user state to know what to do next
|
||||
// and only needs to enter their email to get the user state to know what to do next
|
||||
|
||||
if (password === undefined) {
|
||||
return res.status(200).send({ state: user.state });
|
||||
|
@ -244,7 +255,7 @@ export async function Login(req: Request, res: Response) {
|
|||
}
|
||||
|
||||
// create session
|
||||
saveSession(req, res, user.user_id, user.username, rememberMe);
|
||||
saveSession(req, res, true, user.user_id, user.username, rememberMe);
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
|
@ -385,19 +396,19 @@ export async function GetUser(req: Request, res: Response) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function IsAccountNameAvailable(req: Request, res: Response) {
|
||||
export async function IsEmailAvailable(req: Request, res: Response) {
|
||||
try {
|
||||
let { accountName } = req.body;
|
||||
let { email } = req.body;
|
||||
|
||||
// validate request
|
||||
|
||||
if (!accountName) {
|
||||
if (!email) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
accountName = accountName.toLowerCase();
|
||||
email = email.toLowerCase();
|
||||
|
||||
if (!isAccountNameValid(accountName)) {
|
||||
if (!isEmailValid(email)) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -405,7 +416,7 @@ export async function IsAccountNameAvailable(req: Request, res: Response) {
|
|||
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -413,7 +424,7 @@ export async function IsAccountNameAvailable(req: Request, res: Response) {
|
|||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
res.status(200).send({ msg: "account name available" });
|
||||
res.status(200).send({ msg: "email available" });
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
|
@ -432,7 +443,7 @@ export async function GetUserProfileSettings(req: Request, res: Response) {
|
|||
where: {
|
||||
user_id: session.user_id,
|
||||
},
|
||||
attributes: ["language", "analytics_enabled", "username", "account_name"],
|
||||
attributes: ["language", "analytics_enabled", "username", "email"],
|
||||
});
|
||||
|
||||
res.status(200).json(user);
|
||||
|
@ -444,14 +455,9 @@ export async function GetUserProfileSettings(req: Request, res: Response) {
|
|||
|
||||
export async function UpdateUserProfileSettings(req: Request, res: Response) {
|
||||
try {
|
||||
const { language, analyticsEnabled, username, accountName } = req.body;
|
||||
const { language, analyticsEnabled, username, email } = req.body;
|
||||
|
||||
if (
|
||||
!language &&
|
||||
analyticsEnabled === undefined &&
|
||||
!username &&
|
||||
!accountName
|
||||
) {
|
||||
if (!language && analyticsEnabled === undefined && !username && !email) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -487,12 +493,12 @@ export async function UpdateUserProfileSettings(req: Request, res: Response) {
|
|||
user.username = username;
|
||||
}
|
||||
|
||||
if (accountName) {
|
||||
if (!isAccountNameValid(accountName)) {
|
||||
if (email) {
|
||||
if (!isEmailValid(email)) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
user.account_name = accountName;
|
||||
user.email = email;
|
||||
}
|
||||
|
||||
await user.save();
|
||||
|
@ -757,7 +763,7 @@ export async function ExportUserAccount(req: Request, res: Response) {
|
|||
user: {
|
||||
user_id: user.user_id,
|
||||
username: user.username,
|
||||
account_name: user.account_name,
|
||||
email: user.email,
|
||||
calendar_max_future_booking_days:
|
||||
user.calendar_max_future_booking_days,
|
||||
calendar_min_earliest_booking_time:
|
||||
|
@ -778,22 +784,11 @@ export async function ExportUserAccount(req: Request, res: Response) {
|
|||
|
||||
// send email with file
|
||||
|
||||
channel.sendToQueue(
|
||||
process.env.RABBITMQ_MAIL_QUEUE as string,
|
||||
Buffer.from(
|
||||
JSON.stringify({
|
||||
m: email, // UserMail
|
||||
t: "dashboardUserAccountExportFinish", // TemplateId
|
||||
l: "de", // LanguageId
|
||||
// BodyData
|
||||
b: {
|
||||
accountExportDownloadUrl: `${
|
||||
process.env.ACCOUNT_EXPORT_URL as string
|
||||
}${accountExportId}`,
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
rabbitmq.sendEmail(email, "dashboardUserAccountExportFinish", "de", {
|
||||
accountExportDownloadUrl: `${
|
||||
process.env.ACCOUNT_EXPORT_URL as string
|
||||
}${accountExportId}`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
|
@ -806,7 +801,7 @@ export async function ExportUserAccount(req: Request, res: Response) {
|
|||
}
|
||||
}
|
||||
|
||||
export function GetExportedUserAccount(req: Request, res: Response) {
|
||||
export async function GetExportedUserAccount(req: Request, res: Response) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
@ -830,3 +825,53 @@ export function GetExportedUserAccount(req: Request, res: Response) {
|
|||
res.status(500).send({ err: "invalid request" });
|
||||
}
|
||||
}
|
||||
|
||||
export async function VerifyEmail(req: Request, res: Response) {
|
||||
try {
|
||||
const { state, emailVerificationId } = req.params;
|
||||
|
||||
if (state === undefined || !emailVerificationId) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
const emailVerification = await EmailVerification.findOne({
|
||||
where: {
|
||||
email_verification_id: emailVerificationId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!emailVerification) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
if (
|
||||
emailVerification.state ===
|
||||
EMAIL_VERIFICATION_STATE.PENDING_EMAIL_VERIFICATION
|
||||
) {
|
||||
await User.update(
|
||||
{
|
||||
state: ACCOUNT_STATE.ACTIVE,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
user_id: emailVerification.user_id,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await EmailVerification.destroy({
|
||||
where: {
|
||||
email_verification_id: emailVerificationId,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(200).send({ msg: "email verified" });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(400).send({ err: "invalid request" });
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Request, Response } from "express";
|
||||
import logger from "../logger/logger";
|
||||
import {
|
||||
isAccountNameValid,
|
||||
isEmailValid,
|
||||
isLanguageCodeValid,
|
||||
isPasswordValid,
|
||||
isUserIdValid,
|
||||
|
@ -27,7 +27,7 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
let {
|
||||
storeId,
|
||||
username,
|
||||
accountName,
|
||||
email,
|
||||
password,
|
||||
calendarMaxFutureBookingDays,
|
||||
calendarMinEarliestBookingTime,
|
||||
|
@ -40,18 +40,15 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
if (
|
||||
!storeId ||
|
||||
!username ||
|
||||
!accountName ||
|
||||
!email ||
|
||||
passwordSetOnInitLogging === undefined ||
|
||||
(!password && passwordSetOnInitLogging === false) ||
|
||||
!language ||
|
||||
!isLanguageCodeValid(language)
|
||||
) {
|
||||
logger.debug("Invalid request");
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
logger.debug("Request is valid %s %s", passwordSetOnInitLogging, password);
|
||||
|
||||
// verify if requester is a store master
|
||||
|
||||
const requesterSession = await getUserSession(req);
|
||||
|
@ -74,14 +71,11 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
return res.status(401).send({ err: "unauthorized" });
|
||||
}
|
||||
|
||||
// validate username and account name
|
||||
// validate username and email
|
||||
|
||||
accountName = accountName.toLowerCase();
|
||||
email = email.toLowerCase();
|
||||
|
||||
if (
|
||||
!isUsernameValid(username) ||
|
||||
!(await isAccountNameValid(accountName))
|
||||
) {
|
||||
if (!isUsernameValid(username) || !(await isEmailValid(email))) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
||||
|
@ -89,7 +83,7 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
|
||||
const existingUser = await User.findOne({
|
||||
where: {
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -105,7 +99,7 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
user_id: userId,
|
||||
store_id: storeId,
|
||||
role: Roles.Worker,
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
username: username,
|
||||
calendar_max_future_booking_days: calendarMaxFutureBookingDays,
|
||||
calendar_min_earliest_booking_time: calendarMinEarliestBookingTime,
|
||||
|
@ -136,7 +130,7 @@ export async function AddEmployee(req: Request, res: Response) {
|
|||
user_id: string;
|
||||
store_id: any;
|
||||
role: string;
|
||||
account_name: any;
|
||||
email: any;
|
||||
username: any;
|
||||
calendar_max_future_booking_days: any;
|
||||
calendar_min_earliest_booking_time: any;
|
||||
|
@ -193,7 +187,7 @@ export async function GetEmployees(req: Request, res: Response) {
|
|||
return res.status(401).send({ err: "unauthorized" });
|
||||
}
|
||||
|
||||
// find all employees of the requester and select only the username and account name
|
||||
// find all employees of the requester
|
||||
|
||||
const employees = await User.findAll({
|
||||
where: {
|
||||
|
@ -202,7 +196,7 @@ export async function GetEmployees(req: Request, res: Response) {
|
|||
attributes: [
|
||||
"user_id",
|
||||
"username",
|
||||
"account_name",
|
||||
"email",
|
||||
"calendar_max_future_booking_days",
|
||||
"calendar_min_earliest_booking_time",
|
||||
],
|
||||
|
@ -234,13 +228,11 @@ export async function UpdateEmployee(req: Request, res: Response) {
|
|||
let {
|
||||
userId,
|
||||
username,
|
||||
accountName,
|
||||
email,
|
||||
calendarMaxFutureBookingDays,
|
||||
calendarMinEarliestBookingTime,
|
||||
} = req.body;
|
||||
|
||||
// validate username and account name
|
||||
|
||||
if (!isUserIdValid(userId)) {
|
||||
return res.status(400).send({ err: "invalid request" });
|
||||
}
|
||||
|
@ -282,17 +274,17 @@ export async function UpdateEmployee(req: Request, res: Response) {
|
|||
|
||||
let update = {};
|
||||
|
||||
if (accountName) {
|
||||
accountName = accountName.toLowerCase();
|
||||
if (email) {
|
||||
email = email.toLowerCase();
|
||||
|
||||
if (!(await isAccountNameValid(accountName))) {
|
||||
if (!(await isEmailValid(email))) {
|
||||
res.status(400).send({ err: "invalid request" });
|
||||
return;
|
||||
}
|
||||
|
||||
update = {
|
||||
...update,
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Request, Response } from "express";
|
||||
/*import { Request, Response } from "express";
|
||||
import logger from "../logger/logger";
|
||||
import util from "util";
|
||||
import { exec } from "child_process";
|
||||
|
@ -159,3 +159,4 @@ export async function GetWebsite(req: Request, res: Response) {
|
|||
res.status(500).send({ error: "invalid request" });
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { DataTypes, Model } from "sequelize";
|
||||
import sequelize from "../database/database";
|
||||
|
||||
interface EmailVerificationAttributes {
|
||||
email_verification_id: string; // code that is sent to the user's email
|
||||
user_id: string;
|
||||
state: number; // to know for what the code is used for (like password reset, email verification, etc)
|
||||
}
|
||||
|
||||
class EmailVerification
|
||||
extends Model<EmailVerificationAttributes>
|
||||
implements EmailVerificationAttributes
|
||||
{
|
||||
declare email_verification_id: string;
|
||||
declare user_id: string;
|
||||
declare state: number;
|
||||
}
|
||||
|
||||
EmailVerification.init(
|
||||
{
|
||||
// Model attributes are defined here
|
||||
email_verification_id: {
|
||||
primaryKey: true,
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
user_id: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
state: {
|
||||
type: DataTypes.TINYINT,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
tableName: "email_verification",
|
||||
sequelize, // passing the `sequelize` instance is required
|
||||
createdAt: "created_at",
|
||||
updatedAt: "updated_at",
|
||||
}
|
||||
);
|
||||
|
||||
export default EmailVerification;
|
|
@ -1,4 +1,5 @@
|
|||
import Feedback from "./feedback";
|
||||
import EmailVerification from "./emailVerification";
|
||||
import Session from "./session";
|
||||
import Store from "./store";
|
||||
import StoreService from "./storeService";
|
||||
|
@ -8,6 +9,7 @@ import User from "./user";
|
|||
import Website from "./website";
|
||||
|
||||
function syncModels() {
|
||||
EmailVerification.sync();
|
||||
User.sync();
|
||||
Session.sync();
|
||||
Store.sync();
|
||||
|
|
|
@ -6,7 +6,7 @@ interface UserAttributes {
|
|||
store_id: string;
|
||||
// TODO: change to role_id
|
||||
role: string;
|
||||
account_name: string;
|
||||
email: string;
|
||||
username: string;
|
||||
password?: string; // can be null if user is created by store owner - password is set by user when they first login
|
||||
calendar_max_future_booking_days?: number;
|
||||
|
@ -21,7 +21,7 @@ class User extends Model<UserAttributes> implements UserAttributes {
|
|||
declare user_id: string;
|
||||
declare store_id: string;
|
||||
declare role: string;
|
||||
declare account_name: string;
|
||||
declare email: string;
|
||||
declare username: string;
|
||||
declare password: string;
|
||||
declare calendar_max_future_booking_days: number;
|
||||
|
@ -49,7 +49,7 @@ User.init(
|
|||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
account_name: {
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
unique: true,
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import amqplib from "amqplib";
|
||||
|
||||
export let channel: amqplib.Channel;
|
||||
let channel: amqplib.Channel;
|
||||
|
||||
async function connect(
|
||||
RABBITMQ_USERNAME: string,
|
||||
|
@ -13,16 +13,45 @@ async function connect(
|
|||
);
|
||||
|
||||
if (!open) {
|
||||
console.log("RabbitMQ connection failed");
|
||||
console.log("⚡️[RabbitMQ]: connection failed");
|
||||
return;
|
||||
}
|
||||
|
||||
channel = await open.createChannel();
|
||||
|
||||
if (!channel) {
|
||||
console.log("RabbitMQ channel failed");
|
||||
console.log("⚡️[RabbitMQ]: channel failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect;
|
||||
async function sendEmail(
|
||||
email: string,
|
||||
templateId: string,
|
||||
languageId: string,
|
||||
bodyData: any
|
||||
) {
|
||||
if (!channel) {
|
||||
console.log("⚡️[RabbitMQ]: channel not available");
|
||||
return;
|
||||
}
|
||||
|
||||
channel.sendToQueue(
|
||||
process.env.RABBITMQ_MAIL_QUEUE as string,
|
||||
Buffer.from(
|
||||
JSON.stringify({
|
||||
m: email,
|
||||
t: templateId,
|
||||
l: languageId,
|
||||
b: bodyData,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const rabbitmq = {
|
||||
connect,
|
||||
sendEmail,
|
||||
};
|
||||
|
||||
export default rabbitmq;
|
||||
|
|
|
@ -6,6 +6,10 @@ import logger from "../logger/logger";
|
|||
import Session from "../models/session";
|
||||
|
||||
import * as calendarController from "../controllers/calendarController";
|
||||
import {
|
||||
PASSPORT_FAILURE_REDIRECT_URL,
|
||||
PASSPORT_SUCCESS_REDIRECT_URL,
|
||||
} from "../utils/constants";
|
||||
|
||||
router.get(
|
||||
"/auth/google",
|
||||
|
@ -24,7 +28,7 @@ router.get(
|
|||
router.get(
|
||||
"/auth/google/callback",
|
||||
passport.authenticate("google", {
|
||||
failureRedirect: process.env.PASSPORT_FAILURE_REDIRECT_URL as string,
|
||||
failureRedirect: PASSPORT_FAILURE_REDIRECT_URL,
|
||||
}),
|
||||
function (req, res) {
|
||||
// Successful authentication, redirect home.
|
||||
|
@ -38,7 +42,7 @@ router.get(
|
|||
|
||||
if (!sessionId) {
|
||||
logger.error("session cookie not found");
|
||||
res.redirect(process.env.PASSPORT_FAILURE_REDIRECT_URL as string);
|
||||
res.redirect(PASSPORT_FAILURE_REDIRECT_URL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -50,7 +54,7 @@ router.get(
|
|||
.then((userSession) => {
|
||||
if (!userSession) {
|
||||
logger.error("user session not found");
|
||||
res.redirect(process.env.PASSPORT_FAILURE_REDIRECT_URL as string);
|
||||
res.redirect(PASSPORT_FAILURE_REDIRECT_URL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,11 +74,11 @@ router.get(
|
|||
logger.info("err %s", err);
|
||||
});
|
||||
|
||||
res.redirect(process.env.PASSPORT_SUCCESS_REDIRECT_URL as string);
|
||||
res.redirect(PASSPORT_SUCCESS_REDIRECT_URL);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err);
|
||||
res.redirect(process.env.PASSPORT_FAILURE_REDIRECT_URL as string);
|
||||
res.redirect(PASSPORT_FAILURE_REDIRECT_URL);
|
||||
});
|
||||
|
||||
// /api/v1/addGoogleAccount
|
||||
|
|
|
@ -8,7 +8,7 @@ router.post("/auth/signup", userController.SignUp);
|
|||
router.post("/auth/login", userController.Login);
|
||||
router.delete("/auth/logout", sessionProtection, userController.Logout);
|
||||
router.get("/", sessionProtection, userController.GetUser);
|
||||
router.post("/auth/check/accountname", userController.IsAccountNameAvailable);
|
||||
router.post("/auth/check/email", userController.IsEmailAvailable);
|
||||
router.get(
|
||||
"/profile/settings",
|
||||
sessionProtection,
|
||||
|
@ -45,5 +45,6 @@ router.post(
|
|||
userController.ExportUserAccount
|
||||
);
|
||||
router.get("/profile/export/:id", userController.GetExportedUserAccount);
|
||||
router.get("/verify/:state/:emailVerificationId", userController.VerifyEmail);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import express from "express";
|
||||
/*import express from "express";
|
||||
const router = express.Router();
|
||||
|
||||
import * as websiteController from "../controllers/websiteController";
|
||||
|
@ -11,3 +11,4 @@ router.post("/", websiteController.CreateWebsite);
|
|||
router.get("/:storeId", websiteController.GetWebsite);
|
||||
|
||||
export default router;
|
||||
*/
|
||||
|
|
|
@ -5,8 +5,9 @@ export const USER_SESSION_LENGTH = 32;
|
|||
export const USERNAME_MIN_LENGTH = 3;
|
||||
export const USERNAME_MAX_LENGTH = 20;
|
||||
|
||||
export const ACCOUNT_NAME_MIN_LENGTH = 3;
|
||||
export const ACCOUNT_NAME_MAX_LENGTH = 20;
|
||||
export const EMAIL_MIN_LENGTH = 3;
|
||||
export const EMAIL_MAX_LENGTH = 64;
|
||||
export const EMAIL_REGEX = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
|
||||
|
||||
export const PASSWORD_MIN_LENGTH = 8;
|
||||
export const PASSWORD_MAX_LENGTH = 64;
|
||||
|
@ -51,6 +52,7 @@ export enum ACCOUNT_STATE {
|
|||
PENDING_DELETION = 1, // account still exists but is marked as deleted, can be restored or will be deleted after a certain time
|
||||
INIT_LOGIN = 2, // account is created but password is not set yet
|
||||
BANNED = 3, // account is banned, cannot login
|
||||
PENDING_EMAIL_VERIFICATION = 4, // account is created but email is not verified yet
|
||||
}
|
||||
|
||||
export const FEEDBACK_MIN_LENGTH = 3;
|
||||
|
@ -69,3 +71,13 @@ export const Roles = {
|
|||
export const ACCOUNT_DEMO_DAYS = Number(
|
||||
process.env.CONSTANTS_ACCOUNT_DEMO_DAYS
|
||||
); // how many days a demo account is valid until payment is required or account will be deleted
|
||||
|
||||
export enum EMAIL_VERIFICATION_STATE {
|
||||
PENDING_EMAIL_VERIFICATION = 0, // account is created but email is not verified yet
|
||||
}
|
||||
|
||||
export const DASHBOARD_URL = process.env.DASHBOARD_URL as string;
|
||||
export const GOOGLE_CALLBACK_URL = `${process.env.GOOGLE_CALLBACK_URL}/v1/calendar/auth/google/callback`;
|
||||
export const PASSPORT_FAILURE_REDIRECT_URL = `${process.env.DASHBOARD_URL}/store/calendar/auth/failed`;
|
||||
export const PASSPORT_SUCCESS_REDIRECT_URL = `${process.env.DASHBOARD_URL}/store/calendar/auth/finish`;
|
||||
export const ACCOUNT_EXPORT_URL = `${process.env.DASHBOARD_API_URL}/v1/user/profile/export/`;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { v4 as uuidv4 } from "uuid";
|
|||
import { Request, Response } from "express";
|
||||
import Session from "../models/session";
|
||||
import {
|
||||
DASHBOARD_URL,
|
||||
DEFAULT_SESSION_EXPIRY,
|
||||
HEADER_X_AUTHORIZATION,
|
||||
SESSION_EXPIRY_NOT_REMEMBER_ME,
|
||||
|
@ -42,6 +43,10 @@ export function newUserSession() {
|
|||
return crypto.randomBytes(USER_SESSION_LENGTH).toString("hex");
|
||||
}
|
||||
|
||||
export function newEmailVerificationId() {
|
||||
return crypto.randomBytes(64).toString("hex");
|
||||
}
|
||||
|
||||
export function newSessionId() {
|
||||
return uuidv4();
|
||||
}
|
||||
|
@ -57,6 +62,7 @@ export function newAccountExportId() {
|
|||
export async function saveSession(
|
||||
req: Request,
|
||||
res: Response,
|
||||
sendResponseData: boolean,
|
||||
userId: string,
|
||||
username: string,
|
||||
rememberMe: boolean
|
||||
|
@ -77,12 +83,15 @@ export async function saveSession(
|
|||
),
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
XAuthorization: userSession,
|
||||
Username: username,
|
||||
});
|
||||
if (sendResponseData) {
|
||||
res.status(200).json({
|
||||
XAuthorization: userSession,
|
||||
Username: username,
|
||||
});
|
||||
} else {
|
||||
res.status(200).send({ msg: "success" });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(500).send({ err: "invalid request" });
|
||||
}
|
||||
}
|
||||
|
@ -100,3 +109,10 @@ export async function getUserSession(req: Request) {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getEmailVerificationUrl(
|
||||
state: string,
|
||||
emailVerificationId: string
|
||||
) {
|
||||
return `${DASHBOARD_URL}/verify/${state}/${emailVerificationId}`;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ import {
|
|||
USERNAME_MAX_LENGTH,
|
||||
PASSWORD_MIN_LENGTH,
|
||||
PASSWORD_MAX_LENGTH,
|
||||
ACCOUNT_NAME_MIN_LENGTH,
|
||||
ACCOUNT_NAME_MAX_LENGTH,
|
||||
USER_ID_LENGTH,
|
||||
STORE_SERVICE_MIN_LENGTH,
|
||||
STORE_SERVICE_MAX_LENGTH,
|
||||
|
@ -20,6 +18,9 @@ import {
|
|||
FEEDBACK_MIN_LENGTH,
|
||||
COMPANY_NAME_MIN_LENGTH,
|
||||
COMPANY_NAME_MAX_LENGTH,
|
||||
EMAIL_MIN_LENGTH,
|
||||
EMAIL_MAX_LENGTH,
|
||||
EMAIL_REGEX,
|
||||
} from "../utils/constants";
|
||||
import User from "../models/user";
|
||||
|
||||
|
@ -31,20 +32,21 @@ export function isUsernameValid(username: string) {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: regex for account name
|
||||
export async function isAccountNameValid(accountName: string) {
|
||||
// TODO: regex for email
|
||||
export async function isEmailValid(email: string) {
|
||||
if (
|
||||
accountName.length < ACCOUNT_NAME_MIN_LENGTH ||
|
||||
accountName.length > ACCOUNT_NAME_MAX_LENGTH
|
||||
email.length < EMAIL_MIN_LENGTH ||
|
||||
email.length > EMAIL_MAX_LENGTH ||
|
||||
!EMAIL_REGEX.test(email)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if account name is already taken in the database
|
||||
// check if email is already taken in the database
|
||||
|
||||
const existingUser = await User.findOne({
|
||||
where: {
|
||||
account_name: accountName,
|
||||
email: email,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -63,11 +65,6 @@ export function isPasswordValid(password: string) {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: regex for email
|
||||
export function isEmailValid(email: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isUserIdValid(userId: string) {
|
||||
return userId.length === USER_ID_LENGTH;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue