
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
Mango is a powerful, code-first CMS built on MongoDB that emphasizes configuration through code. This documentation covers how to configure and use Mango CMS through the `config` folder.
Mango is a powerful, code-first CMS built on MongoDB that emphasizes configuration through code. This documentation covers how to configure and use Mango CMS through the config folder.
Collections are the foundation of Mango CMS. Each collection represents a MongoDB collection and is defined in the config/collections directory.
export default {
// Required: Singular form of collection name
singular: 'project',
// Optional: Enable real-time updates via WebSocket
subscribe: true,
// Define collection fields
fields: {
title: String,
description: String,
// ... more fields
},
}
singular: Required. The singular form of your collection name (e.g., 'project' for projects)subscribe: Optional. Enable WebSocket subscriptions for real-time updatesformatRequest: Optional. Custom request formatting before processingUse formatRequest to modify incoming requests before they're processed:
export default {
singular: 'project',
formatRequest(request) {
// Example 1: Add default search criteria
if (request.method === 'read') {
request.search = {
...request.search,
status: 'active'
}
}
// Example 2: Convert create to update for duplicate unique fields
if (request.method === 'create' && request.document.address) {
const existing = await readEntry({
collection: 'project',
search: { address: request.document.address }
})
if (existing) {
request.method = 'update'
request.document.id = existing.id
}
}
return request
}
}
Mango provides several built-in field types and allows for custom field creation.
import fields from '@cms/1. build/fields'
const { Relationship, Timestamp, File, Image } = fields
export default {
fields: {
// Basic Types
title: String,
count: 'Int',
isActive: Boolean,
// Complex Types
createdAt: Timestamp(),
thumbnail: Image(),
attachment: File(),
author: Relationship({
collection: 'user',
single: true,
}),
tags: Relationship({
collection: 'tag',
}),
},
}
Relationship({
collection: 'user', // Required: Target collection
single: true, // Optional: Single relationship (default: false)
required: true, // Optional: Field is required
default: null, // Optional: Default value
})
{
fields: {
fullName: {
type: String,
computed: (document) => {
return `${document.firstName} ${document.lastName}`
},
// Optional: Cache expiration in seconds
expiresCache: 3600,
// Optional: Values to provide to the computed function
provide: ['firstName', 'lastName']
}
}
}
Fields can transform data on input and output using translateInput and translateOutput:
{
fields: {
price: {
inputType: String, // Input comes as string
type: 'Int', // Stored as integer
// Transform input before saving
translateInput: (value, { request, index, parentValue }) => {
// Convert string price to number and handle currency
return parseFloat(value.replace('$', '')) * 100
},
// Transform output before sending to client
translateOutput: (value, { request, document }) => {
// Convert stored cents to dollars with currency format
return `$${(value / 100).toFixed(2)}`
}
}
}
}
Create custom fields in config/fields/ directory:
// config/fields/currency.js
export default {
type: 'Currency',
inputType: String,
translateInput(value, { request }) {
return parseFloat(value.replace('$', '')) * 100
},
translateOutput(value, { request, document }) {
return `$${(value / 100).toFixed(2)}`
},
validate(value, { request }) {
if (typeof value !== 'number' || isNaN(value)) {
throw new Error('Invalid currency value')
}
},
}
Using custom fields in collections:
import Currency from '../fields/currency'
export default {
singular: 'product',
fields: {
name: String,
price: Currency(),
salePrice: Currency(),
},
}
Mango provides flexible permission control at the collection level.
export default {
permissions: {
// Define permissions for each role in member.roles
public: ['create'],
owner: ['read', 'update', 'delete'],
},
}
export default {
async permissions(request) {
// For create requests, always check member permissions
if (!request.id) {
return {
authorized: request.member?.roles.includes('editor'),
}
}
// For existing documents, check ownership or team access
const document = request.originalDocument
return {
authorized:
// Member is the owner
document.createdBy === request.member?.id ||
// Member is part of the document's team
document.team?.includes(request.member?.id) ||
// Member has admin role
request.member?.roles.includes('admin'),
}
},
}
Hooks allow you to execute code at different stages of the CRUD operations.
export default {
hooks: {
created: async ({ request, response, document }) => {
// Handle post-creation tasks
console.log('New document created:', document.id)
},
read: async ({ request, response, document }) => {
// Handle during read operation
console.log('Document accessed:', document.id)
},
updated: async ({ request, response, document, originalDocument }) => {
// Handle post-update tasks with access to previous version
console.log('Document updated from:', originalDocument, 'to:', document)
},
deleted: async ({ request, response, document, originalDocument }) => {
// Handle post-deletion tasks with access to deleted document
console.log('Document deleted:', document.id)
console.log('Original state:', originalDocument)
},
},
}
Create custom endpoints by exporting an object structure in config/endpoints/index.js:
import { sendEmail } from '../services/email'
export default {
contact: {
// POST /endpoints/contact
async post(req) {
const { name, email, message } = req.body
// Validate required fields
if (!name || !email || !message) {
return {
success: false,
error: 'Missing required fields',
}
}
// Send email
await sendEmail({
to: 'support@example.com',
subject: `Contact Form: ${name}`,
text: `From: ${name} (${email})\n\n${message}`,
})
return {
success: true,
message: 'Thank you for your message',
}
},
support: {
// POST /endpoints/contact/support
async post(req) {
const { ticketId, message } = req.body
// Create support ticket
const ticket = await createSupportTicket({
id: ticketId,
message,
member: req.member.id,
})
return {
success: true,
ticket,
}
},
},
},
}
Plugins extend Mango's functionality through a structured folder in config/plugins:
config/plugins/
my-plugin/
collections/
widget.js
gadget.js
fields/
customField.js
endpoints/
index.js
Example plugin structure:
// config/plugins/my-plugin/collections/widget.js
export default {
singular: 'widget',
fields: {
name: String,
configuration: {...}
}
}
// config/plugins/my-plugin/fields/customField.js
export default {
type: 'CustomField',
// field implementation
}
// config/plugins/my-plugin/endpoints/index.js
export default {
widgets: {
async get(req) {
return { success: true }
}
}
}
Extend the built-in member collection by creating config/config/users.js:
import fields from '@cms/1. build/fields'
const { Relationship, Image } = fields
export default {
// Extend the member collection with custom fields
fields: {
// Built-in fields: email, password, roles
// Add custom fields
displayName: String,
avatar: Image(),
timezone: {
type: String,
default: 'UTC',
},
teams: Relationship({
collection: 'team',
}),
preferences: {
fields: {
theme: String,
notifications: Boolean,
},
default: {
theme: 'light',
notifications: true,
},
},
},
// Add member-specific hooks
hooks: {
created: async ({ document }) => {
// Send welcome email
await sendWelcomeEmail(document.email)
},
updated: async ({ document, originalDocument }) => {
// Handle email change verification
if (document.email !== originalDocument.email) {
await sendEmailVerification(document.email)
}
},
},
// Custom member permissions
permissions: {
owner: ['read', 'update'],
admin: ['create', 'read', 'update', 'delete'],
},
}
The user configuration allows you to:
Member documents will automatically include these custom fields alongside the built-in authentication fields.
# Install nginx
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx
# Install Node
sudo apt update
sudo apt install -y nodejs
sudo apt install -y npm
sudo npm install -g n
sudo n stable
export PATH="/usr/local/bin:$PATH"
source ~/.bashrc # or source ~/.zshrc
sudo apt remove -y nodejs
node -v
# Install MongoDB
sudo apt-get install gnupg curl
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo systemctl start mongod
sudo systemctl enable mongod
# Install PM2
sudo npm install -g pm2
pm2 startup
# Run ssh-keygen and add the public key to your keys in GitHub
# Clone your repo and build
# Add your dist folder to the nginx config at /etc/nginx/sites-available/default
# Configure Permissions
sudo chown -R www-data:www-data /root/Sites/{{repo}}/front/dist
sudo chmod -R 755 /root/Sites/{{repo}}/front/dist
sudo chmod +x /root
sudo chmod +x /root/Sites
sudo chmod +x /root/Sites/{{repo}}
sudo chmod +x /root/Sites/{{repo}}/front
# Ensure the site is working by going to the IP address in your browser
# Add your site DNS to CloudFlare and point it to the IP address
# Install Certbot
apt install certbot -y
sudo apt install python3-certbot-nginx
certbot
# Install redis
sudo apt install redis-server -y
sudo systemctl enable redis-server
FAQs
Mango is a powerful, code-first CMS built on MongoDB that emphasizes configuration through code. This documentation covers how to configure and use Mango CMS through the `config` folder.
The npm package mango-cms receives a total of 269 weekly downloads. As such, mango-cms popularity was classified as not popular.
We found that mango-cms demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer 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
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.