Compare commits

..

No commits in common. "master" and "v1.0.25" have entirely different histories.

20 changed files with 928 additions and 12649 deletions

4
.gitignore vendored
View File

@ -15,6 +15,8 @@ node_modules/
# builds
dist/
dist_*/
dist_web/
dist_serve/
dist_ts_web/
# custom

View File

@ -12,35 +12,29 @@ stages:
- release
- metadata
before_script:
- npm install -g @shipzone/npmci
# ====================
# security stage
# ====================
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
mirror:
stage: security
script:
- npmci npm prepare
- npmci command npm install --production --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
- npmci git mirror
tags:
- lossless
- docker
allow_failure: true
- notpriv
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
snyk:
image: registry.gitlab.com/hosttoday/ht-docker-node:snyk
stage: security
script:
- npmci npm prepare
- npmci command npm install --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=dev
- npmci command snyk test
tags:
- lossless
- docker
allow_failure: true
- notpriv
# ====================
# test stage
@ -55,7 +49,9 @@ testStable:
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- lossless
- docker
- priv
testBuild:
stage: test
@ -66,7 +62,9 @@ testBuild:
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- lossless
- docker
- notpriv
release:
stage: release
@ -86,12 +84,11 @@ release:
codequality:
stage: metadata
allow_failure: true
only:
- tags
script:
- npmci command npm install -g typescript
- npmci command npm install -g tslint typescript
- npmci npm prepare
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- lossless
- docker
@ -111,10 +108,11 @@ trigger:
pages:
stage: metadata
script:
- npmci node install stable
- npmci node install lts
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command npm run buildDocs
- npmci command tsdoc
tags:
- lossless
- docker

View File

@ -0,0 +1,8 @@
terminal:
image: registry.gitlab.com/hosttoday/ht-docker-node:stable
before_script:
- npm install -g npm-check
variables:
RAILS_ENV: "test"
NODE_ENV: "test"

4
.snyk Normal file
View File

@ -0,0 +1,4 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.13.5
ignore: {}
patch: {}

24
.vscode/launch.json vendored
View File

@ -2,10 +2,28 @@
"version": "0.2.0",
"configurations": [
{
"command": "npm test",
"name": "Run npm test",
"name": "current file",
"type": "node",
"request": "launch",
"type": "node-terminal"
"args": [
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
}
]
}

View File

@ -11,13 +11,7 @@
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
"description": "settings for gitzone"
}
}
}

View File

@ -5,7 +5,7 @@
"githost": "gitlab.com",
"gitscope": "mojoio",
"gitrepo": "mailgun",
"description": "an api abstraction package for mailgun",
"shortDescription": "an api abstraction package for mailgun",
"npmPackagename": "@mojoio/mailgun",
"license": "MIT",
"projectDomain": "mojo.io"

13194
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +1,41 @@
{
"name": "@mojoio/mailgun",
"version": "2.0.1",
"version": "1.0.25",
"private": false,
"description": "an api abstraction package for mailgun",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"author": "Lossless GmbH",
"license": "MIT",
"scripts": {
"test": "(tstest test/)",
"build": "(tsbuild --web --allowimplicitany)",
"format": "(gitzone format)",
"buildDocs": "tsdoc"
"build": "(tsbuild)",
"format": "(gitzone format)"
},
"devDependencies": {
"@gitzone/tsbuild": "^2.1.65",
"@gitzone/tstest": "^1.0.73",
"@pushrocks/qenv": "^5.0.2",
"@pushrocks/tapbundle": "^5.0.4",
"@types/node": "^18.6.4"
"@gitzone/tsbuild": "^2.1.22",
"@gitzone/tstest": "^1.0.15",
"@pushrocks/qenv": "^4.0.6",
"@pushrocks/tapbundle": "^3.2.1",
"@types/node": "^13.13.4",
"tslint": "^6.1.1",
"tslint-config-prettier": "^1.15.0"
},
"dependencies": {
"@pushrocks/smartfile": "^10.0.4",
"@pushrocks/smartmail": "^1.0.22",
"@pushrocks/smartrequest": "^2.0.10",
"@pushrocks/smartsmtp": "^3.0.3",
"@pushrocks/smartstring": "^4.0.2"
"@pushrocks/smartfile": "^7.0.12",
"@pushrocks/smartmail": "^1.0.11",
"@pushrocks/smartrequest": "^1.1.47",
"@pushrocks/smartstring": "^3.0.18"
},
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_web/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 chrome versions"
]
}

View File

