I'd like to be able to allow admins to use a single email and have multiple user names (bypassing the unique constraint for email). Moreover, if users aren't admins then they can't bypass the unique constraint.
However, using partialFilterExpression I thought this was the route to go using the roles I have defined (user and admin) but every time I save a new admin user, then when I try to add another admin user with another username (same email) I get the error:
E11000 duplicate key error collection: convofinddb.users index: email_1 dup key: { email: "email#gmail.com" }
User Schema
const userSchema = new mongoose.Schema(
{
firstName: {},
lastName: {},
userName: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
index: {
unique: true,
partialFilterExpression: { role: { $eq: 'user' } },
},
},
password: {},
role: {
type: String,
enum: ['admin', 'user'],
default: 'user',
},
},
{
timestamps: true
}
Any ideas?
Looks like I figured it out
I'll need to check if a user's role is an admin
If an admin, create a new user
Then create another email index using partialFilterExpression and { unique: false }
// this data would be from some form...
const userBody = {
firstName: 'John',
lastName: 'Doe',
userName: 'welit9000',
email: 'email#gmail.com',
password: '****',
confirmPassword: '****',
role: 'admin'
}
if (userBody.role === 'admin') {
const user = await UserModel.create(userBody);
await UserModel.collection.createIndex({ email: 1 }, { unique: false, partialFilterExpression: { role: { $eq: 'admin' } } });
}
Related
Why I'm getting this error!!
GOOGLE USER: null
/Users//my-blog/api/node_modules/mongodb/lib/operations/insert.js:53
return callback(new error_1.MongoServerError(res.writeErrors[0]));
^
MongoServerError: E11000 duplicate key error collection: blog.users
index: email_1 dup key: { email: "" }
I dropped the whole collection in Mongodb and when I tried again it gave me the same error but the data stored in MongoDB collection, the problem is with the error it stops the whole app from running. I don't know where I went wrong or what I'm missing.
Auth.js Code:
router.get("/google", passport.authenticate("google", {
scope: ["profile", "email"] }));
router.get("/auth/google/callback", passport.authenticate("google", {
successRedirect: "http://localhost:3000/",
failureRedirect: "/googleLogin/failed"
}));
router.get("/googleLogin/success", async (req, res)=>{
if(req.user){
const user = await User.findOne({provider_id: req.user.id,
provider: req.user.provider})
if(user){
res.status(200).json({
success: true,
message: "success",
user: user
})
console.log("GOOGLE USER IS: " + user)
}else{
const checkUserEmail = await User.findOne({email: req.user.email})
if(checkUserEmail){
res.status(401).json({
success: false,
message: "User already Exist with this email id",
})
}else{
const user = await User.create({
username: req.user.name.givenName+ "_" +req.user.name.familyName,
firstName: req.user.name.givenName,
lastName: req.user.name.familyName,
email: req.user.emails[0].value,
provider: req.user.provider,
provider_id: req.user.id,
// profilePic: req.user.photos?.[0]?.value,
});
res.status(200).json({
success: true,
message: "success",
user: user
})
}
}
console.log("GOOGLE USER: ", user);
}
})
router.get("/googleLogin/failed", (req, res)=>{
if(req.user){
res.status(401).json({
success: false,
message: "failure",
})
}
})
I THINK the code keeps running nonstop ! thats why it giving me the error even so the collection is empty, it sign up the user then it sign up again non stop. I think that it should be a done() in the code but I didn't know how to fix it.
Passport.js Setup:
passport.use(new GoogleStrategy({
clientID: process.env.REACT_APP_GOOGLE_CLIENT_ID,
clientSecret: process.env.REACT_APP_GOOGLE_CLIENT_SECRET,
callbackURL: "/auth/google/callback",
passReqToCallback: true,
scope: ["profile", "email"],
},
function(request, accessToken, refreshToken, profile, done){
console.log(profile.emails[0].value);
console.log(profile.photos[0].value);
return done(null, profile)
}
));
passport.serializeUser((user, done)=>{
done(null, user)
})
passport.deserializeUser((user, done)=>{
done(null, user)
})
Here is my User Schema: User.js
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: false,
/* Can't create a user with the same username */
unique: true,
minlength: 3,
maxlength: 30,
},
firstName: {
type: String,
required: false,
unique: false,
maxlength: 20,
},
lastName: {
type: String,
required: false,
unique: false,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
},
repeatPassword: {
type: String,
},
profilePic: {
type: String,
default: "",
},
birthday: {
type: String,
required: false,
unique: false,
},
country: {
type: String,
required: false,
unique: false,
},
googleId: {
type: String
},
provider: {
type: String,
default: "email"
},
provider_id: {
type: String,
}
},
{ timestamps: true }
);
module.exports = mongoose.model("User", UserSchema);
I create 2 connections using TypeORM as followed:
await createConnections([
{
name: "conn1",
type: "mssql",
host: "localhost",
port: parseInt(process.env.DB_PORT || "1433"),
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
logging: true,
entities: [__dirname + "/entities/example/*{.js,.ts}"],
options: {
enableArithAbort: true,
},
},
{
name: "conn2",
type: "mssql",
host: "localhost",
port: parseInt(process.env.DB_PORT || "1433"),
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME1,
logging: true,
entities: [__dirname + "/entities/*{.js,.ts}"],
options: {
enableArithAbort: true,
},
},
]);
My Employee Entity is in conn1, User Entity is in conn2. Then I setup OneToOne between Employee and User like:
#ObjectType()
#Entity("Employees")
export class Employee extends BaseEntity {
...
#Field(() => User, { nullable: true })
#OneToOne(() => User, (user) => user.employee)
#JoinColumn({ name: "EmployeeID", referencedColumnName: "id" })
user: User | null = null;
}
#ObjectType()
#Entity({ name: "User" })
export class GMGUser extends BaseEntity {
...
#Field(() => Employee)
#OneToOne(() => Employee, (employee) => employee.user)
#JoinColumn({ name: "id", referencedColumnName: "EmployeeID" })
employee!: Employee;
}
But I still got an Error: Entity metadata for User#employee was not found or Employee#user not found after I check all connections; import path and spells are correct. I try setup relations in each of the connections are also working. But I cannot figure out how to set up relations between different connections in TypeORM. Welcome any suggestions or solutions.
I have a user model here that currently only have 2 possible roles. One is admin and one is regular user.
const userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
required: true,
default: false
}
}, { timestamps: true
})
As you can see, if the isAdmin was set to true, it will automatically become an admin otherwise its a regular user.
But things change when I added a new role like: isOwner which is I added a new field again:
const userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
required: true,
default: false
},
isOwner: {
type: Boolean,
required: true,
default: false
},, { timestamps: true
})
As you can see I added a new field isOwner at the top.
My question is given these three roles: isAdmin, isOwner and regular user, am I doing the right way to do this and manage 3 different roles? or is there a better way to do this?
Note: The way this works is that admin has the overall access to all of the job post of the owner while the regular user can only comment on the job post posted by the owner (who posted the job)
In future you may have more roles, It is best to use Array field for user roles.
array reference document
const userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
required: true,
default: false
},
roles: {
type:[String],
required: true,
default:["regular"]
}
},
{ timestamps: true
})
I have formik form with below initialValues
const initialValues1 = {
people: [
{
id: Math.random(),
email: "",
isNewUser: true,
profile: {
firstName: "",
lastName: "",
}
}
]
};
I want to validate firstName and lastName only when is isNewUser is true by Yup I am trying below but it is not working. How can validate conditionally in Formik Yup
people: Yup.array().of(
Yup.object().shape({
isNewUser: Yup.boolean(),
profile: Yup.object().shape({
firstName: Yup
.string()
.when('isNewUser', {
is: true,
then: Yup.string().required("First name is required")
}),
})
})
)
formatted code in IDE
people: Yup.array().of(
Yup.object({
isNewUser: Yup.boolean(),
profile: Yup.object().when('isNewUser', {
is: true,
then: Yup.object({
firstName: Yup.string().required('First name is required'),
})
})
})
);
isNewUser is sibling of profile attribute, so we can use it in when for defining profile schema not it's child(first_name) schema directly.
you can also specify else part using otherwise key
when({ 'attrbute', is: 'func or value', then: 'schema if is true', otherwise: 'schema if is false'})
As per docs ,
Adjust the schema based on a sibling or sibling children fields. You
can provide an object literal where the key is is value or a matcher
function, then provides the true schema and/or otherwise for the
failure condition.
So move the isNewUser to reflect as sibling. Like this:
const initialValues1 = {
people: [
{
id: Math.random(),
email: "",
//isNewUser: true, // <--------------- remove this
profile: {
firstName: "",
lastName: "",
isNewUser: true // <---------------- here.
}
}
]
};
I have a select dropdown I'd like to populate with the names of users in objects:
<select id="entityDropDown"
ng-model="selectedUser"
ng-options="user as user.name for user in users"
ng-change="getUserInfo(selectedUser)">
</select>
My object is structured as such:
users: {
1: {
name: Demo Administrator,
id: 1,
domain: null,
email: null,
isAdmin: False,
},
4: {
name: ITUN\WSS,
id: 4,
domain: i:0#.f|admembers|,
email: ,
isAdmin: False,
}
}
Try using the comprehension expression for objects:
ng-options="user as user.name for (key,user) in users"
In your case users is not an array.
So instead of
users: {
1: {
name: Demo Administrator
id: 1
domain: ITUN\demoadmin_compulite
email: simkessy#gmail.com
isAdmin: False
}
}
You want something like this
users: [
{
name: Demo Administrator
id: 1
domain: ITUN\demoadmin_compulite
email: simkessy#gmail.com
isAdmin: False
} ,
{
name: Demo Administrator
id: 2
domain: ITUN\demoadmin_compulite
email: simkessy#gmail.com
isAdmin: False
}
]