Skip to content

esbozos/nodejs-nets-core

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NODEJS NETS CORE

npm version CI License: MIT Node.js Version npm downloads TypeScript

A comprehensive Express.js framework for Node.js applications providing lazy API request handlers, authentication with OTP verification codes, role-based access control (RBAC), email notifications, push notifications and common tasks.

This is a port of django-nets-core for Node.js/Express applications.

Features

  • 🔐 Complete Authentication System

    • OTP-based authentication with email verification codes
    • OAuth2 token management (access & refresh tokens)
    • Device management and tracking
    • Session management with Redis
  • 👥 Role-Based Access Control (RBAC)

    • Flexible permission system
    • Multi-project support with project-level roles
    • Role inheritance and custom permissions
    • Middleware for route protection
  • 📧 Email System

    • Template-based emails with Handlebars
    • Queue support for batch sending
    • Domain blacklist/whitelist
    • HTML and text alternatives
    • Customizable footers
  • 📱 Push Notifications

    • Firebase Cloud Messaging integration
    • Device token management
    • Batch notifications to user devices
    • Error handling and token cleanup
  • 🛡️ Security

    • JWT token authentication
    • CSRF protection
    • Rate limiting
    • Helmet security headers
    • IP tracking and validation
  • 🔧 Developer Experience

    • TypeScript support
    • Request parameter validation
    • Automatic type conversion
    • Comprehensive error handling
    • Logging utilities

Installation

npm install nodejs-nets-core
# or
yarn add nodejs-nets-core

Quick Start

import express from 'express';
import { NetsCoreApp, requestHandler, RequestParam } from 'nodejs-nets-core';

const app = express();

// Initialize nets-core
const netsCore = new NetsCoreApp({
  database: {
    dialect: 'postgres',
    host: 'localhost',
    port: 5432,
    database: 'mydb',
    username: 'user',
    password: 'password',
  },
  redis: {
    host: 'localhost',
    port: 6379,
  },
  jwt: {
    secret: 'your-secret-key',
    accessTokenExpire: '30d',
  },
});

// Apply middleware
netsCore.applyMiddleware(app);

// Define a protected route with parameter validation
app.post(
  '/api/example',
  requestHandler({
    params: [
      new RequestParam('name', 'string'),
      new RequestParam('age', 'int', true), // optional
    ],
    public: false, // requires authentication
  }),
  async (req, res) => {
    const { name, age } = req.params;
    res.json({ res: 1, data: { name, age } });
  }
);

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Authentication

Login Flow

// 1. Request verification code
app.post(
  '/auth/login',
  requestHandler({
    params: [new RequestParam('email', 'email'), new RequestParam('device', 'object', true)],
    public: true,
  }),
  authController.login
);

// 2. Authenticate with code
app.post(
  '/auth/authenticate',
  requestHandler({
    params: [
      new RequestParam('email', 'email'),
      new RequestParam('code', 'string'),
      new RequestParam('client_id', 'string'),
      new RequestParam('client_secret', 'string'),
    ],
    public: true,
  }),
  authController.authenticate
);

Client Implementation

// Step 1: Request verification code
const response = await fetch('/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    device: {
      name: 'iPhone 14',
      os: 'iOS',
      os_version: '17.0',
      firebase_token: 'device-fcm-token',
    },
  }),
});

const { device_uuid } = await response.json();

// Step 2: Authenticate with code (received via email)
const authResponse = await fetch('/auth/authenticate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    code: '123456',
    client_id: 'your-client-id',
    client_secret: 'your-client-secret',
    device_uuid,
  }),
});

const { access_token, refresh_token, user } = await authResponse.json();

// Use access_token for authenticated requests
fetch('/api/protected', {
  headers: {
    Authorization: `Bearer ${access_token}`,
  },
});

Request Handler & Validation

The requestHandler decorator provides automatic parameter validation and type conversion:

import { requestHandler, RequestParam } from 'nodejs-nets-core';

app.post(
  '/api/create-post',
  requestHandler({
    params: [
      new RequestParam('title', 'string'),
      new RequestParam('content', 'string'),
      new RequestParam('published', 'bool', true, { default: false }),
      new RequestParam('publish_date', 'datetime', true),
      new RequestParam('tags', 'list', true),
    ],
    public: false, // requires authentication
    canDo: 'blog.create_post', // permission check
  }),
  async (req, res) => {
    // All parameters are validated and available in req.params
    const { title, content, published, publish_date, tags } = req.params;

    // req.user is automatically populated
    // req.perm indicates if user has the required permission

    res.json({ res: 1, data: 'Post created' });
  }
);

Available Parameter Types

  • string / str
  • int / integer
  • float / number
  • bool / boolean
  • date
  • datetime
  • email
  • list / array
  • object / dict
  • file

Permissions & Roles

Define Permissions

import { Permission, Role } from 'nodejs-nets-core/models';

// Create permissions
const createPostPerm = await Permission.create({
  codename: 'blog.create_post',
  name: 'Can create blog posts',
  description: 'Allows user to create new blog posts',
});

// Create role
const editorRole = await Role.create({
  codename: 'editor',
  name: 'Editor',
  description: 'Blog editor role',
});

// Assign permission to role
await editorRole.addPermission(createPostPerm);

Protect Routes

// Method 1: Using canDo parameter
app.post(
  '/api/posts',
  requestHandler({
    canDo: 'blog.create_post',
    permRequired: true, // deny access if permission not granted
  }),
  createPostHandler
);

// Method 2: Using middleware
import { checkPermission } from 'nodejs-nets-core/middleware';

app.delete('/api/posts/:id', checkPermission('blog.delete_post'), deletePostHandler);

// Method 3: Role-based check
app.get(
  '/api/admin',
  requestHandler({
    canDo: 'role:admin', // check if user has admin role
  }),
  adminHandler
);

Multi-Project Support

// Configure project models
netsCore.configure({
  projectModel: 'Project',
  projectMemberModel: 'ProjectMember',
});

// Routes automatically check project membership
app.post(
  '/api/projects/:projectId/data',
  requestHandler({
    projectRequired: true,
    canDo: 'project.edit_data',
  }),
  async (req, res) => {
    // req.project - current project instance
    // req.projectMembership - user's membership in project
    // req.perm - permission check result
  }
);

Email System

Send Email

import { sendEmail } from 'nodejs-nets-core/mail';

const [sent, reason, description] = await sendEmail({
  subject: 'Welcome to our platform',
  email: 'user@example.com', // or array of emails
  template: 'emails/welcome.hbs',
  context: {
    username: 'John Doe',
    activation_link: 'https://...',
  },
  toQueued: true, // queue for batch sending
});

if (!sent) {
  console.error(`Email not sent: ${reason} - ${description}`);
}

Email Templates

Create Handlebars templates in your templates directory:

<!-- templates/emails/welcome.hbs -->

<html>
  <head>
    <title>Welcome</title>
  </head>
  <body>
    <h1>Welcome {{username}}!</h1>
    <p>Click <a href='{{activation_link}}'>here</a> to activate your account.</p>
  </body>
</html>

Configuration

netsCore.configure({
  email: {
    from: 'noreply@example.com',
    excludeDomains: ['tempmail*', 'guerrillamail.com', '10minutemail.com'],
    footer: '<p>© 2024 MyCompany</p>',
    debugEnabled: false, // don't send emails in development
  },
});

Push Notifications

Setup Firebase

netsCore.configure({
  firebase: {
    credentialsPath: './firebase-credentials.json',
  },
});

Send Notifications

import { sendUserDeviceNotification } from 'nodejs-nets-core/firebase';

// Send to all user's devices
const results = await sendUserDeviceNotification(
  user,
  'New Message',
  'You have a new message from John',
  {
    type: 'message',
    messageId: '12345',
  },
  'messages' // notification channel
);

// Check results
Object.entries(results).forEach(([deviceId, result]) => {
  if (result.success) {
    console.log(`Sent to device ${deviceId}: ${result.message_id}`);
  } else {
    console.error(`Failed to send to device ${deviceId}: ${result.error}`);
  }
});

Models

All models extend NetsCoreBaseModel which provides:

  • Automatic timestamps (created, updated)
  • Change tracking (updated_fields)
  • JSON serialization (toJSON() method)
  • Field protection (sensitive fields excluded from JSON)

Base Models

import { NetsCoreBaseModel, OwnedModel } from 'nodejs-nets-core/models';

// NetsCoreBaseModel - timestamps only
class MyModel extends NetsCoreBaseModel {
  // your fields
}

// OwnedModel - timestamps + user ownership
class MyOwnedModel extends OwnedModel {
  // automatically has userId field
}

Built-in Models

  • User - Base user model
  • VerificationCode - OTP codes for authentication
  • UserDevice - Device registration and tracking
  • Permission - Permission definitions
  • Role - Role definitions
  • RolePermission - Role-permission associations
  • UserRole - User-role assignments
  • EmailTemplate - Reusable email templates
  • CustomEmail - Queued emails
  • EmailNotification - Email sending queue
  • UserFirebaseNotification - Push notification history

Configuration

Environment Variables

Create a .env file:

# Database
DB_DIALECT=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_USER=user
DB_PASSWORD=password

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# JWT
JWT_SECRET=your-secret-key-change-in-production
ACCESS_TOKEN_EXPIRE=30d

# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-password
DEFAULT_FROM_EMAIL=noreply@example.com

# Firebase
FIREBASE_CONFIG=./firebase-credentials.json

# App
NODE_ENV=development
PORT=3000

Full Configuration

const netsCore = new NetsCoreApp({
  database: {
    dialect: 'postgres',
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT),
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    logging: false,
  },
  redis: {
    host: process.env.REDIS_HOST,
    port: parseInt(process.env.REDIS_PORT),
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    accessTokenExpire: process.env.ACCESS_TOKEN_EXPIRE || '30d',
  },
  email: {
    host: process.env.SMTP_HOST,
    port: parseInt(process.env.SMTP_PORT),
    secure: false,
    auth: {
      user: process.env.SMTP_USER,
      pass: process.env.SMTP_PASSWORD,
    },
    from: process.env.DEFAULT_FROM_EMAIL,
    excludeDomains: ['tempmail*'],
    debugEnabled: false,
  },
  firebase: {
    credentialsPath: process.env.FIREBASE_CONFIG,
  },
  verificationCode: {
    expireSeconds: 15 * 60, // 15 minutes
    debugCode: '123456',
    testersEmails: ['google_tester*'],
    testersCode: '789654',
  },
  security: {
    globalProtectedFields: ['password', 'token', 'secret'],
    rateLimitWindowMs: 15 * 60 * 1000,
    rateLimitMax: 100,
  },
  projectModel: 'Project', // optional for multi-project support
  projectMemberModel: 'ProjectMember', // optional
});

API Reference

Request Handler Options

interface RequestHandlerOptions {
  // Model to load based on index field
  model?: any;

  // Field to use as identifier (default: 'id')
  indexField?: string;

  // Permission to check
  canDo?: string;

  // Require permission (deny if not granted)
  permRequired?: boolean;

  // Request parameters to validate
  params?: RequestParam[];

  // Allow unauthenticated access
  public?: boolean;

  // Require project context
  projectRequired?: boolean;
}

RequestParam

class RequestParam {
  constructor(
    key: string, // Parameter name
    type: ParamType, // Data type
    optional?: boolean, // Is optional?
    options?: {
      default?: any; // Default value
      validate?: Function; // Custom validator
      filetypes?: string[]; // For file uploads
    }
  );
}

Response Helpers

import {
  successResponse,
  errorResponse,
  notFoundResponse,
  permissionDenied,
} from 'nodejs-nets-core/responses';

// Success with data
return successResponse({ user: userData });

// Success with extra data
return successResponse(userData, { token: 'abc123' });

// Error
return errorResponse('Invalid input', 400);

// Not found
return notFoundResponse();

// Permission denied
return permissionDenied();

Testing

Tester Accounts

Configure tester emails that bypass OTP verification:

netsCore.configure({
  verificationCode: {
    testersEmails: ['google_tester*', 'test@example.com'],
    testersCode: '475638', // static code for testers
  },
});

Emails matching tester patterns will always use the configured code instead of generating random ones.

Migration from Django

If you're migrating from django-nets-core:

  1. Models: Sequelize models map directly to Django models
  2. Decorators: @request_handler becomes requestHandler()
  3. Permissions: Same codename system
  4. Email: Similar API with template support
  5. Firebase: Same Firebase Admin SDK

See MIGRATION.md for detailed migration guide.

Examples

Check the /examples directory for:

  • Basic authentication setup
  • Role-based access control
  • Email notifications
  • Push notifications
  • Multi-project applications

License

MIT

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for details.

Support

Credits

Port of django-nets-core by esbozos.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •