
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
mongoose-explore
Advanced tools
an opinionated and highly customizable admin interface interface generated using pug to explore your mongoose models. strongly inspired by adminjs
npm install mongoose-paginate-v2 mongoose-explore
yarn add mongoose-paginate-v2 mongoose-explore
pnpm add mongoose-paginate-v2 mongoose-explore
mongoose-paginate-v2
plugin on your modelsString
, Number
, Boolean
, and Date
as internally Model.castObject()
is used to accurately convert url-encoded data to align with the corresponding model's schema when creating and editing documentshelmet
middleware, allow the unsafe-inline
script source directive in your content security policy, as follows:app.use(helmet({
contentSecurityPolicy: {
directives: {
"script-src": ["'unsafe-inline'", "https://cdn.jsdelivr.net"]
}
}
}));
import express from "express";
import mongoose from "mongoose";
import { MongooseExplorer } from "mongoose-explore";
import paginate from "mongoose-paginate-v2";
mongoose.plugin(paginate);
const app = express();
const explorer = new MongooseExplorer({ mongoose });
app.use(explorer.rootpath, explorer.router());
await mongoose.connect(url);
app.listen(port);
voila! you now have an
admin interfaceinterface on /admin/explorer to explore and manipulate your mongoose models
all configuration options
interface MongooseExplorerOptions {
mongoose: typeof mongoose;
rootpath?: string;
datetimeformatter?: (date: Date) => string;
explorables?: string[];
query_runner?: boolean | string[];
fallback_value?: string;
version_key?: string;
show_indexes?: string;
timestamps?: {
created?: string;
updated?: string;
};
widgets?: Array<
| {
type: "stat";
title: string;
resolver: () => Promise<string | number>;
render?: (value: string | number) => string;
}
| {
type: "tabular";
title: string;
resolver: () => Promise<Record<string, any>>;
header?: boolean;
}
| {
type: "doughnut chart";
title: string;
resolver: () => Promise<{
labels: string[];
dataset: Array<{ data: number[]; label?: string }>;
}>;
}
| {
type: "pie chart";
title: string;
resolver: () => Promise<{
labels: string[];
dataset: Array<{ label?: string; data: number[] }>;
}>;
}
| {
type: "line chart";
title: string;
resolver: () => Promise<{
labels: string[];
dataset: Array<{
label: string;
data: number[];
}>;
}>;
}
| {
type: "radar chart";
title: string;
resolver: () => Promise<{
labels: string;
dataset: Array<{ label: string; data: number[] }>;
}>;
}
| {
type: "bar chart";
title: string;
resolver: () => Promise<{
labels: string[];
dataset: Array<{ label: string; data: number[] }>;
}>;
}
| {
type: "custom";
title: string;
render: (ds: DesignSystem) => string | Promise<string>;
wrapper_style?: string;
}
>;
resources: Record<string, {
explorable?: string;
creatable?: boolean;
deletable?: boolean | ((doc: mongoose.LeanDocument) => boolean);
editable?: boolean | ((doc: mongoose.LeanDocument) => boolean);
sortable?: boolean;
limit?: number;
properties?: Record<string, {
label?: string;
editable?: boolean;
sortable?: boolean;
viewable?: boolean;
filterable?: boolean;
required?: boolean | ((value?: any) => boolean);
creatable?: boolean;
textarea?: boolean;
as_image?: boolean;
renderers?: {
list?: (value: any) => string;
create?: (enumvalues: any[] | null) => string;
filter?: (enumvalues: any[] | null) => string;
view?: (value: any) => string;
edit?: (params: { value: any; enumvalues: any[] | null }) => string;
}
}>;
viewables?: string[];
filterables?: string[];
creatables?: string[];
editables?: string[];
sortables?: string[];
ref_newtab?: boolean;
virtuals?: Record<string, (doc: mongoose.LeanDocument) => string>;
show_indexes?: boolean;
query_runner?: boolean;
bulk_delete?: {
enabled: boolean;
use_document?: boolean;
};
actions?: {
customs?: {
bulk?: Array<{
operation: string;
handler: (docs: string[] | mongoose.Document[]) => void | Promise<void>;
as_documents?: boolean;
guard?: boolean | string;
element?: {
variant?: "primary" | "secondary" | "outline" | "danger" | "success" | "warning";
label?: string;
style?: string;
wrapper_style?: string;
};
}>;
document?: Array<{
operation: string;
handler: (doc: mongoose.Document) => void | Promise<void>;
applicable?: (doc: mongoose.Document) => boolean;
guard?: boolean | string;
element?: {
variant?: "primary" | "secondary" | "outline" | "danger" | "success" | "warning";
label?: string;
style?: string;
wrapper_style?: string;
};
}>;
};
create?: {
handler?: (body: Record<string, any>) => Promise<mongoose.Document>;
pre?: (body: Record<string, any>) => void | Promise<void>;
post?: (doc: mongoose.Document) => void | Promise<void>;
};
delete?: {
handler?: (doc: mongoose.Document) => Promise<mongoose.Document>;
post?: (doc: mongoose.Document) => void | Promise<void>;
};
update?: {
handler?: (doc: mongoose.Document,body: Record<string, any>) => Promise<mongoose.Document>;
pre?: (doc_id: string, body: Record<string, any>) => void | Promise<void>;
post?: (doc: mongoose.Document, body: Record<string, any>) => void | Promise<void>;
};
}
cascade_delete?:
| {
model: mongoose.Model<any>;
relation: {
local_field: string;
foreign_field: string;
}
}[]
| ((doc: any, session?: mongoose.mongo.ClientSession) => Promise<void>);
}>;
views?: Array<{
name: string;
execute: (t: { limit: number; page?: number }) => Promsie<{
docs: Record<string, any>;
total_docs?: number;
total_pages?: number;
page?: number;
prev_ppage?: number | null;
next_page?: number | null;
}>;
newtab?: boolean;
limit?: number;
render?: string | ((t: { data: any[]; ds: DesignSystem }) => string);
design_system?: boolean;
}>;
pages?: Array<{
path: string;
pug: string;
label?: string;
newtab?: boolean;
design_system?: boolean;
locals?: (req: express.Request) => Record<string, any> | Promise<Record<string, any>>;
}>;
titles?: {
home?: string;
list?: (modelname: string) => string;
view?: (modelname: string, doc: mongoose.LeanDocument) => string;
edit?: (modelname: string, doc: mongoose.LeanDocument) => string;
create?: (modelname: string) => string;
queryrunner?: (modelname: string) => string;
}
theming?: {
colors?: {
primary?: string;
secondary?: string;
danger?: string;
warning?: string;
success?: string;
border?: string;
text?: string;
};
font?: {
url: string;
family: string;
};
};
}
mongoose.model(
"User",
new mongoose.Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
profile: {
name: String,
settings: {
notifications: Boolean
}
},
role: {
type: String,
enum: ["default", "admin"],
default: "default"
},
is_banned: {
type: Boolean,
default: false
},
created_at: {
type: Date,
default: () => new Date()
},
updated_at: {
type: Date,
default: () => new Date()
}
})
);
mongoose.model("Post", new mongoose.Schema({
content: String,
author_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
}));
mongoose.model("Notification", new mongoose.Schema({
recipient_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
}));
changes the default rootpath /admin/explorer where the interface is mounted
new MongooseExplorer({ mongoose, rootpath: "/some-other-path" });
formatted string representation of dates
new MongooseExplorer({
mongoose,
datetimeformatter: (date) => datefns.format(date, "MM/dd/yyyy")
});
defaults to
date.toLocaleString("en-US", {
hour: "2-digit",
minute: "2-digit",
hour12: true,
day: "2-digit",
month: "long",
year: "numeric"
}); // January 1, 1970 at 12:00 AM
defines which resources should be explorable. if set, only the resources included in it are explorable, rendering any explorable: true
setting under individual resource configuration irrelevant
new MongooseExplorer({
mongoose,
explorables: ["User"]
});
defines whether query runner is enabled for specific resources or for all resources. set to true
to enable for all resources, or provide an array of string resource names to enable it selectively for the specified resources, rendering individual query_runner
configurations for each resource irrelevant
new MongooseExplorer({
mongoose,
query_runner: ["User"]
});
html or string to be rendered if a property has no value. defaults to -
new MongooseExplorer({
mongoose,
fallback_value: "<span style='font-style: italic'>???</span>"
});
the version key of your schema, if enabled. defaults to __v
determines whether indexes for all resources should be shown or not
prevents these fields from being modified among other things
new MongooseExplorer({
mongoose,
timestamps: {
/**
* default
*/
created: "created_at",
updated: "updated_at"
}
});
new MongooseExplorer({
mongoose,
widgets: [
{
/**
* represents a statistical widget displaying a single value.
* useful for showcasing aggregated data or quick insights
*/
type: "stat",
/**
* title of the widget
*/
title: "Total Users",
/**
* a function that resolves and retrieves the statistical value
*/
resolver: () => Promise.resolve(20000),
/**
* custom rendering function for the displayed value.
* can return a plain string or html string to be rendered instead of
* the default value (resolver's return)
*/
render: (value) => `<p style="font-style: italic">${value.toLocaleString()}</p>`
},
...
]
});
{
type: "tabular",
title: "Recent Orders",
resolver: () => Promise.resolve([
{
customer: "john",
product: "laptop",
quantity: 2,
date: "2024-03-05 10:30",
status: "shipped"
},
{
customer: "mary",
product: "smartphone",
quantity: 1,
date: "2024-03-04 15:45",
status: "delivered"
},
...
]),
/**
* determines whether table header is shown or not.
* defaults to 'false'
*/
header: true
}
{
type: "bar chart",
title: "Employee Performance",
resolver: () => Promise.resolve({
labels: ["john", "sarah", "michael", "emily", "james"],
dataset: [
{
label: "quarter 1",
data: [85, 92, 78, 88, 95]
},
{
label: "quarter 2",
data: [78, 85, 90, 75, 88]
},
{
label: "quarter 3",
data: [92, 88, 95, 82, 90]
}
]
})
}
{
type: "pie chart",
title: "Expenses",
resolver: () => Promise.resolve({
labels: ["rent", "utilities", "groceries", "entertainment", "others"],
dataset: [{
data: [800, 150, 200, 100, 50]
}]
})
}
{
type: "doughnut chart",
title: "Project Tasks Distribution",
resolver: () => Promise.resolve({
labels: ["design", "development", "testing", "documentation"],
dataset: [{
data: [25, 40, 20, 15]
}]
})
}
{
type: "line chart",
title: "Project Progress",
resolver: () => Promise.resolve({
labels: ["week 1", "week 2", "week 3", "week 4", "week 5"],
dataset: [
{
label: "development progress",
data: [20, 40, 60, 80, 100]
},
{
label: "degradation progress",
data: [100, 80, 60, 40, 20]
}
]
})
}
{
type: "radar chart",
title: "Skills",
resolver: () => Promise.resolve({
labels: [
"coding",
"design",
"communication",
"problem solving",
"time management",
"teamwork"
],
dataset: [
{
label: "team A",
data: [80, 70, 85, 90, 75, 65]
},
{
label: "team B",
data: [90, 65, 80, 85, 70]
}
]
})
}
see custom pages section for more details on ds
(design system)
{
type: "custom",
title: "Custom Widget",
render: (ds) => `<div>${ds.components.button({ label: "click", variant: "primary" })}</div>`,
/**
* extra styling to be applied to the wrapper element (widget element).
*/
wrapper_style: "width: 250px; height: 250px"
}
new MongooseExplorer({
mongoose,
/**
* each resource corresponds a to mongoose model name
*/
resources: {
User: {
/**
* determines whether the model is explorable.
* set to 'false' to disable
*/
explorable: true,
/**
* determines whether new documents can be created for this model.
* set to 'false' to prevent the creation of new documents
*/
creatable: true,
/**
* determines whether documents of the model are deletable.
* set to 'false' to prevent the deletion of documents all documents and 'true'
* to allow deletion of all documents. defaults to 'true'.
* can also be a function which takes in the mongoose lean document as the argument
* and should return boolean to either allow or disallow deletion of all documents
* that meet the specified condition
*/
deletable: (user) => user.role !== "admin",
/**
* determines whether documents are editable.
* set to 'false' to prevent the edition of all documents and 'true' to allow
* edition of all documents. defaults to 'true'.
* can also be a function which takes in the mongoose lean document as the argument
* and should return boolean to either allow or disallow edition of all documents
* that meet the specified condition
*/
editable: true,
/**
* enables or disables sorting criteria.
* set to 'true' to allow sorting (default), or 'false' to prevent sorting
*/
sortable: true,
/**
* number of documents returned per query.
* can also be changed from the UI.
* defaults to '15'
*/
limit: 10,
/**
* defines properties which are shown in the UI.
* this takes precedence over the viewable options on the property level
*/
viewables: ["_id", "email", "profile.name"],
/**
* defines properties which can be used as a filtering criteria.
* this takes precedence over the filterable options on the property level
*/
filterables: ["_id", "email", "created_at", "updated_at"],
/**
* defines properties which can be created in the UI.
* this takes precedence over the creatable options on the property level
*/
creatables: ["email", "password", "profile.name"],
/**
* defines properties which can be edited.
* this takes precedence over the editable options on the property level
*/
editables: ["profile.name"],
/**
* defines properties which can be sorted.
* this takes precedence over the sortable options on the property level
*/
sortables: ["created_at"],
/**
* determines whether to open a referenced document in a new tab or not.
* set to 'true' to make referenced documents open in a new tab
*/
ref_newtab: false,
/**
* avoid confusing this with mongoose's virtuals, as they are distinct
* and separate functionalities.
*
* dynamically inject additional properties when listing and viewing
* documents. when listing documents (i.e, in a table), this will
* dynamically add a new table header. the header corresponds to the
* object's key, while the resulting html or string represents the
* content in the table data cell. likewise, when viewing an individual
* document, this will dynamically add a new property. the property key
* corresponds to the object's key, while the resulting html or string
* represents the content associated with that key
*/
virtuals: {
another: (user) => "i will be rendered under 'another' property",
field: (user) => "<p style='font-style: italic'>so will i, under 'field' property</p>"
},
/**
* determines whether the indexes should be shown or not
*/
show_indexes: true,
/**
* query runner feature enables you to dynamically compose and execute
* custom queries on the associated mongoose model and instantly view the result.
* when enabled, you can interactively input queries and query options.
* it supports `find`, `findOne`, `aggregate`, `countDocuments`, and
* `estimatedDocumentCount` mongoose operations.
* query options are applicable to `find` and `findOne` operations only.
* not enabled by default
*
* note: the default setting for `lean: true` is applied to `find` and `findOne`
* operations, and this behavior cannot be overridden
*/
query_runner: true,
bulk_delete: {
/**
* determines whether bulk deletion of documents is enabled.
* defaults to 'false'
*/
enabled: true,
/**
* determines how the deletion is performed.
* if set to 'true', individual documents are deleted using
* `Document.deleteOne()`. if not specified or set to 'false', the
* default behavior is to use `Model.deleteMany()`, which triggers the
* 'deleteMany' hook (if any). if you opt to use `use_document: true`,
* ensure that your 'deleteOne' hook (if any) is appropriately
* configured
*
*/
use_document: true
},
/**
* configuring each property of your mongoose schema
*/
properties: {
password: {
/**
* allows you to specify a custom display name for the property key
* in the UI. when set, this label will be used in place of the
* default property key when rendering in the interface
*/
label: "secret",
/**
* determines whether password is editable.
* set to 'false' to prevent the edition of passwords
*/
editable: true,
/**
* determines if the property can be used for sorting criteria
*/
sortable: true,
/**
* determines whether password is shown in the UI.
* set to 'false' to disallow it from being shown
*/
viewable: true,
/**
* determines whether password can be used as a filtering criteria
* when querying. set to 'false' to disable
*/
filerable: true,
/**
* determines whether password is a required property when creating
* and editing. set to 'false' to make it optional.
* note: this is purely for UI purposes and does not modify it on the
* schema level.
* can also be a function which returns a boolean. it takes the value
* of the property as a function parameter only in the context of editing,
* and is `undefined` in other contexts
*/
required: true,
/**
* allows the creation of password field in the UI.
* set to 'false' to disallow
*/
creatable: true,
/**
* specifies whether password should be rendered as a textarea
* note: this is only considered if property is a String
*/
textarea: false,
/**
* if set to 'true', displays the image instead of the url string,
* assuming the value is a valid image url
*/
as_image: true,
/**
* render functions for generating html specific to various operations.
*/
renderers: {
/**
* function for generating html which will be rendered in this
* property's field (password, in this case) when listing (i.e, the
* table view) documents. returned string or html is rendered inside
* a '<td>' fyi.
* useful if customization is desired, as the default behavior is to render the value
*/
list: (value) => `${value}`,
/**
* function for generating html which will be rendered in this
* property's field when creating documents. enum values maybe null
* or array of values (depending on the value type on the schema).
* you may loop over the enum and render a select as you wish.
* ensure that you include the 'name' property
*/
create: (enumvalues) => `<input type="password" name="password" />`,
/**
* function for generating html which will be rendered in this
* property's field when adding sorting criteria. enumvalues maybe
* be null or array of values (depending on the value type on the
* schema). you may loop over the enum and render a select
* as you wish.
* ensure that you include the 'name' property
*/
filter: (enumvalues) => `<input type="text" name="password" />`,
/**
* function for generating html which will be rendered in this
* property's field when viewing an individual document.
* useful if customization is desired, as the default behavior is to render the value
* in a <p> tag if value is primitive. it the value is an array or object, the array
* or object is looped over and displayed in a <ul><li> structure, with the actual
* values in <span> tags
*/
view: (value) => `<p>${value}</p>`,
/**
* function for generating html which will be rendered in this
* property's field when editing a document.
* ensure that you include the 'name' property
*/
edit: ({ value, enumvalues }) => `<input type="password" name="password" />`
}
},
/**
* target properties nested within an object by using dot notation
*/
"profile.settings.notifications": {}
},
actions: {
customs: {
/**
* custom actions for multiple selected documents
*/
bulk: [{
/**
* must be unique and contain only lowercase letters and '-'
*/
operation: "ban-all",
/**
* bulk-ban operation implementation.
* the 'docs' parameter can either be an array of strings
* containing the ids of the selected documents or an array of
* lean documents if 'as_documents' is set to 'true'
*/
handler: (docs) => {},
/**
* determines the format of the 'docs' parameter in the bulk
* operation. if set to 'true', the 'docs' parameter will be an
* array of lean documents. if set to 'false' or omitted, the
* 'docs' parameter will be an array of strings containing document
* ids. defaults to 'false'
*/
as_documents: true,
/**
* window.confirm prompt message before executing handler.
* if set to 'true' default message "are you sure?" is used
* otherwise no prompt, and handler is executed immediately.
* defaults to 'false'
*/
guard: "are you sure you want to ban these users?",
/**
* html button (actually an <a>) the operation is rendered in
*/
element: {
/**
* specifices the visual variant for the button.
* defaults to 'outline'
*/
variant: "danger",
/**
* label of the operation's button
* defaults to the operation name
*/
label: "ban all",
/**
* extra styling to the operation's button
*/
style: "color: red; font-size: 12px",
/**
* extra styling to the operation's button wrapper element
*/
wrapper_style: "border: 1px solid gold"
}
}],
/**
* custom actions for individual documents
*/
document: [{
/**
* must be unique and contain only lowercase letters and '-'
*/
operation: "ban-user",
/**
* ban-user operation implementation
*/
handler: async (user) => {
user.is_banned = true;
await user.save();
},
/**
* determines whether the action is applicable to the document
*
*/
applicable: (user) => user.role !== "admin",
guard: "are you sure you want to ban this user?",
element: {
variant: "danger",
label: "ban",
style: "color: red; font-size: 12px",
wrapper_style: "border: 1px solid gold"
}
}]
},
create: {
/**
* function to override how documents are created.
* defaults to `Model.create()`
*/
handler: (body: req.body) => Promise<mongoose.Document>,
/**
* function executed before creating a document. think of it as 'pre
* hook'. runs before mongoose's validations/hooks, if any
*/
pre: (body: req.body) => void | Promise<void>,
/**
* function executed after creating a document. think of it as 'post
* hook'. runs after mongoose's post hooks, if any
*/
post: (user: mongoose.Document) => void | Promise<void>
},
delete: {
/**
* function to override how documents are deleted. must return the
* mongoose document. defaults to `Document.deleteOne()`
*/
handler: (user: mongoose.Document) => Promise<mongoose.Document>,
/**
* function executed after deleting a document. think of it as 'post
* hook'. runs after mongoose's post hooks, if any.
*/
post: (user: mongoose.Document) => void | Promise<void>
},
/**
* note: if you find that none of your update actions are running, it's
* due to an empty `req.body`
*/
update: {
/**
* function to override how documents are updated. must return the
* mongoose document. defaults to `Document.set(body).save()`
*/
handler: (user: mongoose.Document, body: req.body) => Promise<mongoose.Document>,
/**
* function executed before updating a document.
* think of it as 'pre hook'.
* runs before mongoose's validations/hooks, if any
*/
pre: (doc_id: string, body: req.body) => void | Promise<void>,
/**
* function executed after updating a document.
* think of it as 'post hook'.
* runs after mongoose's post hooks, if any
*/
post: (user: mongoose.Document, body: req.body) => void | Promise<void>
}
},
/**
* cascading delete behavior for dependent documents. can be an array of
* objects, each with a `model` (mongoose model of dependent documents),
* `local_field` (field in the current document), and `foreign_field`
* (field in the dependent documents referencing the current document);
* or a function that handles deletion manually with the deleted document
* and an optional mongodb client session, returning a promise.
* if a function is passed, the session is defined if mongodb is in a replica
* set, otherwise, it is undefined.
* uses transaction to delete documents if mongodb is in a replica set,
* or Promise.all otherwise.
* note: cascade delete is currently not supported for bulk delete
*/
cascade_delete: [
{
model: Post,
relation: {
foreign_field: "author_id",
local_field: "_id"
}
},
{
model: Notification,
relation: {
foreign_field: "recipient_id",
local_field: "_id"
}
}
]
/**
cascade_delete: async (doc, session) => {
await Post.deleteMany({ author_id: doc._id }, { session });
await Notification.deleteMany({ recipient_id: doc._id }, { session });
}
**/
}
}
});
all
able
's aretrue
by default
configuration for dynamic data views in the UI. allows you to define custom queries which dynamically fetch and provide the data for specific sections in the UI. the concept is similar to PostgreSQL views where you dont have to type the query each time you need it
new MongooseExplorer({
mongoose,
views: [{
name: "new users",
execute: async ({ limit, page = 1 }) => {
const thirtydays = new Date();
thirtydays.setDate(thirtydays.getDate() - 30);
const users = await User.aggregate([
{
$match: {
role: "default",
created_at: {
$gte: thirtydays
}
}
},
{
$limit: limit
},
{
$addFields: {
a_new_property: "your value here"
}
}
]);
return { docs: users };
/**
* if pagination is desired for your query, include the computed values of
* `total_docs`, `total_pages`, `prev_page`, `next_page` and `page` properties
* in the return object. this enables pagination controls in the UI
*/
},
/**
* determines if the view is to be opened in a new tab.
* defaults to 'false'
*/
newtab: true,
/**
* default number of documents returned per query.
* can be changed from the UI and defaults to 15.
* accessible via the `limit` variable in the execute function
*/
limit: 20,
/**
* the view's page title
*/
title: "New Users",
/**
* defines the rendering mechanism for the view's data.
* if a string, it must be an absolute path to a pug file, where
* 'data' (the return value from execute) and ds (Design system)
* are accessible as local variables.
* if a function, it accepts 'data' (from execute function) and 'ds'
* (design system) as parameters, and must return html markup as string.
* 'ds' may be undefined if 'design_system' is set to 'false'
*/
render: path.join(process.cwd(), "assets", "views", "new-users.pug"),
/**
* determines whether to inject our default styles to your page/markup
*/
design_system: true,
/**
* configuration of each property from the data returned from the execute query
*/
properties: {
a_new_property: {
/**
* returned string or html is rendered in this property's field inside a '<td>' tag.
* useful if customization is desired, as the default behavior is to render the value
*/
render: (value) => `<span></span>`;
}
}
}]
});
new MongooseExplorer({
mongoose,
titles: {
home: "explorer",
list: (modelname) => `${modelname} model`,
view: (modelname, doc) => `view ${modelname} document - ${doc._id}`,
edit: (modelname, doc) => `edit ${modelname} document - ${doc._id}`,
create: (modelname) => `create ${modelname} document`,
queryrunner: (modelname) => `${modelname} query runner`
}
});
new MongooseExplorer({
mongoose,
pages: [
{
/**
* path where the page will be mounted.
* notice how it's not prefixed with '/'
*/
path: "custom",
/**
* absolute path to the custom page's pug file
*/
pug: path.join(process.cwd(), "views", "custom.pug"),
/**
* label of the page's link element
* defaults to 'path' value
*/
label: "Custom",
/**
* determines where the page should be opened in a new tab or not.
* set to 'true' to open the page in a new tab
*/
newtab: false,
/**
* determines whether to inject default styles into your custom page using our design system.
* this also ships a 'ds' locals object accessible in your pages and exposes
* '--primary', '--danger', '--secondary', '--success', '--warning', '--text-color', and
* '--border-color' css variables
*
* ```
* type Variant = "primary" | "secondary" | "outline" | "success" | "warning" | "danger";
*
* const ds = {
* font: {
* url: "",
* family: "",
* },
* colors: {
* primary: "",
* secondary: "",
* danger: "",
* warning: "",
* success: "",
* text: "",
* border: "",
* },
* styles: {
* /**
* * default styling for the main content container use in pages. its a <div>
* * encapsulating the primary content of each page to ensure consistent styling,
* * including a border, fixed width of 400px, centered alignment, and padding
* */
* contentdiv: ""
* },
* components: {
* title: (t: { label: string; style?: string; }) => <h2>,
* link: (t: { label: string; href: string; newtab?: boolean; style?: string; }) => <a>,
* dash: (t?: { style?: string; }) => <hr>,
* button: (t: { label: string; variant?: Variant; style?: string }) => <button>
* }
* }
* ```
*/
design_system: true,
/**
* provide local variables for your custom page. the returned
* object will be merged with the below default locals object
*
* ```
* const locals = {
* /*
* * use this function to build links to other custom pages.
* * ex: `buildlink("another-custom-page")`
* */
* buidlink: (path: string) => string,
* rootpath: "",
* ds: "**see above**"
* }
* ```
*/
locals: (req) => ({ user: req.user })
}
]
});
self-explanatory
new MongooseExplorer({
mongoose,
theming: {
colors: {
primary: "gold",
secondary: "purple",
danger: "red",
success: "green",
warning: "yellow",
border: "gray",
text: "black"
},
font: {
url: "google font url here",
/**
* must be of this format: `"Somefont", sans-serif;`.
* notice the `;` at the end
*/
family: "font family here"
}
}
});
defaults to
{
colors: {
primary: "#4f46e5",
secondary: "#DCE546",
danger: "#dc2626",
success: "#059669",
warning: "#d97706",
border: "#4B5563",
text: "#fff",
},
font: {
url: "https://fonts.googleapis.com/css2?family=Finlandica&display=swap",
family: `"Finlandica", sans-serif;`
}
}
[!NOTE]
background-color
is set to#101113
, which is a very dark shade and is not customizable. if you choose to customize the colors, be mindful of the background color as it sets the foundation for the overall appearance of the interface
/
characters. simply place your regex pattern between these delimeters. for example, /doe/i/
, gets converted to /doe/i
and translated to the mongodb $regex
operatorpre
hook, the request fails and the operation is not executed; however, if an error is thrown from any post
hook, the request still succeeds, and the error is suppressedviewable: false
as not doing so might lead to unexpected behaviorsFAQs
<!-- markdownlint-disable MD001 MD033 -->
The npm package mongoose-explore receives a total of 0 weekly downloads. As such, mongoose-explore popularity was classified as not popular.
We found that mongoose-explore demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.