Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: build
on:
push:
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Use Node 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Linting
run: pnpm lint
- name: Build modules
run: pnpm build
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dist/
node_modules/
.DS_Store
.idea/
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Minimal Dockerfile to run the NestJS proofpoint-mock API using pnpm
FROM node:20-alpine

# Create app directory
WORKDIR /app

# Enable and prepare pnpm via Corepack (bundled with Node.js >=16.13)
RUN corepack enable && corepack prepare pnpm@9 --activate

# Install dependencies with pnpm; leverage lockfile if present
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --prefer-offline --strict-peer-dependencies=false

# Copy source
COPY tsconfig.json ./
COPY src ./src

# Expose API port
ARG PORT=3000
ENV PORT=${PORT}
#ENV NODE_ENV=production
EXPOSE ${PORT}

# Start the application (ts-node is in devDependencies)
CMD ["pnpm", "start"]
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Proofpoint Mock API

## Components
* custom api: mocks Proofpoint's `send` api endpoint and sends an email with smtp
* Nodemailer: smtp client
* Mailhog: fake smtp server

## Build the proofpoint mock api
```
pnpm clean && pnpm install && pnpm build
docker compose build --no-cache
```

## Usage (local development)
Starts the fake smtp server and the mock api:
```
pnpm dev
```

Send a PUT request to the /send api endpoint
e.g.
```
curl --location 'http://localhost:3000/send' \
--header 'Content-Type: application/json' \
--data-raw '{
"content": [
{
"body": "Simple test message from SER!",
"type": "text/html"
}
],
"from": {
"email": "USER@YOURDOMAIN.COM"
},
"headers": {
"from": {
"email": "USER@YOURDOMAIN.COM"
}
},
"subject": "Test Subject from SER!",
"tos": [
{
"email": "RECIPIENT@YOURDOMAIN.COM",
"name": "YOUR NAME"
}
]
}'
```
23 changes: 23 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: "3.8"

services:
# api:
# build: .
# container_name: proofpoint-mock-api
# environment:
# - PORT=3000
# - SMTP_HOST=smtp
# - SMTP_PORT=1025
# ports:
# - "3000:3000"
# depends_on:
# - smtp

smtp:
image: mailhog/mailhog:v1.0.1
container_name: fake-smtp
restart: unless-stopped
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
# Usage: Visit http://localhost:8025 to view captured emails.
73 changes: 73 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { defineConfig, globalIgnores } from 'eslint/config';
import tsParser from '@typescript-eslint/parser';
import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin';
import globals from 'globals';
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});

/**
* @type {import('@eslint/eslintrc').Config[]}
*/
const config = defineConfig([
{
languageOptions: {
parser: tsParser,
sourceType: 'module',

parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
},

globals: {
...globals.node,
...globals.jest,
},
},

plugins: {
'@typescript-eslint': typescriptEslintEslintPlugin,
},

extends: compat.extends(
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
),

rules: {
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],

'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',

'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
},
},
globalIgnores(['**/.eslintrc.js']),
]);

export default config;
47 changes: 47 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "proofpoint-mock",
"version": "1.0.0",
"description": "Mock NestJS API for Proofpoint Email Submission send endpoint",
"main": "index.js",
"packageManager": "pnpm@10.11.0",
"scripts": {
"clean": "rimraf dist && rimraf node_modules",
"start": "nest start --watch",
"start:dev": "ts-node src/main.ts",
"dev": "docker compose up -d && pnpm start",
"build": "nest build --webpack",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint \"{src,test}/**/*.ts\" --fix"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"express": "^5.1.0",
"nodemailer": "^6.10.1",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.34.0",
"@nestjs/cli": "^10.4.9",
"@types/express": "^5.0.3",
"@types/node": "^22.18.0",
"@types/nodemailer": "^7.0.1",
"@typescript-eslint/eslint-plugin": "^8.41.0",
"@typescript-eslint/parser": "^8.41.0",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"globals": "^16.3.0",
"prettier": "^3.6.2",
"rimraf": "^6.0.1",
"ts-loader": "^9.5.4",
"ts-node": "^10.9.2",
"typescript": "^5.4.0"
},
"private": true
}
Loading