@ -1,3 +1,2 @@
required:
- MAILGUN_API_TOKEN
- MAILGUN_SMTP_CREDENTIALS
- MAILGUN_API_TOKEN

View File

@ -8,29 +8,16 @@ an api abstraction package for mailgun
* [docs (typedoc)](https://mojoio.gitlab.io/mailgun/)
## Status for master
Status Category | Status Badge
-- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/mojoio/mailgun/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/mojoio/mailgun/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@mojoio/mailgun)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/mojoio/mailgun)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@mojoio/mailgun)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@mojoio/mailgun)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@mojoio/mailgun)](https://lossless.cloud)
Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20Windows%2010/yes/green?icon=windows)](https://lossless.cloud) [![Supports Mac OS X](https://badgen.net/badge/supports%20Mac%20OS%20X/yes/green?icon=apple)](https://lossless.cloud)
[![build status](https://gitlab.com/mojoio/mailgun/badges/master/build.svg)](https://gitlab.com/mojoio/mailgun/commits/master)
[![coverage report](https://gitlab.com/mojoio/mailgun/badges/master/coverage.svg)](https://gitlab.com/mojoio/mailgun/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/@mojoio/mailgun.svg)](https://www.npmjs.com/package/@mojoio/mailgun)
[![Known Vulnerabilities](https://snyk.io/test/npm/@mojoio/mailgun/badge.svg)](https://snyk.io/test/npm/@mojoio/mailgun)
[![TypeScript](https://img.shields.io/badge/TypeScript->=%203.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-prettier-ff69b4.svg)](https://prettier.io/)
## Usage
Use TypeScript for best in class intellisense.
## Contribution
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)

View File

@ -4,56 +4,34 @@ import * as smartmail from '@pushrocks/smartmail';
const testQenv = new Qenv('./', './.nogit');
import * as mailgun from '../ts/index.js';
import { IMailgunMessage } from '../ts/index.js';
import * as mailgun from '../ts/index';
import { IMailgunMessage } from '../ts/index';
let testMailgunAccount: mailgun.MailgunAccount;
let testSmartmail: smartmail.Smartmail<IMailgunMessage>;
tap.test('should create a mailgun account', async () => {
testMailgunAccount = new mailgun.MailgunAccount({
apiToken: testQenv.getEnvVarOnDemand('MAILGUN_API_TOKEN'),
region: 'eu',
});
await testMailgunAccount.addSmtpCredentials(
testQenv.getEnvVarOnDemand('MAILGUN_SMTP_CREDENTIALS')
);
expect(testMailgunAccount).toBeInstanceOf(mailgun.MailgunAccount);
testMailgunAccount = new mailgun.MailgunAccount(testQenv.getEnvVarOnDemand('MAILGUN_API_TOKEN'));
expect(testMailgunAccount).to.be.instanceOf(mailgun.MailgunAccount);
});
tap.test('should create a smartmail', async () => {
testSmartmail = new smartmail.Smartmail({
body: 'hi there. This is the body.',
from: 'Lossless GmbH <noreply@mail.lossless.com>',
subject: 'TestMessage from @mojoio/mailgun test',
subject: 'TestMessage from @mojoio/mailgun test'
});
expect(testSmartmail).toBeInstanceOf(smartmail.Smartmail);
return testSmartmail;
});
tap.test('should send a smartmail', async () => {
await testMailgunAccount.sendSmartMail(testSmartmail, 'Sandbox Team <sandbox@mail.git.zone>');
});
tap.test('should send a smartmail with empty body', async () => {
const emptyBodySmartmail = new smartmail.Smartmail<IMailgunMessage>({
body: '',
from: 'Lossless GmbH <noreply@mail.lossless.one>',
subject: 'A message with no body from @mojoio/mailgun test',
});
await testMailgunAccount.sendSmartMail(
emptyBodySmartmail,
'Sandbox Team <sandbox@mail.git.zone>'
);
});
tap.test('should retrieve a mail using a retrieval url', async () => {
const result = await testMailgunAccount.retrieveSmartMailFromMessageUrl(
'https://sw.api.mailgun.net/v3/domains/mail.lossless.one/messages/AgMFnnnAKC8xp_dDa79LyoxhloxtaVmnRA=='
);
console.log(result);
// TODO handle empty body
if (false) {
const result = await testMailgunAccount.retrieveSmartMailFromMessageUrl('https://sw.api.mailgun.net/v3/domains/mail.lossless.one/messages/AgMFnnnAKC8xp_dDa79LyoxhloxtaVmnRA==');
if (result) {
result.options.subject = 'hi there. This is a testmail with attachment';
result.options.from = 'noreply@mail.lossless.com';
await testMailgunAccount.sendSmartMail(result, 'Sandbox Team <sandbox@mail.git.zone>');

View File

@ -1,8 +0,0 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@mojoio/mailgun',
version: '2.0.1',
description: 'an api abstraction package for mailgun'
}

View File

@ -1,2 +1,2 @@
export * from './mailgun.classes.account.js';
export * from './interfaces/index.js';
export * from './mailgun.classes.account';
export * from './interfaces';

View File

@ -1,2 +1,2 @@
export * from './message.js';
export * from './notification.js';
export * from './message';
export * from './notification';

View File

@ -6,7 +6,7 @@ export interface IMailgunNotification {
'Received-Spf': string;
From: string;
'Return-Path': string;
'Arc-Seal': string[];
'Arc-Seal': string[],
'Delivered-To': string;
'X-Google-Dkim-Signature': string;
To: string;
@ -43,5 +43,5 @@ export interface IMailgunNotification {
'stripped-signature': string;
// Lossless specific
'X-Lossless-Auth': string;
}
"X-Lossless-Auth": string;
}

View File

@ -1,57 +1,27 @@
import * as plugins from './mailgun.plugins.js';
import * as interfaces from './interfaces/index.js';
export interface IMailgunAccountContructorOptions {
apiToken: string;
region: 'eu' | 'us';
}
import * as plugins from './mailgun.plugins';
import * as interfaces from './interfaces';
export class MailgunAccount {
public apiBaseUrl: string;
public baseUrl = 'https://api.mailgun.net/v3';
public apiToken: string;
public options: IMailgunAccountContructorOptions;
public smartSmtps: { [domain: string]: plugins.smartsmtp.Smartsmtp } = {};
constructor(optionsArg: IMailgunAccountContructorOptions) {
this.options = optionsArg;
this.apiBaseUrl =
this.options.region === 'eu'
? 'https://api.eu.mailgun.net/v3'
: 'https://api..mailgun.net/v3';
}
/**
* allows adding smtp credentials
* Format: [domain]|[username]|[password]
*/
public async addSmtpCredentials(credentials: string) {
const credentialArray = credentials.split('|');
if (credentialArray.length !== 3) {
throw new Error('credentials are in the wrong format');
}
this.smartSmtps[credentialArray[0]] =
await plugins.smartsmtp.Smartsmtp.createSmartsmtpWithRelay({
smtpServer: 'smtp.eu.mailgun.org',
smtpUser: credentialArray[1],
smtpPassword: credentialArray[2],
});
constructor(apiTokenArg: string) {
this.apiToken = apiTokenArg;
}
public async getRequest(routeArg: string, binaryArg: boolean = false) {
let requestUrl = routeArg;
const needsBaseUrlPrefix = !routeArg.startsWith('https://');
if (needsBaseUrlPrefix) {
requestUrl = `${this.apiBaseUrl}${routeArg}`;
}
needsBaseUrlPrefix ? requestUrl = `${this.baseUrl}${routeArg}` : null;
console.log(requestUrl);
const requestOptions: plugins.smartrequest.ISmartRequestOptions = {
method: 'GET',
headers: {
Authorization: `Basic ${plugins.smartstring.base64.encode(`api:${this.options.apiToken}`)}`,
'Content-Type': 'application/json',
},
keepAlive: false,
Authorization: `Basic ${plugins.smartstring.base64.encode(
`api:${this.apiToken}`
)}`,
'Content-Type': 'application/json'
}
};
let response: plugins.smartrequest.IExtendedIncomingMessage;
if (!binaryArg) {
@ -63,17 +33,15 @@ export class MailgunAccount {
}
public async postFormData(routeArg: string, formFields: plugins.smartrequest.IFormField[]) {
const requestUrl = `${this.apiBaseUrl}${routeArg}`;
console.log(requestUrl);
const requestUrl = `${this.baseUrl}${routeArg}`; // TODO;
const response = await plugins.smartrequest.postFormData(
requestUrl,
{
headers: {
Authorization: `Basic ${plugins.smartstring.base64.encode(
`api:${this.options.apiToken}`
)}`,
},
keepAlive: false,
`api:${this.apiToken}`
)}`
}
},
formFields
);
@ -83,33 +51,29 @@ export class MailgunAccount {
/**
* sends a SmartMail
*/
public async sendSmartMail(
smartmailArg: plugins.smartmail.Smartmail<interfaces.IMailgunMessage>,
toArg: string,
dataArg = {}
) {
public async sendSmartMail(smartmailArg: plugins.smartmail.Smartmail<interfaces.IMailgunMessage>, toArg: string, dataArg = {}) {
const domain = smartmailArg.options.from.split('@')[1].replace('>', '');
const formFields: plugins.smartrequest.IFormField[] = [
{
name: 'from',
type: 'string',
payload: smartmailArg.options.from,
payload: smartmailArg.options.from
},
{
name: 'to',
type: 'string',
payload: toArg,
payload: toArg
},
{
name: 'subject',
type: 'string',
payload: smartmailArg.getSubject(dataArg),
payload: smartmailArg.getSubject(dataArg)
},
{
name: 'html',
type: 'string',
payload: smartmailArg.getBody(dataArg),
},
payload: smartmailArg.getBody(dataArg)
}
];
console.log(smartmailArg.attachments);
@ -119,44 +83,20 @@ export class MailgunAccount {
name: 'attachment',
type: 'Buffer',
payload: attachment.contentBuffer,
fileName: attachment.parsedPath.base,
fileName: attachment.parsedPath.base
});
}
if (smartmailArg.getBody(dataArg)) {
console.log('All requirements for API met');
const response = await this.postFormData(`/${domain}/messages`, formFields);
if (response.statusCode === 200) {
console.log(
`Sent mail with subject ${smartmailArg.getSubject(
dataArg
)} to ${toArg} using the mailgun API`
);
return response.body;
} else {
console.log(response.body);
throw new Error('could not send email');
}
const response = await this.postFormData(`/${domain}/messages`, formFields);
if (response.statusCode === 200) {
return response.body;
} else {
console.log(
'An empty body was provided. This does not work via the API, but using SMTP instead.'
);
const wantedSmartsmtp = this.smartSmtps[domain];
if (!wantedSmartsmtp) {
console.log('did not find appropriate smtp credentials');
return;
}
await wantedSmartsmtp.sendSmartMail(smartmailArg, toArg);
console.log(
`Sent mail with subject "${smartmailArg.getSubject(
dataArg
)}" to "${toArg}" using an smtp transport over Mailgun.`
);
console.log(response.body);
throw new Error('could not send email');
}
}
public async retrieveSmartMailFromMessageUrl(messageUrlArg: string) {
console.log(`retrieving message for ${messageUrlArg}`);
const response = await this.getRequest(messageUrlArg);
if (response.statusCode === 404) {
console.log(response.body.message);
@ -165,9 +105,9 @@ export class MailgunAccount {
const responseBody: interfaces.IMailgunMessage = response.body;
const smartmail = new plugins.smartmail.Smartmail<interfaces.IMailgunMessage>({
from: responseBody.From,
body: responseBody['body-html'],
body: responseBody["body-html"],
subject: responseBody.Subject,
creationObjectRef: responseBody,
creationObjectRef: responseBody
});
// lets care about attachments
@ -175,19 +115,17 @@ export class MailgunAccount {
for (const attachmentInfo of responseBody.attachments) {
const attachmentName = attachmentInfo.name;
const attachmentContents = await this.getRequest(attachmentInfo.url, true);
smartmail.addAttachment(
new plugins.smartfile.Smartfile({
path: `./${attachmentName}`,
base: `./${attachmentName}`,
contentBuffer: attachmentContents.body,
})
);
smartmail.addAttachment(new plugins.smartfile.Smartfile({
path: `./${attachmentName}`,
base: `./${attachmentName}`,
contentBuffer: attachmentContents.body
}));
}
}
return smartmail;
}
public async retrieveSmartMailFromNotifyPayload(notifyPayloadArg: any) {
return await this.retrieveSmartMailFromMessageUrl(notifyPayloadArg['message-url']);
}

View File

@ -2,7 +2,6 @@
import * as smartfile from '@pushrocks/smartfile';
import * as smartmail from '@pushrocks/smartmail';
import * as smartrequest from '@pushrocks/smartrequest';
import * as smartsmtp from '@pushrocks/smartsmtp';
import * as smartstring from '@pushrocks/smartstring';
export { smartfile, smartmail, smartrequest, smartsmtp, smartstring };
export { smartfile, smartmail, smartrequest, smartstring };

View File

@ -1,10 +0,0 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "nodenext",
"esModuleInterop": true
}
}

17
tslint.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}