MosquitoTransport is a powerful wrapper around mongodb and express that enables developers to persist and synchronize data between their MongoDB database and frontend applications. It offers a centralized and self-hosted solution for managing server infrastructure and data, along with robust authentication, real-time data updates, scalability, and cross-platform compatibility.
Under the hood, mosquito-transport uses Mongodb to store it data, along with express, socket.io for making request and jwt for signing authentication token, so make sure you have mongodb installed before using this package.
- Data Persistence and Synchronization 🔁:
- Seamlessly persist and synchronize data between MongoDB and frontend applications, ensuring consistency across all clients.
- Self-Hosted Server 💾:
- Host your own server infrastructure, giving you full control over data storage, access, and management.
- User Authentication and Authorization 🔐:
- Easily implement user authentication and authorization using JWT (JSON Web Tokens), providing secure access control to your application's resources.
- End-to-End Encryption 🔗:
- Optionally enforce end-to-end encryption by allowing only encrypted data to be transmitted between client and server, ensuring data privacy and security.
- Real-Time Data Updates 🚨:
- Enable real-time updates to keep data synchronized across all clients in real-time, providing a seamless user experience.
- Scalability and Performance 🚛:
- Benefit from auto-scaling and high performance, allowing your application to handle varying workloads with ease.
- Cross-Platform Compatibility 📱:
- Compatible with React Native and web applications, allowing you to build cross-platform solutions with ease.
- Easy Data Backup and Restore 💿:
- Effortlessly secure your data with seamless backup and restore functionality, ensuring quick and reliable recovery whenever needed
npm install mosquito-transport mongodb --save
or using yarn
yarn add mosquito-transport mongodb
import MosquitoTransportServer from "mosquito-transport";
import { MongoClient } from "mongodb";
// create a mongodb instance
const dbInstance = new MongoClient("mongodb://127.0.0.1:27017");
dbInstance
.connect()
.then(() => {
console.log("connected to mongodb");
})
.catch((e) => {
console.error("failed to connected to mongodb");
});
// setup your server
const serverApp = new MosquitoTransportServer({
projectName: "app_name",
port: 4534, // defaults to 4291
signerKey: "random_90_hash_key_for_signing_jwt_tokens", // must be 90 length
accessKey: "some_unique_string",
externalAddress: "https://example.yourdomain.com",
mongoInstances: {
// this is where user info and tokens is stored
admin: {
instance: dbInstance,
defaultName: "ADMIN_DB_NAME",
},
// this will be the default db if no dbName was provided
default: {
instance: dbInstance,
defaultName: "DEFAULT_DB_NAME",
},
},
databaseRules: ({
auth,
collection,
value,
afterData,
beforeData,
operation,
...otherProps
}) =>
new Promise((resolve, reject) => {
// validate and authorize all incoming request to the database
if (collection === "user") {
if (afterData && auth && auth.uid === value._id) {
resolve(); // allow read/write
} else reject("You don`t own this data, stay away"); // reject read/write
} else if (collection === "other_paths") {
// blah, blah, other algorithm ...
}
}),
storageRules: ({ ...props }) =>
new Promise((resolve) => {
// validate and authorize all uploads/downloads
resolve(true); // handle read/write yourself here
}),
googleAuthConfig: {
clientID: "your_google_authentication_clientID.apps.googleusercontent.com",
},
appleAuthConfig: {
...props,
},
...otherProps,
});
your server is now ready to be deploy on node.js! 🚀. Now install any mosquito-transport client sdk and start making requests to the server.
- react-native-mosquito-transport for react native apps
- mosquito-transport-web for web platform
- mongodb-hack-middleware hacks for querying random document and fulltext search
- MosquitoTransportServer Constructor
- projectName
- signerKey
- port
- storageRules
- databaseRules
- accessTokenInterval
- refreshTokenExpiry
- accessKey
- mongoInstances
- externalAddress
- hostname
- enableSequentialUid
- mergeAuthAccount
- sneakSignupAuth
- onUserMounted
- uidLength
- enforceE2E
- e2eKeyPair
- logger
- castBSON
- ddosMap
- internals
- dumpsterPath
- preMiddlewares
- transformMediaRoute
- transformMediaCleanupTimeout
- MosquitoTransportServer Getters
- MosquitoTransportServer Methods
- getDatabase
- listenDatabase
- listenStorage
- listenHttpsRequest
- listenNewUser
- listenDeletedUser
- verifyToken
- validateToken
- invalidateToken
- getUserData
- updateUserProfile
- updateUserMetadata
- updateUserClaims
- updateUserEmailAddress
- updateUserPassword
- updateUserEmailVerify
- signOutUser
- disableUser
- getStorageSource
- createWriteStream
- writeFile
- deleteFile
- deleteFolder
- inspectDocDisconnectionTask
- extractBackup
- Extracting Backup
- Installing Backup
- Authentication Setup
the name for your mosquito-transport instance. this is required and used internally by both the backend and frontend client.
a 90 character string which is used in signing jwt access and refresh token.
the port number you want mosquito-transport instance to be running on
a function used for securing all file operations (uploadFile
, downloadFile
, deleteFile
, deleteFolder
) made by the frontend client.
a function used for securing all mongodb read and write operations made by the frontend client.
numbers of milliseconds until generated access token expires. Defaults to 1 hour
(3600000).
numbers of milliseconds until generated refresh token expires. Defaults to 1 month
(2419200000).
a random string used by the frontend client for accessing internal resources.
an object that maps names to your mongodb instance. if no dbRef
were provided, the default
mongodb instance will be used.
import MosquitoTransportServer from "mosquito-transport";
import { MongoClient } from "mongodb";
// create a mongodb instance
const dbInstance = new MongoClient("mongodb://127.0.0.1:27017");
dbInstance.connect();
const remoteInstance = new MongoClient("mongodb://other-searver.com");
remoteInstance.connect();
const serverApp = new MosquitoTransportServer({
...otherProps,
mongoInstances: {
// frontend client are prohitted from accessing this instance
admin: {
instance: dbInstance,
defaultName: "ADMIN_DB_NAME",
},
// this will be the default db if no dbRef was provided by the frontend client
default: {
instance: dbInstance,
defaultName: "DEFAULT_DB_NAME",
},
// additional instance
remoteBackup: {
instance: remoteInstance,
defaultName: "WEB_BACKUP",
},
},
});
// then you can access this via frontend client
const webInstance = new MosquitoTransport({
projectUrl: "http://localhost:4534/app_name",
accessKey: "some_unique_string",
...options,
});
webInstance
.getDatabase(
// if this is undefined, the server will use `defaultName` as the default name
"database_name",
// the name of the mongoInstances map
"remoteBackup"
)
.collection("transactions")
.findOne({ date: { $gt: 1719291129937 } })
.get();
// or access the default db
webInstance.getDatabase().collection("testing");
this should be a valid http or https link. it is used internally while signing jwt and for prefixing storage downloadUrl
when uploading a file by frontend client.
if no externalAddress
was provided, externalAddress
will be a construct as follows:
`http://${hostname || "localhost"}:${port}`;
true if you want new users to be assign a sequential uid
like 0, 1, 2, 3, 4, 5, ...,
true if you want to threat the same email address from different auth provider as a single user.
a function use in preventing signup and adding metadata before signup
import MosquitoTransportServer, { AUTH_PROVIDER_ID } from "mosquito-transport";
const blacklisted_country = ["RU", "AF", "NG"];
const serverApp = new MosquitoTransportServer({
...otherProps,
sneakSignupAuth: ({ request, email, name, password, method }) => {
const geo = lookupIpAddress(request.ip);
if (!geo) throw "Failed to lookup request location";
if (blacklisted_country.includes(geo.country))
throw "This platform is not yet available in your location";
if (method === AUTH_PROVIDER_ID.PASSWORD && password.length < 5)
throw "password is too short";
const uid = randomString(11),
lang = getCountryLang(geo?.country || "US");
return {
metadata: {
country: geo.country,
city: geo.city,
location: geo.ll,
tz: geo?.timezone,
ip: request.ip,
locale: "en",
},
uid,
};
},
});
a function that is called when a user's mosquito client sdk is authenticated and online
import MosquitoTransportServer from "mosquito-transport";
const serverApp = new MosquitoTransportServer({
...otherProps,
onUserMounted: ({ user, headers }) => {
// update the user online status
serverApp.collection("users").updateOne(
{ _id: user.uid },
{
status: "online",
onlineOn: Date.now(),
}
);
return () => {
// update the user offline status
serverApp.collection("users").updateOne(
{ _id: user.uid },
{
status: "offline",
offlineOn: Date.now(),
}
);
};
},
});
the length of generated user uid. default to 30
.
true if you want to enforce end-to-end encryption for all request made by the server
an array of string, [public key, private key]
. You can get a sample as follows:
import MosquitoTransportServer from "mosquito-transport";
const serverApp = new MosquitoTransportServer({ ...options });
console.log("pair key", serverApp.sampleE2E);
path to where mosquito-transport stores it files. Defaults to the current working directory.
this will be the first middleware that will be executed for all incoming http request to this mosquito-transport instance.
You may intercept this middleware to manage and prevent ddos attack and handle some custom route such as favicon.ico
import MosquitoTransportServer from "mosquito-transport";
const serverApp = new MosquitoTransportServer({
...otherProps,
preMiddlewares: (req, res, next) => {
// do some enforcement checking here
next();
},
});
this option helps you to transform image and video files on the fly without having to write boilerplate code for this.
All you have to do is set transformMediaRoute
to *
as follows:
import MosquitoTransportServer from "mosquito-transport";
const serverApp = new MosquitoTransportServer({
...otherProps,
transformMediaRoute: "*",
});
now you can automatically transform images and video by appending some query parameter to the url of the image or video you're accessing.
the following list the parameters available for image media
width
orw
: a number that sets the width of the imageheight
orh
: a number that sets the height of the imagetop
ort
: a number that sets the top position of the imageleft
orl
: a number that sets the left position of the imagegrayscale
orgray
: set this to1
ortrue
if you want the image in grayscaleblur
orb
: either set this totrue
to blur the image or a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where sigma = 1 + radius / 2.flip
: set totrue
or1
to flip the image about the vertical Y axis. The use of flip implies the removal of the EXIF Orientation tag, if any.flop
: set totrue
or1
to flop the image about the horizontal X axis. The use of flop implies the removal of the EXIF Orientation tag, if any.format
oro
: this set the output format of the image, can be any ofavif
,dz
,fits
,gif
,heif
,input
,jpeg
,jpg
,jp2
,jxl
,magick
,openslide
,pdf
,png
,ppm
,raw
,svg
,tiff
,tif
,v
orwebp
quality
orq
: set the quality of the image from a scale of 0 - 1.lossless
orloss
: set to1
ortrue
to use lossless compression mode
Example
the following transform the image at http://localhost:5622/storage/users/richard/photo.png
:
// resize the image to 70 width and scale the height respectively
`http://localhost:5622/storage/users/richard/photo.png?w=70` // apply grayscale to the image and set the quality to 0.3
`http://localhost:5622/storage/users/richard/photo.png?grayscale=true&q=0.3`;
the following list the parameters available for video media
width
orw
: a number that sets the width of the video.height
orh
: a number that sets the height of the video.top
ort
: a number that sets the top position of the video.left
orl
: a number that sets the left position of the video.mute
: set to1
ortrue
to mute the video.vbr
: set the bitrate of the video. Equivalent to-v:a
command of ffmpeg.abr
: set the bitrate of the audio. Equivalent to-b:a
command of ffmpeg.fps
: an integer to set the frame per seconds of the video. This parameter plays a significant role in reducing the output size and processing time of the video. Equivalent to-r
command of ffmpeg.grayscale
orgray
: set this to1
ortrue
if you want the video in grayscaleflip
: set totrue
or1
to flip the video about the vertical Y axis.flop
: set totrue
or1
to flop the video about the horizontal X axis.quality
orq
: set the quality of the video from a scale of 0 - 1.lossless
orloss
: set to1
ortrue
to use lossless compression modepreset
: set the-preset
of ffmpeg. Defaults to medium.format
oro
: this set the output format of the image, can be any ofavif
,dz
,fits
,gif
,heif
,input
,jpeg
,jpg
,jp2
,jxl
,magick
,openslide
,pdf
,png
,ppm
,raw
,svg
,tiff
,tif
,v
orwebp
Example
the following transform the video at http://localhost:5622/storage/video/lil-yatchy/range-rover-sport-truck.mp4
:
// resize the video to 200 height and scale the width respectively
`http://localhost:5622/storage/video/lil-yatchy/range-rover-sport-truck.mp4?height=200` // apply grayscale to the video, set the quality to 0.7 and set the fps to 30
`http://localhost:5622/storage/video/lil-yatchy/range-rover-sport-truck.mp4?grayscale=true&q=0.7&fps=30`;
Additional Dependency
Internally mosquito-transport uses sharp
to transform images and ffmpeg
to transform video, so make sure these library are installed before setting transformMediaRoute: '*'
yarn add sharp
This is the numbers of milliseconds to cache the transformed video media file before it is deleted. This is basically to avoid the overhead processing time next time the frontend client tries to access it. Defaults to 7 hours.
can either be a string or array containing any of the following:
all
: log all requestsauth
: log authentication requestsdatabase
: log database requestsstorage
: log storage requestsexternal-requests
: log api requestsserved-content
: log serve content requestsdatabase-snapshot
: log database snapshot eventserror
: log all internal errors
true to deserialize BSON values emited at {@link MosquitoServerConfig.databaseRules} to their Node.js closest equivalent types default value is true
this prevent ddos attack on this server instance by rate limiting request made to specific endpoint base on client ip address. the default value prevent ddos attack to auth endpoint as follows:
{
"auth": {
"signup": { "calls": 7, "perSeconds": 1800 },
"signin": { "calls": 10, "perSeconds": 600 },
"google_signin": { "calls": 7, "perSeconds": 300 }
}
}
you can also provide additional ddos config for other endpoint as follows
{
// provide config for individual endpoint
"database": { "read": {...}, "query": {...}, "write": {...}},
// limit to 10 request per 10 minute for each storage endpoint
"storage": { "calls": 10, "perSeconds": 600 }
}
disable remote client access to internal functionalities. by default all internal functionalities are enabled for remote client.
Static Content Props for storage file response. See SendFileOptions
Provide a max-age in milliseconds for http caching. This will only be applied to storage file response.
Enable or disable setting Cache-Control response header. This will only be applied to storage file response.
set cors origin for all outgoing request
the maximum size in bytes of each request payload. Default to 100MB
the maximum size in byte of each uploading request payload. Default to 10GB
get the directory where storage files are saved
quickly get an end-to-end encryption pair key for your server
get the internal express instance use
returns the db instance of mongodb.
serverApp
.getDatabase(
// if this is undefined, the server will use `defaultName` as the default name
"database_name",
// the name of the mongoInstances map
"remoteBackup"
)
.collection("transactions")
.findOne({ date: { $gt: 1719291129937 } })
.get();
// or access the default db
serverApp.getDatabase().collection("testing");
listen to insert, update and delete events from mongodb
serverApp.listenDatabase("transactions", async (snapshot) => {
console.log("transaction snapshot", snapshot);
});
listen to storage event. these event are typically made by the frontend client.
serverApp.listenStorage(async (event) => {
console.log("storage event", event);
});
listen to incoming request
// only allow authenticated user to access this endpoint
serverApp.listenHttpsRequest(
"check_user",
async (req, res, user) => {
// user will always be present
res.status(200).send({ uid: user.uid });
},
{
enforceVerifiedUser: true,
enforceUser: true,
}
);
// optionally allow un-authenticated user
serverApp.listenHttpsRequest(
"server_time",
async (req, res, user) => {
// user may be present
if (user) {
res.status(200).send({ uid: user.uid });
} else {
res.status(403).send({ error: "No user provided" });
}
},
{
validateUser: true,
}
);
// disable end-to-end encrytion for this endpoint and user authentication
serverApp.listenHttpsRequest(
"server_time",
async (req, res) => {
res.status(200).send({ currentData: Date.now() });
},
{
rawEntry: true,
}
);
listen to new user
serverApp.listenNewUser(async (user) => {
console.log("new signup", user);
});
listen to deletedUser
serverApp.listenDeletedUser((uid) => {
console.log("deleted user", uid);
});
parse jwt token
verify token to check if it was trully created using signerKey without checking against the expiry or local token reference
verify token to check if it was trully created using signerKey and checking against the expiry and local token reference
remove local reference of a token
get the user data belonging to a user
update the profile data of a user
update user metadata
update the custom claim of a user
update the email address of a user
update the user password of a user
update the verify status of a user
purge all tokens references for a user and sign-out the user immediately
disable a user
get the local source where a file is stored on the disk
stream a file to the storage directory and optionally create hash for it to reduce duplicate file storage
write a file to the storage directory and optionally create hash for it to reduce duplicate file storage
delete file in the storage directory
delete folder in the storage directory
npx extract_mosquito_backup password=your_custom_password storage=../junk dest=./backup.bin dbName=my_admin_db/my_main_db/other_db
dest
is the destination to write the backup to, can be a file path or an http/https line. Defaults tomosquito_backup.bin
.dbName
should contain list of databases to extracts seperated with forward slash or "$" to extract all databases. backup is performed on "mongodb://localhost:27017".password
is use for encrypting the backup data.storage
is the directory to storage file.
Executing only npx extract_mosquito_backup
will use "mosquito.config.js" file in the current working directory.
If the config file is not found, then command npx extract_mosquito_backup dbName=$
will executed instead.
You can provide a custom configuration file for extracting backup like:
npx extract_mosquito_backup ./path/to/custom_backup_config.js
You are expected to export extract
in your config file.
an example on how the content of the file should look like is shown below:
export const extract = {
password: "your_custom_password",
storage: "./path/to/your/storage",
dest: "https://api.mega.io/upload/backup/my_server", // can be a link or filepath
destHeaders: {
// only provide if "dest" is an http(s) link
access_key: "backup_server_password",
},
database: {
"mongodb://example.com:27017": {
// <--- database url
my_admin_db: "*", // <--- "*" to include all collection
my_main_db: ["my_custom_collection", ...more],
},
},
onMongodbOption: (dbUrl) => {
// you can also return a `MongoClient` instance
return {
auth: {
username: "my_mongodb_username",
password: "my_mongodb_password",
},
...otherProps,
};
},
};
npx install_mosquito_backup password=your_custom_password storage=../junk source=./backup.bin
password
is use for encrypting the backup datastorage
is the directory to storage filesource
is the path to the backup, can be a file path or http/https link. Defaults tomosquito_backup.bin
.
Executing only npx install_mosquito_backup
will use "mosquito.config.js" file in the current working directory
You can provide a custom configuration file for extracting backup like:
npx install_mosquito_backup ./path/to/custom_backup_config.js
You are expected to export install
in your config file.
an example on how the content of the file should look like is shown below:
export const install = {
password: "your_backup_password",
storage: "./path/to/your/storage",
source: "https://api.mega.io/retrieve/backup/my_server", // can be a link or filepath
sourceHeader: {
// only provide if "source" is an http(s) link
access_key: "backup_server_password",
},
onMongodbOption: (dbUrl) => {
// you can also return a `MongoClient` instance
return {
url: "mongodb://...your_address_here", // <--- provide this to remap dbUrl to url
auth: {
username: "my_mongodb_username",
password: "my_mongodb_password",
},
...otherProps,
};
},
};
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT