mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
Compare commits
1 Commits
big-163-us
...
backup-scr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
341bcbea7d |
@@ -48,9 +48,6 @@ SIGNUP_DISABLED=false
|
|||||||
SIGNUP_ALLOWED_DOMAINS=
|
SIGNUP_ALLOWED_DOMAINS=
|
||||||
SIGNUP_ALLOWED_EMAILS=
|
SIGNUP_ALLOWED_EMAILS=
|
||||||
|
|
||||||
# Sign-up Email Confirmation
|
|
||||||
SIGNUP_EMAIL_CONFIRMATION=false
|
|
||||||
|
|
||||||
# API rate limit (points,duration,block duration).
|
# API rate limit (points,duration,block duration).
|
||||||
API_RATE_LIMIT=120,60,600
|
API_RATE_LIMIT=120,60,600
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
|||||||
import LoginThrottlerMiddleware from '@/api/middleware/LoginThrottlerMiddleware';
|
import LoginThrottlerMiddleware from '@/api/middleware/LoginThrottlerMiddleware';
|
||||||
import AuthenticationApplication from '@/services/Authentication/AuthApplication';
|
import AuthenticationApplication from '@/services/Authentication/AuthApplication';
|
||||||
|
|
||||||
import JWTAuth from '@/api/middleware/jwtAuth';
|
|
||||||
import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser';
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationController extends BaseController {
|
export default class AuthenticationController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -30,20 +28,6 @@ export default class AuthenticationController extends BaseController {
|
|||||||
asyncMiddleware(this.login.bind(this)),
|
asyncMiddleware(this.login.bind(this)),
|
||||||
this.handlerErrors
|
this.handlerErrors
|
||||||
);
|
);
|
||||||
router.use('/register/verify/resend', JWTAuth);
|
|
||||||
router.use('/register/verify/resend', AttachCurrentTenantUser);
|
|
||||||
router.post(
|
|
||||||
'/register/verify/resend',
|
|
||||||
asyncMiddleware(this.registerVerifyResendMail.bind(this)),
|
|
||||||
this.handlerErrors
|
|
||||||
);
|
|
||||||
router.post(
|
|
||||||
'/register/verify',
|
|
||||||
this.signupVerifySchema,
|
|
||||||
this.validationResult,
|
|
||||||
asyncMiddleware(this.registerVerify.bind(this)),
|
|
||||||
this.handlerErrors
|
|
||||||
);
|
|
||||||
router.post(
|
router.post(
|
||||||
'/register',
|
'/register',
|
||||||
this.registerSchema,
|
this.registerSchema,
|
||||||
@@ -115,17 +99,6 @@ export default class AuthenticationController extends BaseController {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private get signupVerifySchema(): ValidationChain[] {
|
|
||||||
return [
|
|
||||||
check('email')
|
|
||||||
.exists()
|
|
||||||
.isString()
|
|
||||||
.isEmail()
|
|
||||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
|
||||||
check('token').exists().isString(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset password schema.
|
* Reset password schema.
|
||||||
* @returns {ValidationChain[]}
|
* @returns {ValidationChain[]}
|
||||||
@@ -193,58 +166,6 @@ export default class AuthenticationController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the provider user's email after signin-up.
|
|
||||||
* @param {Request} req
|
|
||||||
* @param {Response}| res
|
|
||||||
* @param {Function} next
|
|
||||||
* @returns {Response|void}
|
|
||||||
*/
|
|
||||||
private async registerVerify(req: Request, res: Response, next: Function) {
|
|
||||||
const signUpVerifyDTO: { email: string; token: string } =
|
|
||||||
this.matchedBodyData(req);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const user = await this.authApplication.signUpConfirm(
|
|
||||||
signUpVerifyDTO.email,
|
|
||||||
signUpVerifyDTO.token
|
|
||||||
);
|
|
||||||
return res.status(200).send({
|
|
||||||
type: 'success',
|
|
||||||
message: 'The given user has verified successfully',
|
|
||||||
user,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resends the confirmation email to the user.
|
|
||||||
* @param {Request} req
|
|
||||||
* @param {Response}| res
|
|
||||||
* @param {Function} next
|
|
||||||
*/
|
|
||||||
private async registerVerifyResendMail(
|
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: Function
|
|
||||||
) {
|
|
||||||
const { user } = req;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await this.authApplication.signUpConfirmResend(user.id);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
type: 'success',
|
|
||||||
message: 'The given user has verified successfully',
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send reset password handler
|
* Send reset password handler
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
|
|||||||
@@ -153,13 +153,6 @@ module.exports = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign-up email confirmation
|
|
||||||
*/
|
|
||||||
signupConfirmation: {
|
|
||||||
enabled: parseBoolean<boolean>(process.env.SIGNUP_EMAIL_CONFIRMATION, false),
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puppeteer remote browserless connection.
|
* Puppeteer remote browserless connection.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -66,27 +66,16 @@ export interface IAuthResetedPasswordEventPayload {
|
|||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface IAuthSendingResetPassword {
|
export interface IAuthSendingResetPassword {
|
||||||
user: ISystemUser;
|
user: ISystemUser,
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
export interface IAuthSendedResetPassword {
|
export interface IAuthSendedResetPassword {
|
||||||
user: ISystemUser;
|
user: ISystemUser,
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAuthGetMetaPOJO {
|
export interface IAuthGetMetaPOJO {
|
||||||
signupDisabled: boolean;
|
signupDisabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAuthSignUpVerifingEventPayload {
|
|
||||||
email: string;
|
|
||||||
verifyToken: string;
|
|
||||||
userId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAuthSignUpVerifiedEventPayload {
|
|
||||||
email: string;
|
|
||||||
verifyToken: string;
|
|
||||||
userId: number;
|
|
||||||
}
|
|
||||||
@@ -91,7 +91,6 @@ import { SaleEstimateMarkApprovedOnMailSent } from '@/services/Sales/Estimates/s
|
|||||||
import { DeleteCashflowTransactionOnUncategorize } from '@/services/Cashflow/subscribers/DeleteCashflowTransactionOnUncategorize';
|
import { DeleteCashflowTransactionOnUncategorize } from '@/services/Cashflow/subscribers/DeleteCashflowTransactionOnUncategorize';
|
||||||
import { PreventDeleteTransactionOnDelete } from '@/services/Cashflow/subscribers/PreventDeleteTransactionsOnDelete';
|
import { PreventDeleteTransactionOnDelete } from '@/services/Cashflow/subscribers/PreventDeleteTransactionsOnDelete';
|
||||||
import { SubscribeFreeOnSignupCommunity } from '@/services/Subscription/events/SubscribeFreeOnSignupCommunity';
|
import { SubscribeFreeOnSignupCommunity } from '@/services/Subscription/events/SubscribeFreeOnSignupCommunity';
|
||||||
import { SendVerfiyMailOnSignUp } from '@/services/Authentication/events/SendVerfiyMailOnSignUp';
|
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
@@ -223,7 +222,6 @@ export const susbcribers = () => {
|
|||||||
DeleteCashflowTransactionOnUncategorize,
|
DeleteCashflowTransactionOnUncategorize,
|
||||||
PreventDeleteTransactionOnDelete,
|
PreventDeleteTransactionOnDelete,
|
||||||
|
|
||||||
SubscribeFreeOnSignupCommunity,
|
SubscribeFreeOnSignupCommunity
|
||||||
SendVerfiyMailOnSignUp
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { SaleReceiptMailNotificationJob } from '@/services/Sales/Receipts/SaleRe
|
|||||||
import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob';
|
import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob';
|
||||||
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
||||||
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
||||||
import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob';
|
|
||||||
|
|
||||||
export default ({ agenda }: { agenda: Agenda }) => {
|
export default ({ agenda }: { agenda: Agenda }) => {
|
||||||
new ResetPasswordMailJob(agenda);
|
new ResetPasswordMailJob(agenda);
|
||||||
@@ -28,7 +27,6 @@ export default ({ agenda }: { agenda: Agenda }) => {
|
|||||||
new PaymentReceiveMailNotificationJob(agenda);
|
new PaymentReceiveMailNotificationJob(agenda);
|
||||||
new PlaidFetchTransactionsJob(agenda);
|
new PlaidFetchTransactionsJob(agenda);
|
||||||
new ImportDeleteExpiredFilesJobs(agenda);
|
new ImportDeleteExpiredFilesJobs(agenda);
|
||||||
new SendVerifyMailJob(agenda);
|
|
||||||
|
|
||||||
agenda.start().then(() => {
|
agenda.start().then(() => {
|
||||||
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject, Container } from 'typedi';
|
||||||
import {
|
import {
|
||||||
IRegisterDTO,
|
IRegisterDTO,
|
||||||
ISystemUser,
|
ISystemUser,
|
||||||
@@ -9,9 +9,6 @@ import { AuthSigninService } from './AuthSignin';
|
|||||||
import { AuthSignupService } from './AuthSignup';
|
import { AuthSignupService } from './AuthSignup';
|
||||||
import { AuthSendResetPassword } from './AuthSendResetPassword';
|
import { AuthSendResetPassword } from './AuthSendResetPassword';
|
||||||
import { GetAuthMeta } from './GetAuthMeta';
|
import { GetAuthMeta } from './GetAuthMeta';
|
||||||
import { AuthSignupConfirmService } from './AuthSignupConfirm';
|
|
||||||
import { SystemUser } from '@/system/models';
|
|
||||||
import { AuthSignupConfirmResend } from './AuthSignupResend';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationApplication {
|
export default class AuthenticationApplication {
|
||||||
@@ -21,12 +18,6 @@ export default class AuthenticationApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private authSignupService: AuthSignupService;
|
private authSignupService: AuthSignupService;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private authSignupConfirmService: AuthSignupConfirmService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private authSignUpConfirmResendService: AuthSignupConfirmResend;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private authResetPasswordService: AuthSendResetPassword;
|
private authResetPasswordService: AuthSendResetPassword;
|
||||||
|
|
||||||
@@ -53,28 +44,6 @@ export default class AuthenticationApplication {
|
|||||||
return this.authSignupService.signUp(signupDTO);
|
return this.authSignupService.signUp(signupDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verfying the provided user's email after signin-up.
|
|
||||||
* @param {string} email
|
|
||||||
* @param {string} token
|
|
||||||
* @returns {Promise<SystemUser>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirm(
|
|
||||||
email: string,
|
|
||||||
token: string
|
|
||||||
): Promise<SystemUser> {
|
|
||||||
return this.authSignupConfirmService.signUpConfirm(email, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resends the confirmation email of the given system user.
|
|
||||||
* @param {number} userId - System user id.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirmResend(userId: number) {
|
|
||||||
return this.authSignUpConfirmResendService.signUpConfirmResend(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates and retrieve password reset token for the given user email.
|
* Generates and retrieve password reset token for the given user email.
|
||||||
* @param {string} email
|
* @param {string} email
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { defaultTo, isEmpty, omit } from 'lodash';
|
import { isEmpty, omit } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import crypto from 'crypto';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import {
|
import {
|
||||||
IAuthSignedUpEventPayload,
|
IAuthSignedUpEventPayload,
|
||||||
@@ -43,13 +42,6 @@ export class AuthSignupService {
|
|||||||
|
|
||||||
const hashedPassword = await hashPassword(signupDTO.password);
|
const hashedPassword = await hashPassword(signupDTO.password);
|
||||||
|
|
||||||
const verifyTokenCrypto = crypto.randomBytes(64).toString('hex');
|
|
||||||
const verifiedEnabed = defaultTo(config.signupConfirmation.enabled, false);
|
|
||||||
const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
|
|
||||||
const verified = !verifiedEnabed;
|
|
||||||
|
|
||||||
const inviteAcceptedAt = moment().format('YYYY-MM-DD');
|
|
||||||
|
|
||||||
// Triggers signin up event.
|
// Triggers signin up event.
|
||||||
await this.eventPublisher.emitAsync(events.auth.signingUp, {
|
await this.eventPublisher.emitAsync(events.auth.signingUp, {
|
||||||
signupDTO,
|
signupDTO,
|
||||||
@@ -58,12 +50,10 @@ export class AuthSignupService {
|
|||||||
const tenant = await this.tenantsManager.createTenant();
|
const tenant = await this.tenantsManager.createTenant();
|
||||||
const registeredUser = await systemUserRepository.create({
|
const registeredUser = await systemUserRepository.create({
|
||||||
...omit(signupDTO, 'country'),
|
...omit(signupDTO, 'country'),
|
||||||
verifyToken,
|
|
||||||
verified,
|
|
||||||
active: true,
|
active: true,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
tenantId: tenant.id,
|
tenantId: tenant.id,
|
||||||
inviteAcceptedAt,
|
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
|
||||||
});
|
});
|
||||||
// Triggers signed up event.
|
// Triggers signed up event.
|
||||||
await this.eventPublisher.emitAsync(events.auth.signUp, {
|
await this.eventPublisher.emitAsync(events.auth.signUp, {
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { SystemUser } from '@/system/models';
|
|
||||||
import { ERRORS } from './_constants';
|
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import {
|
|
||||||
IAuthSignUpVerifiedEventPayload,
|
|
||||||
IAuthSignUpVerifingEventPayload,
|
|
||||||
} from '@/interfaces';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AuthSignupConfirmService {
|
|
||||||
@Inject()
|
|
||||||
private eventPublisher: EventPublisher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the provided user's email after signing-up.
|
|
||||||
* @throws {ServiceErrors}
|
|
||||||
* @param {IRegisterDTO} signupDTO
|
|
||||||
* @returns {Promise<ISystemUser>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirm(
|
|
||||||
email: string,
|
|
||||||
verifyToken: string
|
|
||||||
): Promise<SystemUser> {
|
|
||||||
const foundUser = await SystemUser.query().findOne({ email, verifyToken });
|
|
||||||
|
|
||||||
if (!foundUser) {
|
|
||||||
throw new ServiceError(ERRORS.SIGNUP_CONFIRM_TOKEN_INVALID);
|
|
||||||
}
|
|
||||||
const userId = foundUser.id;
|
|
||||||
|
|
||||||
// Triggers `signUpConfirming` event.
|
|
||||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirming, {
|
|
||||||
email,
|
|
||||||
verifyToken,
|
|
||||||
userId,
|
|
||||||
} as IAuthSignUpVerifingEventPayload);
|
|
||||||
|
|
||||||
const updatedUser = await SystemUser.query().patchAndFetchById(
|
|
||||||
foundUser.id,
|
|
||||||
{
|
|
||||||
verified: true,
|
|
||||||
verifyToken: '',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// Triggers `signUpConfirmed` event.
|
|
||||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirmed, {
|
|
||||||
email,
|
|
||||||
verifyToken,
|
|
||||||
userId,
|
|
||||||
} as IAuthSignUpVerifiedEventPayload);
|
|
||||||
|
|
||||||
return updatedUser as SystemUser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { SystemUser } from '@/system/models';
|
|
||||||
import { ERRORS } from './_constants';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AuthSignupConfirmResend {
|
|
||||||
@Inject('agenda')
|
|
||||||
private agenda: any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resends the email confirmation of the given user.
|
|
||||||
* @param {number} userId - User ID.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirmResend(userId: number) {
|
|
||||||
const user = await SystemUser.query().findById(userId).throwIfNotFound();
|
|
||||||
|
|
||||||
// Throw error if the user is already verified.
|
|
||||||
if (user.verified) {
|
|
||||||
throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
|
|
||||||
}
|
|
||||||
// Throw error if the verification token is not exist.
|
|
||||||
if (!user.verifyToken) {
|
|
||||||
throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
|
|
||||||
}
|
|
||||||
const payload = {
|
|
||||||
email: user.email,
|
|
||||||
token: user.verifyToken,
|
|
||||||
fullName: user.firstName,
|
|
||||||
};
|
|
||||||
await this.agenda.now('send-signup-verify-mail', payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,33 +33,4 @@ export default class AuthenticationMailMesssages {
|
|||||||
})
|
})
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends signup verification mail.
|
|
||||||
* @param {string} email - Email address
|
|
||||||
* @param {string} fullName - User name.
|
|
||||||
* @param {string} token - Verification token.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async sendSignupVerificationMail(
|
|
||||||
email: string,
|
|
||||||
fullName: string,
|
|
||||||
token: string
|
|
||||||
) {
|
|
||||||
const verifyUrl = `${config.baseURL}/auth/email_confirmation?token=${token}&email=${email}`;
|
|
||||||
|
|
||||||
await new Mail()
|
|
||||||
.setSubject('Bigcapital - Verify your email')
|
|
||||||
.setView('mail/SignupVerifyEmail.html')
|
|
||||||
.setTo(email)
|
|
||||||
.setAttachments([
|
|
||||||
{
|
|
||||||
filename: 'bigcapital.png',
|
|
||||||
path: `${global.__views_dir}/images/bigcapital.png`,
|
|
||||||
cid: 'bigcapital_logo',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.setData({ verifyUrl, fullName })
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,4 @@ export const ERRORS = {
|
|||||||
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
||||||
SIGNUP_RESTRICTED_NOT_ALLOWED: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
SIGNUP_RESTRICTED_NOT_ALLOWED: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
||||||
SIGNUP_RESTRICTED: 'SIGNUP_RESTRICTED',
|
SIGNUP_RESTRICTED: 'SIGNUP_RESTRICTED',
|
||||||
SIGNUP_CONFIRM_TOKEN_INVALID: 'SIGNUP_CONFIRM_TOKEN_INVALID',
|
|
||||||
USER_ALREADY_VERIFIED: 'USER_ALREADY_VERIFIED',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { IAuthSignedUpEventPayload } from '@/interfaces';
|
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { Inject } from 'typedi';
|
|
||||||
|
|
||||||
export class SendVerfiyMailOnSignUp {
|
|
||||||
@Inject('agenda')
|
|
||||||
private agenda: any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attaches events with handles.
|
|
||||||
*/
|
|
||||||
public attach(bus) {
|
|
||||||
bus.subscribe(events.auth.signUp, this.handleSendVerifyMailOnSignup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {ITaxRateEditedPayload} payload -
|
|
||||||
*/
|
|
||||||
private handleSendVerifyMailOnSignup = async ({
|
|
||||||
user,
|
|
||||||
}: IAuthSignedUpEventPayload) => {
|
|
||||||
const payload = {
|
|
||||||
email: user.email,
|
|
||||||
token: user.verifyToken,
|
|
||||||
fullName: user.firstName,
|
|
||||||
};
|
|
||||||
await this.agenda.now('send-signup-verify-mail', payload);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { Container } from 'typedi';
|
|
||||||
import AuthenticationMailMesssages from '@/services/Authentication/AuthenticationMailMessages';
|
|
||||||
|
|
||||||
export class SendVerifyMailJob {
|
|
||||||
/**
|
|
||||||
* Constructor method.
|
|
||||||
* @param {Agenda} agenda
|
|
||||||
*/
|
|
||||||
constructor(agenda) {
|
|
||||||
agenda.define(
|
|
||||||
'send-signup-verify-mail',
|
|
||||||
{ priority: 'high' },
|
|
||||||
this.handler.bind(this)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle send welcome mail job.
|
|
||||||
* @param {Job} job
|
|
||||||
* @param {Function} done
|
|
||||||
*/
|
|
||||||
public async handler(job, done: Function): Promise<void> {
|
|
||||||
const { data } = job.attrs;
|
|
||||||
const { email, fullName, token } = data;
|
|
||||||
const authService = Container.get(AuthenticationMailMesssages);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await authService.sendSignupVerificationMail(email, fullName, token);
|
|
||||||
done();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
done(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,6 @@ export default {
|
|||||||
signUp: 'onSignUp',
|
signUp: 'onSignUp',
|
||||||
signingUp: 'onSigningUp',
|
signingUp: 'onSigningUp',
|
||||||
|
|
||||||
signUpConfirming: 'signUpConfirming',
|
|
||||||
signUpConfirmed: 'signUpConfirmed',
|
|
||||||
|
|
||||||
sendingResetPassword: 'onSendingResetPassword',
|
sendingResetPassword: 'onSendingResetPassword',
|
||||||
sendResetPassword: 'onSendResetPassword',
|
sendResetPassword: 'onSendResetPassword',
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
exports.up = function (knex) {
|
|
||||||
return knex.schema
|
|
||||||
.table('users', (table) => {
|
|
||||||
table.string('verify_token');
|
|
||||||
table.boolean('verified').defaultTo(false);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return knex('USERS').update({ verified: true });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = (knex) => {};
|
|
||||||
@@ -4,12 +4,6 @@ import SystemModel from '@/system/models/SystemModel';
|
|||||||
import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
|
import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
|
||||||
|
|
||||||
export default class SystemUser extends SystemModel {
|
export default class SystemUser extends SystemModel {
|
||||||
firstName!: string;
|
|
||||||
lastName!: string;
|
|
||||||
verified!: boolean;
|
|
||||||
inviteAcceptedAt!: Date | null;
|
|
||||||
deletedAt!: Date | null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -35,33 +29,23 @@ export default class SystemUser extends SystemModel {
|
|||||||
* Virtual attributes.
|
* Virtual attributes.
|
||||||
*/
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['fullName', 'isDeleted', 'isInviteAccepted', 'isVerified'];
|
return ['fullName', 'isDeleted', 'isInviteAccepted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the user is deleted.
|
*
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
*/
|
||||||
get isDeleted() {
|
get isDeleted() {
|
||||||
return !!this.deletedAt;
|
return !!this.deletedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the sent invite is accepted.
|
*
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
*/
|
||||||
get isInviteAccepted() {
|
get isInviteAccepted() {
|
||||||
return !!this.inviteAcceptedAt;
|
return !!this.inviteAcceptedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Detarmines whether the user's email is verified.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get isVerified() {
|
|
||||||
return !!this.verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full name attribute.
|
* Full name attribute.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import _, { isEmpty } from 'lodash';
|
import _ from 'lodash';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ const booleanValuesRepresentingTrue: string[] = ['true', '1'];
|
|||||||
const booleanValuesRepresentingFalse: string[] = ['false', '0'];
|
const booleanValuesRepresentingFalse: string[] = ['false', '0'];
|
||||||
|
|
||||||
const normalizeValue = (value: any): string =>
|
const normalizeValue = (value: any): string =>
|
||||||
value?.toString().trim().toLowerCase();
|
value.toString().trim().toLowerCase();
|
||||||
|
|
||||||
const booleanValues: string[] = [
|
const booleanValues: string[] = [
|
||||||
...booleanValuesRepresentingTrue,
|
...booleanValuesRepresentingTrue,
|
||||||
@@ -338,7 +338,7 @@ const booleanValues: string[] = [
|
|||||||
|
|
||||||
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
||||||
const normalizedValue = normalizeValue(value);
|
const normalizedValue = normalizeValue(value);
|
||||||
if (isEmpty(value) || booleanValues.indexOf(normalizedValue) === -1) {
|
if (booleanValues.indexOf(normalizedValue) === -1) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
||||||
|
|||||||
@@ -1,424 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<title>Bigcapital | Reset your password</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
|
|
||||||
/*All the styling goes here*/
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
}
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #2d95fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #1968F0;
|
|
||||||
border-color: #1968F0;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb4{
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important;
|
|
||||||
}
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important;
|
|
||||||
}
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important;
|
|
||||||
}
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important;
|
|
||||||
}
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%;
|
|
||||||
}
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
}
|
|
||||||
#MessageViewBody a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: inherit;
|
|
||||||
font-family: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #004dd0 !important;
|
|
||||||
}
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #004dd0 !important;
|
|
||||||
border-color: #004dd0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[data-icon="bigcapital"] path {
|
|
||||||
fill: #004dd0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-icon='bigcapital'] .path-1,
|
|
||||||
[data-icon='bigcapital'] .path-13 {
|
|
||||||
fill: #2d95fd;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="">
|
|
||||||
<span class="preheader">Verify your email.</span>
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<table role="presentation" class="main">
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p class="align-center">
|
|
||||||
<img src="cid:bigcapital_logo" />
|
|
||||||
</p>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<p class="align-center">
|
|
||||||
<h2>Verify your email</h2>
|
|
||||||
</p>
|
|
||||||
<p class="mgb-1x">Hi <strong>{{ fullName }}<strong>,</p>
|
|
||||||
<p class="mgb-2-5x">To continue setting up your Bigcapital account, please verify that this is your email address.</p>
|
|
||||||
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{{ verifyUrl }}" target="_blank">Verify email address</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p>If this was a mistake, just ignore this email and nothing will happen.</p>
|
|
||||||
<p class="email-note">This is an automatically generated email please do not reply to this email.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td class="content-block powered-by">
|
|
||||||
Powered by <a href="https://Bigcapital.ly">Bigcapital.ly</a>.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -9,24 +9,13 @@ import 'moment/locale/ar-ly';
|
|||||||
import 'moment/locale/es-us';
|
import 'moment/locale/es-us';
|
||||||
|
|
||||||
import AppIntlLoader from './AppIntlLoader';
|
import AppIntlLoader from './AppIntlLoader';
|
||||||
import { EnsureAuthenticated } from '@/components/Guards/EnsureAuthenticated';
|
import PrivateRoute from '@/components/Guards/PrivateRoute';
|
||||||
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
|
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
|
||||||
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
|
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
|
||||||
import { Authentication } from '@/containers/Authentication/Authentication';
|
import { Authentication } from '@/containers/Authentication/Authentication';
|
||||||
|
|
||||||
import LazyLoader from '@/components/LazyLoader';
|
|
||||||
import { SplashScreen, DashboardThemeProvider } from '../components';
|
import { SplashScreen, DashboardThemeProvider } from '../components';
|
||||||
import { queryConfig } from '../hooks/query/base';
|
import { queryConfig } from '../hooks/query/base';
|
||||||
import { EnsureUserEmailVerified } from './Guards/EnsureUserEmailVerified';
|
|
||||||
import { EnsureAuthNotAuthenticated } from './Guards/EnsureAuthNotAuthenticated';
|
|
||||||
import { EnsureUserEmailNotVerified } from './Guards/EnsureUserEmailNotVerified';
|
|
||||||
|
|
||||||
const EmailConfirmation = LazyLoader({
|
|
||||||
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
|
||||||
});
|
|
||||||
const RegisterVerify = LazyLoader({
|
|
||||||
loader: () => import('@/containers/Authentication/RegisterVerify'),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App inner.
|
* App inner.
|
||||||
@@ -37,30 +26,9 @@ function AppInsider({ history }) {
|
|||||||
<DashboardThemeProvider>
|
<DashboardThemeProvider>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={'/auth/register/verify'}>
|
<Route path={'/auth'} component={Authentication} />
|
||||||
<EnsureAuthenticated>
|
|
||||||
<EnsureUserEmailNotVerified>
|
|
||||||
<RegisterVerify />
|
|
||||||
</EnsureUserEmailNotVerified>
|
|
||||||
</EnsureAuthenticated>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/auth/email_confirmation'}>
|
|
||||||
<EmailConfirmation />
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/auth'}>
|
|
||||||
<EnsureAuthNotAuthenticated>
|
|
||||||
<Authentication />
|
|
||||||
</EnsureAuthNotAuthenticated>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/'}>
|
<Route path={'/'}>
|
||||||
<EnsureAuthenticated>
|
<PrivateRoute component={DashboardPrivatePages} />
|
||||||
<EnsureUserEmailVerified>
|
|
||||||
<DashboardPrivatePages />
|
|
||||||
</EnsureUserEmailVerified>
|
|
||||||
</EnsureAuthenticated>
|
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
useAuthenticatedAccount,
|
useAuthenticatedAccount,
|
||||||
useCurrentOrganization,
|
useCurrentOrganization,
|
||||||
@@ -116,14 +116,6 @@ export function useApplicationBoot() {
|
|||||||
isBooted.current = true;
|
isBooted.current = true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Reset the loading states once the hook unmount.
|
|
||||||
useEffect(
|
|
||||||
() => () => {
|
|
||||||
isAuthUserLoading && !isBooted.current && stopLoading();
|
|
||||||
isOrgLoading && !isBooted.current && stopLoading();
|
|
||||||
},
|
|
||||||
[isAuthUserLoading, isOrgLoading, stopLoading],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading: isOrgLoading || isAuthUserLoading,
|
isLoading: isOrgLoading || isAuthUserLoading,
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useIsAuthenticated } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureAuthNotAuthenticatedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnsureAuthNotAuthenticated({
|
|
||||||
children,
|
|
||||||
redirectTo = '/',
|
|
||||||
}: EnsureAuthNotAuthenticatedProps) {
|
|
||||||
const isAuthenticated = useIsAuthenticated();
|
|
||||||
|
|
||||||
return !isAuthenticated ? (
|
|
||||||
<>{children}</>
|
|
||||||
) : (
|
|
||||||
<Redirect to={{ pathname: redirectTo }} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useIsAuthenticated } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureAuthenticatedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnsureAuthenticated({
|
|
||||||
children,
|
|
||||||
redirectTo = '/auth/login',
|
|
||||||
}: EnsureAuthenticatedProps) {
|
|
||||||
const isAuthenticated = useIsAuthenticated();
|
|
||||||
|
|
||||||
return isAuthenticated ? (
|
|
||||||
<>{children}</>
|
|
||||||
) : (
|
|
||||||
<Redirect to={{ pathname: redirectTo }} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useAuthUserVerified } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureUserEmailNotVerifiedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Higher Order Component to ensure that the user's email is not verified.
|
|
||||||
* If is verified, redirects to the inner setup page.
|
|
||||||
*/
|
|
||||||
export function EnsureUserEmailNotVerified({
|
|
||||||
children,
|
|
||||||
redirectTo = '/',
|
|
||||||
}: EnsureUserEmailNotVerifiedProps) {
|
|
||||||
const isAuthVerified = useAuthUserVerified();
|
|
||||||
|
|
||||||
if (isAuthVerified) {
|
|
||||||
return <Redirect to={{ pathname: redirectTo }} />;
|
|
||||||
}
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useAuthUserVerified } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureUserEmailVerifiedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Higher Order Component to ensure that the user's email is verified.
|
|
||||||
* If not verified, redirects to the email verification page.
|
|
||||||
*/
|
|
||||||
export function EnsureUserEmailVerified({
|
|
||||||
children,
|
|
||||||
redirectTo = '/auth/register/verify',
|
|
||||||
}: EnsureUserEmailVerifiedProps) {
|
|
||||||
const isAuthVerified = useAuthUserVerified();
|
|
||||||
|
|
||||||
if (!isAuthVerified) {
|
|
||||||
return <Redirect to={{ pathname: redirectTo }} />;
|
|
||||||
}
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
19
packages/webapp/src/components/Guards/PrivateRoute.tsx
Normal file
19
packages/webapp/src/components/Guards/PrivateRoute.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import BodyClassName from 'react-body-classname';
|
||||||
|
import { Redirect } from 'react-router-dom';
|
||||||
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
|
|
||||||
|
export default function PrivateRoute({ component: Component, ...rest }) {
|
||||||
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BodyClassName className={''}>
|
||||||
|
{isAuthenticated ? (
|
||||||
|
<Component />
|
||||||
|
) : (
|
||||||
|
<Redirect to={{ pathname: '/auth/login' }} />
|
||||||
|
)}
|
||||||
|
</BodyClassName>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Icon, FormattedMessage as T } from '@/components';
|
|
||||||
|
|
||||||
interface AuthContainerProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AuthContainer({ children }: AuthContainerProps) {
|
|
||||||
return (
|
|
||||||
<AuthPage>
|
|
||||||
<AuthInsider>
|
|
||||||
<AuthLogo>
|
|
||||||
<Icon icon="bigcapital" height={37} width={214} />
|
|
||||||
</AuthLogo>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</AuthInsider>
|
|
||||||
</AuthPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AuthPage = styled.div``;
|
|
||||||
const AuthInsider = styled.div`
|
|
||||||
width: 384px;
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
padding-top: 80px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const AuthLogo = styled.div`
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
`;
|
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { Route, Switch, useLocation } from 'react-router-dom';
|
import React from 'react';
|
||||||
|
import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
|
||||||
import BodyClassName from 'react-body-classname';
|
import BodyClassName from 'react-body-classname';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||||
|
|
||||||
import authenticationRoutes from '@/routes/authentication';
|
import authenticationRoutes from '@/routes/authentication';
|
||||||
import { Icon, FormattedMessage as T } from '@/components';
|
import { Icon, FormattedMessage as T } from '@/components';
|
||||||
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
import { AuthMetaBootProvider } from './AuthMetaBoot';
|
import { AuthMetaBootProvider } from './AuthMetaBoot';
|
||||||
|
|
||||||
import '@/style/pages/Authentication/Auth.scss';
|
import '@/style/pages/Authentication/Auth.scss';
|
||||||
|
|
||||||
export function Authentication() {
|
export function Authentication() {
|
||||||
|
const to = { pathname: '/' };
|
||||||
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
return <Redirect to={to} />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BodyClassName className={'authentication'}>
|
<BodyClassName className={'authentication'}>
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { useEffect, useMemo } from 'react';
|
|
||||||
import { useLocation, useHistory } from 'react-router-dom';
|
|
||||||
import { useAuthSignUpVerify } from '@/hooks/query';
|
|
||||||
import { AppToaster } from '@/components';
|
|
||||||
import { Intent } from '@blueprintjs/core';
|
|
||||||
|
|
||||||
function useQuery() {
|
|
||||||
const { search } = useLocation();
|
|
||||||
return useMemo(() => new URLSearchParams(search), [search]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function EmailConfirmation() {
|
|
||||||
const { mutateAsync: authSignupVerify } = useAuthSignUpVerify();
|
|
||||||
const history = useHistory();
|
|
||||||
const query = useQuery();
|
|
||||||
|
|
||||||
const token = query.get('token');
|
|
||||||
const email = query.get('email');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!token || !email) {
|
|
||||||
history.push('/auth/login');
|
|
||||||
}
|
|
||||||
}, [history, token, email]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
authSignupVerify({ token, email })
|
|
||||||
.then(() => {
|
|
||||||
AppToaster.show({
|
|
||||||
message: 'Your email has been verified, Congrats!',
|
|
||||||
intent: Intent.SUCCESS,
|
|
||||||
});
|
|
||||||
history.push('/');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
AppToaster.show({
|
|
||||||
message: 'Something went wrong',
|
|
||||||
intent: Intent.DANGER,
|
|
||||||
});
|
|
||||||
history.push('/');
|
|
||||||
});
|
|
||||||
}, [token, email, authSignupVerify, history]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
.root {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title{
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
color: #252A31;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description{
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 1.45;
|
|
||||||
color: #404854;
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { Button, Intent } from '@blueprintjs/core';
|
|
||||||
import AuthInsider from './AuthInsider';
|
|
||||||
import { AuthInsiderCard } from './_components';
|
|
||||||
import styles from './RegisterVerify.module.scss';
|
|
||||||
import { AppToaster, Stack } from '@/components';
|
|
||||||
import { useAuthActions } from '@/hooks/state';
|
|
||||||
import { useAuthSignUpVerifyResendMail } from '@/hooks/query';
|
|
||||||
import { AuthContainer } from './AuthContainer';
|
|
||||||
|
|
||||||
export default function RegisterVerify() {
|
|
||||||
const { setLogout } = useAuthActions();
|
|
||||||
const { mutateAsync: resendSignUpVerifyMail, isLoading } =
|
|
||||||
useAuthSignUpVerifyResendMail();
|
|
||||||
|
|
||||||
const handleResendMailBtnClick = () => {
|
|
||||||
resendSignUpVerifyMail()
|
|
||||||
.then(() => {
|
|
||||||
AppToaster.show({
|
|
||||||
intent: Intent.SUCCESS,
|
|
||||||
message: 'The verification mail has sent successfully.',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
AppToaster.show({
|
|
||||||
intent: Intent.DANGER,
|
|
||||||
message: 'Something went wrong.',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const handleSignOutBtnClick = () => {
|
|
||||||
setLogout();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthContainer>
|
|
||||||
<AuthInsider>
|
|
||||||
<AuthInsiderCard className={styles.root}>
|
|
||||||
<h2 className={styles.title}>Please verify your email</h2>
|
|
||||||
<p className={styles.description}>
|
|
||||||
We sent an email to <strong>asdahmed@gmail.com</strong> Click the
|
|
||||||
link inside to get started.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Stack spacing={4}>
|
|
||||||
<Button
|
|
||||||
large
|
|
||||||
fill
|
|
||||||
loading={isLoading}
|
|
||||||
intent={Intent.NONE}
|
|
||||||
onClick={handleResendMailBtnClick}
|
|
||||||
>
|
|
||||||
Resend email
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
large
|
|
||||||
fill
|
|
||||||
minimal
|
|
||||||
intent={Intent.DANGER}
|
|
||||||
onClick={handleSignOutBtnClick}
|
|
||||||
>
|
|
||||||
Not my email
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
</AuthInsiderCard>
|
|
||||||
</AuthInsider>
|
|
||||||
</AuthContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -78,7 +78,7 @@ export const useAuthResetPassword = (props) => {
|
|||||||
*/
|
*/
|
||||||
export const useAuthMetadata = (props) => {
|
export const useAuthMetadata = (props) => {
|
||||||
return useRequestQuery(
|
return useRequestQuery(
|
||||||
[t.AUTH_METADATA_PAGE],
|
[t.AUTH_METADATA_PAGE,],
|
||||||
{
|
{
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `auth/meta`,
|
url: `auth/meta`,
|
||||||
@@ -88,35 +88,5 @@ export const useAuthMetadata = (props) => {
|
|||||||
defaultData: {},
|
defaultData: {},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const useAuthSignUpVerifyResendMail = (props) => {
|
|
||||||
const apiRequest = useApiRequest();
|
|
||||||
|
|
||||||
return useMutation(
|
|
||||||
() => apiRequest.post('auth/register/verify/resend'),
|
|
||||||
props,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface AuthSignUpVerifyValues {
|
|
||||||
token: string;
|
|
||||||
email: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const useAuthSignUpVerify = (props) => {
|
|
||||||
const apiRequest = useApiRequest();
|
|
||||||
|
|
||||||
return useMutation(
|
|
||||||
(values: AuthSignUpVerifyValues) =>
|
|
||||||
apiRequest.post('auth/register/verify', values),
|
|
||||||
props,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useQueryTenant, useRequestQuery } from '../useQueryRequest';
|
|||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
import { useSetFeatureDashboardMeta } from '../state/feature';
|
import { useSetFeatureDashboardMeta } from '../state/feature';
|
||||||
import t from './types';
|
import t from './types';
|
||||||
import { useSetAuthEmailConfirmed } from '../state';
|
|
||||||
|
|
||||||
// Common invalidate queries.
|
// Common invalidate queries.
|
||||||
const commonInvalidateQueries = (queryClient) => {
|
const commonInvalidateQueries = (queryClient) => {
|
||||||
@@ -131,8 +130,6 @@ export function useUser(id, props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthenticatedAccount(props) {
|
export function useAuthenticatedAccount(props) {
|
||||||
const setEmailConfirmed = useSetAuthEmailConfirmed();
|
|
||||||
|
|
||||||
return useRequestQuery(
|
return useRequestQuery(
|
||||||
['AuthenticatedAccount'],
|
['AuthenticatedAccount'],
|
||||||
{
|
{
|
||||||
@@ -142,9 +139,6 @@ export function useAuthenticatedAccount(props) {
|
|||||||
{
|
{
|
||||||
select: (response) => response.data.data,
|
select: (response) => response.data.data,
|
||||||
defaultData: {},
|
defaultData: {},
|
||||||
onSuccess: (data) => {
|
|
||||||
setEmailConfirmed(data.is_verified);
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -172,3 +166,4 @@ export const useDashboardMeta = (props) => {
|
|||||||
}, [state.isSuccess, state.data, setFeatureDashboardMeta]);
|
}, [state.isSuccess, state.data, setFeatureDashboardMeta]);
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { isAuthenticated } from '@/store/authentication/authentication.reducer';
|
import { isAuthenticated } from '@/store/authentication/authentication.reducer';
|
||||||
import {
|
import { setLogin } from '@/store/authentication/authentication.actions';
|
||||||
setEmailConfirmed,
|
|
||||||
setLogin,
|
|
||||||
} from '@/store/authentication/authentication.actions';
|
|
||||||
import { useQueryClient } from 'react-query';
|
import { useQueryClient } from 'react-query';
|
||||||
import { removeCookie } from '@/utils';
|
import { removeCookie } from '@/utils';
|
||||||
|
|
||||||
@@ -67,22 +64,3 @@ export const useAuthUser = () => {
|
|||||||
export const useAuthOrganizationId = () => {
|
export const useAuthOrganizationId = () => {
|
||||||
return useSelector((state) => state.authentication.organizationId);
|
return useSelector((state) => state.authentication.organizationId);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the user's email verification status.
|
|
||||||
*/
|
|
||||||
export const useAuthUserVerified = () => {
|
|
||||||
return useSelector((state) => state.authentication.verified);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the user's email verification status.
|
|
||||||
*/
|
|
||||||
export const useSetAuthEmailConfirmed = () => {
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
return useCallback(
|
|
||||||
(verified?: boolean = true) => dispatch(setEmailConfirmed(verified)),
|
|
||||||
[dispatch],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -28,16 +28,10 @@ export default [
|
|||||||
loader: () => import('@/containers/Authentication/InviteAccept'),
|
loader: () => import('@/containers/Authentication/InviteAccept'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: `${BASE_URL}/register/email_confirmation`,
|
|
||||||
component: LazyLoader({
|
|
||||||
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/register`,
|
path: `${BASE_URL}/register`,
|
||||||
component: LazyLoader({
|
component: LazyLoader({
|
||||||
loader: () => import('@/containers/Authentication/Register'),
|
loader: () => import('@/containers/Authentication/Register'),
|
||||||
}),
|
}),
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,8 +3,4 @@ import t from '@/store/types';
|
|||||||
|
|
||||||
export const setLogin = () => ({ type: t.LOGIN_SUCCESS });
|
export const setLogin = () => ({ type: t.LOGIN_SUCCESS });
|
||||||
export const setLogout = () => ({ type: t.LOGOUT });
|
export const setLogout = () => ({ type: t.LOGOUT });
|
||||||
export const setStoreReset = () => ({ type: t.RESET });
|
export const setStoreReset = () => ({ type: t.RESET });
|
||||||
export const setEmailConfirmed = (verified?: boolean) => ({
|
|
||||||
type: t.SET_EMAIL_VERIFIED,
|
|
||||||
action: { verified },
|
|
||||||
});
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { PayloadAction, createReducer } from '@reduxjs/toolkit';
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
import { persistReducer } from 'redux-persist';
|
import { persistReducer } from 'redux-persist';
|
||||||
import purgeStoredState from 'redux-persist/es/purgeStoredState';
|
import purgeStoredState from 'redux-persist/es/purgeStoredState';
|
||||||
import storage from 'redux-persist/lib/storage';
|
import storage from 'redux-persist/lib/storage';
|
||||||
import { isUndefined } from 'lodash';
|
|
||||||
import { getCookie } from '@/utils';
|
import { getCookie } from '@/utils';
|
||||||
import t from '@/store/types';
|
import t from '@/store/types';
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ const initialState = {
|
|||||||
tenantId: getCookie('tenant_id'),
|
tenantId: getCookie('tenant_id'),
|
||||||
userId: getCookie('authenticated_user_id'),
|
userId: getCookie('authenticated_user_id'),
|
||||||
locale: getCookie('locale'),
|
locale: getCookie('locale'),
|
||||||
verified: true, // Let's be optimistic and assume the user's email is confirmed.
|
|
||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -34,15 +32,6 @@ const reducerInstance = createReducer(initialState, {
|
|||||||
state.errors = [];
|
state.errors = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
[t.SET_EMAIL_VERIFIED]: (
|
|
||||||
state,
|
|
||||||
payload: PayloadAction<{ verified?: boolean }>,
|
|
||||||
) => {
|
|
||||||
state.verified = !isUndefined(payload.action.verified)
|
|
||||||
? payload.action.verified
|
|
||||||
: true;
|
|
||||||
},
|
|
||||||
|
|
||||||
[t.RESET]: (state) => {
|
[t.RESET]: (state) => {
|
||||||
purgeStoredState(CONFIG);
|
purgeStoredState(CONFIG);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ export default {
|
|||||||
LOGOUT: 'LOGOUT',
|
LOGOUT: 'LOGOUT',
|
||||||
LOGIN_CLEAR_ERRORS: 'LOGIN_CLEAR_ERRORS',
|
LOGIN_CLEAR_ERRORS: 'LOGIN_CLEAR_ERRORS',
|
||||||
RESET: 'RESET',
|
RESET: 'RESET',
|
||||||
SET_EMAIL_VERIFIED: 'SET_EMAIL_VERIFIED'
|
|
||||||
};
|
};
|
||||||
24
scripts/backup.sh
Normal file
24
scripts/backup.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Takes a backup of database Docker volume and compress into one .tar.gz
|
||||||
|
# file and upload to s3 package through s3cmd.
|
||||||
|
S3_BUCKET="s3://bigcapital-backup"
|
||||||
|
|
||||||
|
# Generate the current date and time formatted as YYYY-MM-DD-HH-MM-SS
|
||||||
|
CURRENT_DATETIME=$(date +"%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
# Define the filename with the current date and time
|
||||||
|
FILE_NAME="bigcapital-mariadb-${CURRENT_DATETIME}.tar.gz"
|
||||||
|
|
||||||
|
# Create a sample file (replace this with your actual file creation process)
|
||||||
|
echo "This is a sample file created on ${CURRENT_DATETIME}" > "$FILE_NAME"
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
--mount source=bigcapital_prod_mysql,target=/data/db \
|
||||||
|
-v $(pwd):/backup \
|
||||||
|
busybox \
|
||||||
|
tar -czvf "/backup/$FILE_NAME" /data/db
|
||||||
|
|
||||||
|
# Upload the file to S3 using s3cmd
|
||||||
|
s3cmd put "$FILE_NAME" "$S3_BUCKET/"
|
||||||
|
|
||||||
|
# Remove the temporary file
|
||||||
|
rm "$FILE_NAME"
|
||||||
7
scripts/store-backup.sh
Normal file
7
scripts/store-backup.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
# Store the backup.
|
||||||
|
docker run --rm \
|
||||||
|
--mount source=bigcapital_dev_mysql,target=/data/db \
|
||||||
|
-v $(pwd):/backup \
|
||||||
|
busybox \
|
||||||
|
tar -xzvf /backup/bigcapital-mariadb-2024-04-24-15-14-40.tar.gz -C /
|
||||||
Reference in New Issue
Block a user