Code Snippets - xTalk

/* eslint-disable consistent-return */ const _ = require('lodash'); const passport = require('passport'); const Joi = require('joi'); const fs = require('fs'); const path = require('path'); const Image = require('../../media/components/image'); const SYSTEM_CONST = require('../../system/constants'); /** * Create a new user * Using by Administrator */ exports.create = async (req, res, next) => { try { const schema = Joi.object().keys({ username: Joi.string().required(), phoneNumber: Joi.string().optional(), role: Joi.string().valid(['admin', 'user']).default('user').required(), type: Joi.string().valid(['user', 'model']).default('user').required(), email: Joi.string().email().required(), password: Joi.string().min(6).required(), availableToken: Joi.number().min(0).allow([null]).optional(), address: Joi.string().allow([null, '']).optional(), state: Joi.string().allow([null, '']).optional(), city: Joi.string().allow([null, '']).optional(), country: Joi.string().allow([null, '']).optional(), isActive: Joi.boolean().allow([null]).default(true).optional(), avatar: Joi.string().allow([null, '']).optional(), emailVerified: Joi.boolean().allow([null]).default(true).optional(), isCompletedProfile: Joi.boolean().allow([null]).default(true).optional(), isBlocked: Joi.boolean().allow([null]).default(true).optional() }); const validate = Joi.validate(req.body, schema); if (validate.error) { return next(PopulateResponse.validationError(validate.error)); } if (validate.value.role === 'admin' && !validate.value.password) { return next( PopulateResponse.validationError({ msg: 'Admin accounnt needs password to login, please enter password!' }) ); } const user = await Service.User.create(validate.value); res.locals.user = user; return next(); } catch (e) { return next(e); } }; /** * do update for admin update */ exports.update = async (req, res, next) => { try { const user = req.params.id ? await DB.User.findOne({ _id: req.params.id }) : req.user; let publicFields = ['address', 'phoneNumber', 'isActive', 'avatar', 'email', 'isBlocked']; if (req.user.role === 'admin') { publicFields = publicFields.concat(['isCompletedProfile', 'emailVerified', 'role', 'username', 'availableToken']); req.body.password && (publicFields = publicFields.concat(['password'])); } const fields = _.pick(req.body, publicFields); _.merge(user, fields); if (user.type === 'user') { user.isApproved = true; } await user.save(); res.locals.update = user; next(); } catch (e) { next(e); } }; exports.me = (req, res, next) => { res.locals.me = req.user.getPublicProfile(); next(); }; exports.findOne = async (req, res, next) => { try { const user = await DB.User.findOne({ _id: req.params.id }); res.locals.user = user; next(); } catch (e) { next(e); } }; /** * find user from Add Contact Page */ exports.findByUsername = async (req, res, next) => { try { const schema = Joi.object().keys({ username: Joi.string().required() }); const validate = Joi.validate(req.params, schema); if (validate.error) { return next(PopulateResponse.validationError(validate.error)); } const query = _.merge(validate.value, { type: { $ne: req.user.type }, isCompletedProfile: true, isApproved: true, isActive: true, isBlocked: false }); const user = await DB.User.findOne(query); if (!user) { return next(PopulateResponse.notFound({ message: 'User is not found' })); } const contact = await DB.Contact.findOne({ $or: [ { addedBy: req.user._id, userId: user._id }, { addedBy: user._id, userId: req.user._id } ] }); res.locals.user = { ...user.getPublicProfile(), isFriend: contact ? true : false, contactId: contact?._id || null }; return next(); } catch (e) { return next(e); } }; /** * update user avatar */ exports.updateAvatar = async (req, res, next) => { try { const user = req.params.id ? await DB.User.findOne({ _id: req.params.id }) : req.user; if (!user) { return next(PopulateResponse.notFound()); } const avatarSize = await DB.Config.findOne({ key: SYSTEM_CONST.AVATAR_SIZE }); if (!avatarSize || !avatarSize.value || !avatarSize.value.width || !avatarSize.value.height) { return PopulateResponse.serverError({ msg: 'Missing avatar size!' }); } // create thumb for the avatar const thumbPath = await Image.resize({ input: req.file.path, width: avatarSize.value.width || 250, height: avatarSize.value.height || 250, resizeOption: '^' }); await DB.User.update({ _id: req.params.id || req.user._id }, { $set: { avatar: thumbPath } }); // unlink old avatar if (user.avatar && !Helper.String.isUrl(user.avatar) && fs.existsSync(path.resolve(user.avatar))) { fs.unlinkSync(path.resolve(user.avatar)); } // remove tmp file // if (fs.existsSync(path.resolve(req.file.path))) { // fs.unlinkSync(path.resolve(req.file.path)); // } res.locals.updateAvatar = { url: DB.User.getAvatarUrl(thumbPath) }; return next(); } catch (e) { return next(e); } }; exports.search = async (req, res, next) => { const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip() const take = parseInt(req.query.take, 10) || 10; try { let query = Helper.App.populateDbQuery(req.query, { text: ['phoneNumber', 'email', 'username'], boolean: ['isOnline', 'isApproved', 'isCompletedProfile', 'isCompletedDocument', 'isActive', 'isBlocked'], equal: ['role', 'type', 'gender', 'city', 'state', 'country'] }); if (req.user.role !== 'admin') { query = { ...query, isApproved: true, isCompletedProfile: true, isActive: true, isBlocked: false }; } const sort = Helper.App.populateDBSort(req.query); const count = await DB.User.count(query); const items = await DB.User.find(query) .collation({ locale: 'en' }) .sort(sort) .skip(page * take) .limit(take) .exec(); res.locals.search = { count, items: await checkAndConvertFriend(items, req.user) }; next(); } catch (e) { next(e); } }; exports.searchFriends = async (req, res, next) => { const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip() const take = parseInt(req.query.take, 10) || 10; try { let query = Helper.App.populateDbQuery(req.query, { text: ['phoneNumber', 'email', 'username'], boolean: ['isOnline', 'isApproved', 'isCompletedProfile', 'isCompletedDocument', 'isActive', 'isBlocked'], equal: ['role', 'type', 'gender', 'city', 'state', 'country'] }); if (req.user.role !== 'admin') { query = { ...query, isApproved: true, isCompletedProfile: true, isActive: true, isBlocked: false }; } const sort = Helper.App.populateDBSort(req.query); const items = await DB.User.find(query) .collation({ locale: 'en' }) .sort(sort) .exec(); // add user response and check for friend const newItems = await checkAndConvertFriend(items, req.user); const data = newItems.filter((i) => i.isFriend); res.locals.search = { count: data.length, items: data.slice(page * take, (page + 1) * take ) }; next(); } catch (e) { next(e); } }; async function checkAndConvertFriend(models, user) { const query = { $or: [ { userId: user._id, addedBy: { $in: models } }, { userId: { $in: models }, addedBy: user._id } ] }; const contacts = await DB.Contact.find(query); const array = models.map(model => { let data = user?.role === 'admin' ? model : model.getPublicProfile(); const isFriend = contacts.find( contact => contact.userId.toString() === model._id.toString() || contact.addedBy.toString() === model._id.toString() ); data.isFriend = isFriend ? true : false; return data; }); return array; } exports.remove = async (req, res, next) => { try { const user = DB.User.findOne({ _id: req.params.userId }); if (!user) { return next(PopulateResponse.notFound()); } if (user.role === 'admin') { return next(PopulateResponse.forbidden()); } // permanently delete // contact await DB.Contact.deleteMany({ $or: [{ addedBy: req.params.userId }, { userId: req.params.userId }] }); // conversation await DB.Conversation.deleteMany({ memberIds: req.params.userId }); // conversation meta await DB.ConversationUserMeta.deleteMany({ userId: req.params.userId }); // message await DB.Message.deleteMany({ $or: [{ senderId: req.params.userId }, { recipientId: req.params.userId }] }); // device await DB.Device.deleteMany({ userId: req.params.userId }); // invoice await DB.Invoice.deleteMany({ userId: req.params.userId }); // transaction await DB.Transaction.deleteMany({ userId: req.params.userId }); // payout await DB.PayoutRequest.deleteMany({ modelId: req.params.userId }); // purchase item const purchaseItems = await DB.PurchaseItem.find({ userId: req.params.userId }).exec(); if (user.type === 'model') { // sell item - not remove purchase item const sellItemIds = purchaseItems.map(i => i.sellItemId); await DB.SellItem.deleteMany({ $and: [{ userId: req.params.userId }, { _id: { $nin: sellItemIds } }] }); // earning await DB.Earning.deleteMany({ modelId: req.params.userId }); } // media - not remove purchase item const mediaIds = purchaseItems.map(i => i.mediaId); await DB.Media.deleteMany({ $and: [{ ownerId: req.params.userId }, { _id: { $nin: mediaIds } }] }); await DB.PurchaseItem.deleteMany({ userId: req.params.userId }); // share love await DB.ShareLove.deleteMany({ $or: [{ userId: req.params.userId }, { modelId: req.params.userId }] }); // phone verify await DB.VerifyCode.deleteMany({ userId: req.params.userId }); // user social // await DB.UserSocial.deleteMany({ userId: req.params.userId }); await user.remove(); res.locals.remove = { success: true }; return next(); } catch (e) { return next(e); } }; exports.updateProfile = async (req, res, next) => { try { const schema = Joi.object().keys({ username: Joi.string().min(3).required(), gender: Joi.string().allow(['male', 'female', 'transgender']).required(), bio: Joi.string().min(6).required(), age: Joi.number().min(0).required(), address: Joi.string().allow(['', null]).optional(), city: Joi.string().allow(['', null]).optional(), state: Joi.string().allow(['', null]).optional(), country: Joi.string().allow(['', null]).optional(), phoneNumber: Joi.string().allow(['', null]).optional(), email: Joi.string().email().required() }); const validate = Joi.validate(req.body, schema); if (validate.error) { return next(PopulateResponse.validationError(validate.error)); } const user = await DB.User.findOne({ _id: req.user._id }); //? User update profile if (!user) { return next(PopulateResponse.error({ msg: 'User is not found!' })); } const username = validate.value.username.toLowerCase().trim(); const email = validate.value.email.trim(); const count = await DB.User.count({ $or: [{ username }, { email }], _id: { $ne: user._id } }); if (count) { return next(PopulateResponse.error({ msg: 'This username or email has been taken!' })); } _.merge(user, validate.value); await user.save(); const isCompletedProfile = await Service.User.updateCompletedProfile(user); user.isCompletedProfile = isCompletedProfile.success; res.locals.update = user.getPublicProfile(); next(); } catch (e) { return next(e); } }; exports.updateDocument = async (req, res, next) => { try { const schema = Joi.object().keys({ address: Joi.string().required(), city: Joi.string().required(), state: Joi.string().required(), country: Joi.string().required(), firstName: Joi.string().required(), lastName: Joi.string().required(), birthday: Joi.string().required(), instagram: Joi.string().allow([null, '']).optional(), twitter: Joi.string().allow([null, '']).optional(), number: Joi.string().required(), type: Joi.string().allow(['passport', 'ID', 'driverCard']).required(), zipCode: Joi.string().required(), isConfirm: Joi.boolean().required(), isExpired: Joi.boolean().allow([null, '']).default(false).optional(), expiredDate: Joi.string().allow([null, '']).optional(), isApproved: Joi.boolean().optional() //? admin approve the document }); const validate = Joi.validate(req.body, schema); if (validate.error) { return next(PopulateResponse.validationError(validate.error)); } const query = { _id: req.user.role === 'admin' ? req.params.id : req.user._id }; const user = await DB.User.findOne(query); if (!user) { return next(PopulateResponse.notFound()); } user.verificationDocument = Object.assign(user.verificationDocument, _.omit(validate.value, ['isApproved'])); user.isCompletedDocument = true; if (req.user.role === 'admin') { user.isApproved = validate.value.isApproved || false; } await user.save(); res.locals.document = user.verificationDocument; next(); } catch (e) { return next(e); } }; exports.updateTokenPerMessage = async (req, res, next) => { try { const schema = Joi.object().keys({ token: Joi.number().min(1).required() }); const validate = Joi.validate(req.body, schema); if (validate.error) { return next(PopulateResponse.validationError(validate.error)); } if (req.user.type !== 'model') { return next(PopulateResponse.forbidden({ message: 'Only models can update!' })); } req.user.tokenPerMessage = validate.value.token; await req.user.save(); res.locals.tokenPerMessage = req.user; return next(); } catch (e) { return next(e); } }; exports.getOTP = async (req, res, next) => { try { const code = process.env.PHONE_DEBUG ? '0000' : Helper.String.randomString(4, '1234567890'); let data = await DB.VerifyCode.findOne({ email: req.user.email }); if (!data) { data = new DB.VerifyCode({ userId: req.user._id, email: req.user.email }); } data.code = code; await data.save(); const siteName = await DB.Config.findOne({ key: SYSTEM_CONST.SITE_NAME }); // send mail with verify code to user await Service.Mailer.send('verify-code-email.html', req.user.email, { subject: 'Your verify code', verifyCode: code.toString(), siteName: siteName?.value || 'XChat' }); res.locals.getOTP = PopulateResponse.success({ message: 'Send OTP is successfully!' }, 'OTP_SENT'); return next(); } catch (e) { return next(e); } }; /** * update model certification photo */ exports.updateCertificationPhoto = async (req, res, next) => { try { const user = req.params.id ? await DB.User.findOne({ _id: req.params.id, type: 'model' // only model to update certification }) : req.user; if (!user) { return next(PopulateResponse.notFound()); } const certificationSize = await DB.Config.findOne({ key: SYSTEM_CONST.CERTIFICATION_SIZE }); if ( !certificationSize || !certificationSize.value || !certificationSize.value.width || !certificationSize.value.height ) { return PopulateResponse.serverError({ msg: 'Missing certification size!' }); } // create thumb for the certification const thumbPath = await Image.resize({ input: req.file.path, width: certificationSize.value.width || 250, height: certificationSize.value.height || 250, resizeOption: '^' }); const updateString = `verificationDocument.${req.query.position}`; const update = { [updateString]: thumbPath }; await DB.User.update( { _id: req.params.id || req.user._id }, { $set: update } ); // unlink old certification if ( user.verificationDocument && user.verificationDocument[req.query.position] && !Helper.String.isUrl(user.verificationDocument[req.query.position]) && fs.existsSync(path.resolve(user.verificationDocument[req.query.position])) ) { fs.unlinkSync(path.resolve(user.verificationDocument[req.query.position])); } // remove tmp file // if (fs.existsSync(path.resolve(req.file.path))) { // fs.unlinkSync(path.resolve(req.file.path)); // } res.locals.updateCertificationPhoto = { url: DB.User.getAvatarUrl(update[updateString]) // convert to string }; return next(); } catch (e) { return next(e); } }; /** * User update password */ exports.updatePassword = async (req, res, next) => { const schema = Joi.object().keys({ email: Joi.string().email().required(), password: Joi.string().min(6).required(), newPassword: Joi.string().min(6).required() }); const validate = Joi.validate(req.body, schema); if (validate.error) { return next(PopulateResponse.validationError(validate.error)); } try { passport.authenticate('local', async (err, user, info) => { const error = err || info; if (error) { return next(error); } if (!user) { return next(PopulateResponse.notFound()); } user.password = validate.value.newPassword; await user.save(); res.locals.updatePassword = { success: true }; return next(); })(req, res, next); } catch (e) { return next(e); } }; /** * User deactive account yourself */ exports.deactiveAccount = async (req, res, next) => { try { const user = req.user; user.isBlocked = true; await user.save(); res.locals.deactive = PopulateResponse.success( { message: 'Your account has been deactived, you will be logged out.' }, 'USER_DEACTIVED' ); next(); } catch (e) { next(e); } };

Model Details Code Samples

import * as React from 'react'; import Head from 'next/head'; import { connect } from 'react-redux'; import { withAuth } from 'lib/withAuth'; import withReduxSaga from 'lib/withReduxSaga'; import { toast } from 'react-toastify'; import Router from 'next/router'; // Actions import { findContact, resetFindContactStore } from 'lib/contact/actions'; import { loadUser } from 'lib/user/actions'; // Child components import ContactHeader from 'components/contact/contact-detail-box/header'; import ContactContent from 'components/contact/contact-detail-box/content'; import ContactSearchForm from 'components/contact/contact-search-form'; import UserListing from 'components/user/user-listing'; import UserFilter from 'components/user/filter/user-filter'; import LocationSubFilter from 'components/user/filter/location-sub-filter'; interface IProps { authUser: any; addContactStore: { requesting: boolean; success: boolean; error: any; }; resetFindContactStore: Function; findContact: Function; findContactStore: { requesting: boolean; success: boolean; error: any; contact: any; isFriend: boolean; }; removeContact: Function; removeContactStore: { requesting: boolean; success: boolean; error: any; }; shareLoveStore: { requesting: boolean; success: boolean; error: any; }; createConvStore: { requesting: boolean; success: boolean; error: any; data: any; }; loadUser: Function; loadUserStore: { requesting: boolean; success: boolean; error: any; }; } interface IState { isShowLocation: boolean; gender: string; country: string; state: string; city: string; } class ModelListing extends React.Component<IProps, IState> { constructor(props: any) { super(props); this.state = { isShowLocation: false, gender: '', country: '', state: '', city: '' }; } componentDidMount() { this.props.resetFindContactStore(); } componentDidUpdate(prevProps: IProps) { const { addContactStore, findContactStore, removeContactStore, shareLoveStore, createConvStore, loadUserStore } = this.props; //? handle find contact store (only hanlde error) if ( prevProps.findContactStore.requesting && !findContactStore.requesting && !findContactStore.success && findContactStore.error ) { return toast.error(findContactStore.error?.data?.message || 'User is not found'); } //? --- end --- //? handle add contact store if ( prevProps.addContactStore.requesting && !addContactStore.requesting && addContactStore.success && !addContactStore.error ) { return toast.success('Added to your favorites'); } if ( prevProps.addContactStore.requesting && !addContactStore.requesting && !addContactStore.success && addContactStore.error ) { return toast.error(addContactStore.error?.data?.message || 'Add to favorites failed!'); } //? --- end --- //? handle remove contact store if ( prevProps.removeContactStore.requesting && !removeContactStore.requesting && removeContactStore.success && !removeContactStore.error ) { return toast.success('Removed from your favorites'); } if ( prevProps.removeContactStore.requesting && !removeContactStore.requesting && !removeContactStore.success && removeContactStore.error ) { return toast.error(removeContactStore.error?.data?.message || 'Remove from favorites failed!'); } //? --- end --- //? handle share love store if ( prevProps.shareLoveStore.requesting && !shareLoveStore.requesting && shareLoveStore.success && !shareLoveStore.error ) { return toast.success('Tip sent successfully!'); } if ( prevProps.shareLoveStore.requesting && !shareLoveStore.requesting && !shareLoveStore.success && shareLoveStore.error ) { return toast.error(shareLoveStore.error?.data?.message || 'Show love fail!'); } //? --- end --- //? handle create conversation store if ( prevProps.createConvStore.requesting && !createConvStore.requesting && createConvStore.success && !createConvStore.error && createConvStore.data ) { Router.push('/message/' + createConvStore.data?._id); return; } if ( prevProps.createConvStore.requesting && !createConvStore.requesting && !createConvStore.success && createConvStore.error ) { return toast.error(createConvStore.error?.data?.message || 'Create conversation fail!'); } //? --- end --- //? handle load user store (only handle error) if ( prevProps.loadUserStore.requesting && !loadUserStore.requesting && !loadUserStore.success && loadUserStore.error ) { return toast.error(loadUserStore.error?.data?.message || 'Load user fail!'); } //? --- end --- } async loadUser(query: any) { const { gender, country, state, city } = this.state; this.props.loadUser({ ...query, type: 'model', gender, country, state, city }); } checkProfile = (username: string ) => { // if (!isFriend) { // return toast.error('You must add this model to favorite to view his/her profile'); // } Router.push( { pathname: '/contact/detail', query: { username } }, '/contact/detail/' ); } render() { const { findContactStore, authUser } = this.props; const { isShowLocation } = this.state; return ( <React.Fragment> <Head> <title>Models Listing</title> </Head> <main className="main scroll"> <div className="chats"> <div className="chat-body p-3"> <div className="row m-0 "> <div className="col-md-4 col-xs-12"> <h4 className="font-weight-semibold">Models listing</h4> </div> <div className="col-md-8 col-xs-12 mb-2"> <div className="search-filter"> {authUser.type === 'user' && ( <UserFilter onFilter={this.loadUser.bind(this)} isShowLocation={this.setState.bind(this)} /> )} <ContactSearchForm findContact={this.props.findContact.bind(this)} /> </div> </div> {authUser.type === 'user' && isShowLocation && ( <div className="col-md-12 align-self-end mb-2"> <div className="search-filter location-filter"> <LocationSubFilter onFilter={this.loadUser.bind(this)} onLocation={this.setState.bind(this)} /> </div> </div> )} {findContactStore.contact && ( <div className="col-md-12 col-12 mt-2"> <div className="container-xl p-0"> <ContactHeader contact={findContactStore.contact} isFriend={findContactStore.isFriend} /> <div className="row friends-info"> <div className="col"> <ContactContent contact={findContactStore.contact} /> </div> </div> </div> </div> )} </div> <UserListing loadMoreUser={this.loadUser.bind(this)} checkProfile={this.checkProfile.bind(this)} /> </div> </div> </main> </React.Fragment> ); } } const mapStatetoProps = (state: any) => { const { shareLoveStore, addContactStore, findContactStore, removeContactStore } = state.contact; return { shareLoveStore, addContactStore, findContactStore, removeContactStore, createConvStore: state.conversation.createConvStore, ...state.user, authUser: state.auth.authUser }; }; const mapDispatch = { findContact, loadUser, resetFindContactStore }; export default withReduxSaga(withAuth(connect(mapStatetoProps, mapDispatch)(ModelListing))) as any;