Expected behavior
I am trying to set up forest admin in the dev environment for my new project (Express + Mongoose) in a similar way as I did for one of my previous project. Ideally, forest admin should connect to the app.
Actual behavior/ Failure Logs
But I am getting the following error:
[forest] 🌳🌳:deciduous_tree: An error occured while computing the Forest schema. Your application schema cannot be synchronized with Forest. Your admin panel might not reflect your application models definition. Cannot convert undefined or null to object
{
"stack": "TypeError: Cannot convert undefined or null to object\n at Function.values (<anonymous>)\n at /home/akode/Documents/WorkProjects/MConnectServer/node_modules/forest-express/dist/services/models-manager.js:25:37\n at Array.reduce (<anonymous>)\n at ModelsManager._flatConnectionsModels (/home/akode/Documents/WorkProjects/MConnectServer/node_modules/forest-express/dist/services/models-manager.js:24:41)\n at ModelsManager._generateModelList
Code Snippets:
In the server.js file, I am setting up the forest admin and importing the schema.
import express from 'express';
import cors from 'cors';
const { userSchema } = require('./models/User-Model');
const forest = require('forest-express-mongoose');
let allowedOrigins = [/\.forestadmin\.com$/, /localhost:\d{4}$/]
if (process.env.CORS_ORIGINS) {
allowedOrigins = allowedOrigins.concat(process.env.CORS_ORIGINS.split(','));
}
const corsConfig = {
origin: allowedOrigins,
maxAge: 86400, // NOTICE: 1 day
credentials: true,
};
app.use('/forest/authentication', cors({
...corsConfig,
origin: corsConfig.origin.concat('null')
}));
const connectForestAdmin = async()=>{
try {
app.use(
await forest.init({
envSecret: process.env.FOREST_ENV_SECRET,
authSecret: process.env.FOREST_AUTH_SECRET,
objectMapping: userSchema,
connections: { default: connection},
})
);
} catch (error) {
return error
}
}
In the schema file I have the following code:
import { model, Schema, Model, Document } from "mongoose";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
interface UserBaseDocument extends Document {
generateToken(): string;
tokens: any
}
interface UserInterface extends UserBaseDocument, Document {
method: string;
local: {
email: string;
password: string;
isVerified: boolean;
isEmailVerified: boolean;
otp: string;
};
google: {
email: string
emailVerified: boolean
};
localMobile: {
phone: string;
countryCode: string,
otpVerified: boolean;
otp: string;
};
tokens: any,
agreeToPolicy: boolean,
createdAt: number;
updatedAt: number;
}
const userSchema = new Schema(
{
method: {
type: String,
enum: ["local", "localMobile", "google"],
required: true,
},
role: {
type: String,
enum: ["professional", "institution"],
required: true,
},
agreeToPolicy: {
type: Boolean,
required: true
},
local: {
email: {
type: String,
},
password: {
type: String,
},
isVerified: {
type: Boolean,
default: false,
},
isEmailVerified: {
type: Boolean,
default: false,
},
otp: {
type: String,
},
},
localMobile: {
phone: {
type: String,
},
countryCode: {
type: String
},
otp: {
type: String,
default: null,
},
otpVerified: {
type: Boolean,
default: false,
},
},
google: {
email: {
type: String,
},
emailVerified: {
type: Boolean,
}
},
tokens: [{ token: { type: String, required: true } }],
createdAt: { type: Number },
updatedAt: { type: Number },
},
{ writeConcern: { w: "majority", wtimeout: 5000 } }
);
userSchema.pre<UserInterface>("validate", function (next) {
const user = this;
if (
this.isNew &&
this.method === "local" &&
!this.isModified(this.local.password)
) {
this.createdAt = this.updatedAt = Date.now();
const salt = bcrypt.genSaltSync(Number(process.env.SALT_ROUND));
const hash = bcrypt.hashSync(user.local.password, salt);
// generateToken
user.local.password = hash;
}
next();
});
userSchema.methods.generateToken = function (this: UserInterface) {
const user = this;
const payload = {
userId: this._id,
};
const jwtToken = jwt.sign(payload, process.env.JWT_SIG, { expiresIn: "5d" });
user.tokens.push({ token: jwtToken });
user.save().then(() => {
return jwtToken;
});
return jwtToken;
};
const Users: Model<any> = model("users", userSchema);
export { Users, userSchema };
Could you please help me out with what’s wrong with the configuration.