@churchapps/apihelper 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/auth/AuthenticatedUser.d.ts +17 -0
- package/dist/auth/AuthenticatedUser.d.ts.map +1 -0
- package/dist/auth/AuthenticatedUser.js +31 -0
- package/dist/auth/AuthenticatedUser.js.map +1 -0
- package/dist/auth/CustomAuthProvider.d.ts +7 -0
- package/dist/auth/CustomAuthProvider.d.ts.map +1 -0
- package/dist/auth/CustomAuthProvider.js +46 -0
- package/dist/auth/CustomAuthProvider.js.map +1 -0
- package/dist/auth/Principal.d.ts +9 -0
- package/dist/auth/Principal.d.ts.map +1 -0
- package/dist/auth/Principal.js +19 -0
- package/dist/auth/Principal.js.map +1 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/controllers/CustomBaseController.d.ts +15 -0
- package/dist/controllers/CustomBaseController.d.ts.map +1 -0
- package/dist/controllers/CustomBaseController.js +83 -0
- package/dist/controllers/CustomBaseController.js.map +1 -0
- package/dist/controllers/ErrorController.d.ts +7 -0
- package/dist/controllers/ErrorController.d.ts.map +1 -0
- package/dist/controllers/ErrorController.js +63 -0
- package/dist/controllers/ErrorController.js.map +1 -0
- package/dist/controllers/index.d.ts +3 -0
- package/dist/controllers/index.d.ts.map +1 -0
- package/dist/controllers/index.js +8 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/helpers/AwsHelper.d.ts +13 -0
- package/dist/helpers/AwsHelper.d.ts.map +1 -0
- package/dist/helpers/AwsHelper.js +131 -0
- package/dist/helpers/AwsHelper.js.map +1 -0
- package/dist/helpers/BasePermissions.d.ts +31 -0
- package/dist/helpers/BasePermissions.d.ts.map +1 -0
- package/dist/helpers/BasePermissions.js +20 -0
- package/dist/helpers/BasePermissions.js.map +1 -0
- package/dist/helpers/DB.d.ts +8 -0
- package/dist/helpers/DB.d.ts.map +1 -0
- package/dist/helpers/DB.js +72 -0
- package/dist/helpers/DB.js.map +1 -0
- package/dist/helpers/DBCreator.d.ts +6 -0
- package/dist/helpers/DBCreator.d.ts.map +1 -0
- package/dist/helpers/DBCreator.js +56 -0
- package/dist/helpers/DBCreator.js.map +1 -0
- package/dist/helpers/EmailHelper.d.ts +7 -0
- package/dist/helpers/EmailHelper.d.ts.map +1 -0
- package/dist/helpers/EmailHelper.js +81 -0
- package/dist/helpers/EmailHelper.js.map +1 -0
- package/dist/helpers/EncryptionHelper.d.ts +6 -0
- package/dist/helpers/EncryptionHelper.d.ts.map +1 -0
- package/dist/helpers/EncryptionHelper.js +31 -0
- package/dist/helpers/EncryptionHelper.js.map +1 -0
- package/dist/helpers/EnvironmentBase.d.ts +17 -0
- package/dist/helpers/EnvironmentBase.d.ts.map +1 -0
- package/dist/helpers/EnvironmentBase.js +22 -0
- package/dist/helpers/EnvironmentBase.js.map +1 -0
- package/dist/helpers/FileStorageHelper.d.ts +15 -0
- package/dist/helpers/FileStorageHelper.d.ts.map +1 -0
- package/dist/helpers/FileStorageHelper.js +99 -0
- package/dist/helpers/FileStorageHelper.js.map +1 -0
- package/dist/helpers/Interfaces.d.ts +12 -0
- package/dist/helpers/Interfaces.d.ts.map +1 -0
- package/dist/helpers/Interfaces.js +3 -0
- package/dist/helpers/Interfaces.js.map +1 -0
- package/dist/helpers/LoggingHelper.d.ts +15 -0
- package/dist/helpers/LoggingHelper.d.ts.map +1 -0
- package/dist/helpers/LoggingHelper.js +83 -0
- package/dist/helpers/LoggingHelper.js.map +1 -0
- package/dist/helpers/MySqlHelper.d.ts +4 -0
- package/dist/helpers/MySqlHelper.d.ts.map +1 -0
- package/dist/helpers/MySqlHelper.js +10 -0
- package/dist/helpers/MySqlHelper.js.map +1 -0
- package/dist/helpers/OmitEmpty.d.ts +6 -0
- package/dist/helpers/OmitEmpty.d.ts.map +1 -0
- package/dist/helpers/OmitEmpty.js +94 -0
- package/dist/helpers/OmitEmpty.js.map +1 -0
- package/dist/helpers/Pool.d.ts +7 -0
- package/dist/helpers/Pool.d.ts.map +1 -0
- package/dist/helpers/Pool.js +55 -0
- package/dist/helpers/Pool.js.map +1 -0
- package/dist/helpers/index.d.ts +15 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +31 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/models/ErrorLog.d.ts +7 -0
- package/dist/models/ErrorLog.d.ts.map +1 -0
- package/dist/models/ErrorLog.js +7 -0
- package/dist/models/ErrorLog.js.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +6 -0
- package/dist/models/index.js.map +1 -0
- package/dist/tools/DBCreator.d.ts +1 -0
- package/dist/tools/DBCreator.d.ts.map +1 -0
- package/dist/tools/DBCreator.js +1 -0
- package/dist/tools/DBCreator.js.map +1 -0
- package/package.json +60 -0
- package/src/auth/AuthenticatedUser.ts +40 -0
- package/src/auth/CustomAuthProvider.ts +23 -0
- package/src/auth/Principal.ts +22 -0
- package/src/auth/index.ts +3 -0
- package/src/controllers/CustomBaseController.ts +74 -0
- package/src/controllers/ErrorController.ts +33 -0
- package/src/controllers/index.ts +2 -0
- package/src/helpers/AwsHelper.ts +108 -0
- package/src/helpers/BasePermissions.ts +15 -0
- package/src/helpers/DB.ts +41 -0
- package/src/helpers/DBCreator.ts +39 -0
- package/src/helpers/EmailHelper.ts +66 -0
- package/src/helpers/EncryptionHelper.ts +25 -0
- package/src/helpers/EnvironmentBase.ts +35 -0
- package/src/helpers/FileStorageHelper.ts +71 -0
- package/src/helpers/Interfaces.ts +2 -0
- package/src/helpers/LoggingHelper.ts +75 -0
- package/src/helpers/MySqlHelper.ts +5 -0
- package/src/helpers/OmitEmpty.ts +97 -0
- package/src/helpers/Pool.ts +55 -0
- package/src/helpers/index.ts +14 -0
- package/src/index.ts +3 -0
- package/src/models/ErrorLog.ts +7 -0
- package/src/models/index.ts +1 -0
- package/src/tools/DBCreator.ts +0 -0
- package/src/tools/templates/ChurchEmailTemplate.html +383 -0
- package/src/tools/templates/EmailTemplate.html +426 -0
- package/tsconfig.json +39 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { BaseHttpController } from "inversify-express-utils";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import { LoggingHelper } from "../helpers/LoggingHelper";
|
|
4
|
+
import { AuthenticatedUser, Principal } from "../auth"
|
|
5
|
+
|
|
6
|
+
export class CustomBaseController extends BaseHttpController {
|
|
7
|
+
|
|
8
|
+
public logger: LoggingHelper;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
super()
|
|
12
|
+
this.logger = LoggingHelper.getCurrent();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public error(errors: string[]) {
|
|
16
|
+
return this.json({ errors }, 500);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public denyAccess(errors: string[]) {
|
|
20
|
+
return this.json({ errors }, 401);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public authUser(): AuthenticatedUser {
|
|
24
|
+
if (this.httpContext.user === null) return new AuthenticatedUser(new Principal({}));
|
|
25
|
+
else return new AuthenticatedUser(this.httpContext.user);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public include(req: express.Request, item: string) {
|
|
29
|
+
let result = false;
|
|
30
|
+
if (req.query.include !== undefined) {
|
|
31
|
+
const value: string = req.query.include as string;
|
|
32
|
+
const items = value.split(",");
|
|
33
|
+
if (items.indexOf(item) > -1) result = true;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async actionWrapper(req: express.Request, res: express.Response, fetchFunction: (au: AuthenticatedUser) => any): Promise<any> {
|
|
39
|
+
try {
|
|
40
|
+
const result = await fetchFunction(this.authUser());
|
|
41
|
+
await this.logger.flush();
|
|
42
|
+
return result;
|
|
43
|
+
} catch (e:any) {
|
|
44
|
+
try {
|
|
45
|
+
this.logger.error(e);
|
|
46
|
+
await this.logger.flush();
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.log(e);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return this.internalServerError(e);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async actionWrapperAnon(req: express.Request, res: express.Response, fetchFunction: () => any): Promise<any> {
|
|
56
|
+
try {
|
|
57
|
+
const result = await fetchFunction();
|
|
58
|
+
await this.logger.flush();
|
|
59
|
+
return result;
|
|
60
|
+
} catch (e:any) {
|
|
61
|
+
try {
|
|
62
|
+
this.logger.error(e);
|
|
63
|
+
await this.logger.flush();
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.log(e);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return this.internalServerError(e);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { controller, httpPost, httpGet, interfaces, requestParam, httpDelete } from "inversify-express-utils";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import { CustomBaseController } from "./CustomBaseController"
|
|
4
|
+
import { ErrorLog } from "../models"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@controller("/errors")
|
|
8
|
+
export class ErrorController extends CustomBaseController {
|
|
9
|
+
|
|
10
|
+
@httpPost("/")
|
|
11
|
+
public async save(req: express.Request<{}, {}, ErrorLog[]>, res: express.Response): Promise<any> {
|
|
12
|
+
// try {
|
|
13
|
+
/*
|
|
14
|
+
try {
|
|
15
|
+
au = this.authUser();
|
|
16
|
+
} catch (e) {
|
|
17
|
+
this.logger.log(req.body[0].application, "info", e);
|
|
18
|
+
}*/
|
|
19
|
+
req.body.forEach(error => {
|
|
20
|
+
let fullMessage = error.message;
|
|
21
|
+
if (error.additionalDetails !== undefined) fullMessage += "\n" + error.additionalDetails;
|
|
22
|
+
// if (au !== null) fullMessage += "\nUser: " + au.id + " Church: " + au.churchId;
|
|
23
|
+
this.logger.log(error.application, error.level, fullMessage);
|
|
24
|
+
});
|
|
25
|
+
await this.logger.flush();
|
|
26
|
+
return req.body;
|
|
27
|
+
// } catch (e) {
|
|
28
|
+
// console.log(e)
|
|
29
|
+
// };
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import AWS from "aws-sdk"
|
|
2
|
+
import { ByteBuffer } from "aws-sdk/clients/cloudtrail";
|
|
3
|
+
import { EnvironmentBase } from "./EnvironmentBase";
|
|
4
|
+
|
|
5
|
+
export class AwsHelper {
|
|
6
|
+
|
|
7
|
+
private static S3() {
|
|
8
|
+
return new AWS.S3({ apiVersion: "2006-03-01" });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static S3PresignedUrl(key: string): Promise<any> {
|
|
12
|
+
if (key.indexOf("/") === 0) key = key.substring(1, key.length);
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const params: AWS.S3.PresignedPost.Params = { Bucket: EnvironmentBase.s3Bucket }
|
|
15
|
+
|
|
16
|
+
params.Conditions = [
|
|
17
|
+
{ acl: "public-read" },
|
|
18
|
+
{ bucket: EnvironmentBase.s3Bucket },
|
|
19
|
+
{ key },
|
|
20
|
+
["starts-with", "$Content-Type", ""],
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
params.Expires = 1200;
|
|
24
|
+
this.S3().createPresignedPost(params, (error: Error, data: any) => {
|
|
25
|
+
if (error) reject(error);
|
|
26
|
+
else {
|
|
27
|
+
data.key = key;
|
|
28
|
+
resolve(data);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static S3Upload(key: string, contentType: string, contents: ByteBuffer): Promise<void> {
|
|
35
|
+
if (key.indexOf("/") === 0) key = key.substring(1, key.length);
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
const params: AWS.S3.PutObjectRequest = { Bucket: EnvironmentBase.s3Bucket, Key: key, Body: contents, ACL: "public-read", ContentType: contentType }
|
|
38
|
+
this.S3().upload(params, (error: Error, data: AWS.S3.ManagedUpload.SendData) => {
|
|
39
|
+
if (error) reject(error);
|
|
40
|
+
else resolve();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static S3Move(oldKey: string, newKey: string): Promise<void> {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const params: AWS.S3.CopyObjectRequest = { Bucket: EnvironmentBase.s3Bucket, Key: newKey, CopySource: EnvironmentBase.s3Bucket + "/" + oldKey }
|
|
48
|
+
this.S3().copyObject(params, (error: Error, data: AWS.S3.DeleteObjectOutput) => {
|
|
49
|
+
if (error) reject(error);
|
|
50
|
+
else {
|
|
51
|
+
this.S3Remove(oldKey).then(() => {
|
|
52
|
+
resolve();
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static S3Remove(key: string): Promise<void> {
|
|
60
|
+
if (key.indexOf("/") === 0) key = key.substring(1, key.length - 1);
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const params: AWS.S3.PutObjectRequest = { Bucket: EnvironmentBase.s3Bucket, Key: key }
|
|
63
|
+
this.S3().deleteObject(params, (error: Error, data: AWS.S3.DeleteObjectOutput) => {
|
|
64
|
+
if (error) reject(error);
|
|
65
|
+
else resolve();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static async S3Rename(oldKey: string, newKey: string): Promise<void> {
|
|
71
|
+
await this.S3Copy(oldKey, newKey);
|
|
72
|
+
await this.S3Remove(oldKey);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static S3Copy(oldKey: string, newKey: string): Promise<void> {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
const source = "/" + EnvironmentBase.s3Bucket + "/" + oldKey;
|
|
78
|
+
const params: AWS.S3.CopyObjectRequest = { Bucket: EnvironmentBase.s3Bucket, Key: newKey, CopySource: source, ACL: "public-read" }
|
|
79
|
+
this.S3().copyObject(params, (error: Error, data: AWS.S3.DeleteObjectOutput) => {
|
|
80
|
+
if (error) reject(error);
|
|
81
|
+
else resolve();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static async S3List(path: string): Promise<string[]> {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
this.S3().listObjectsV2({ Bucket: EnvironmentBase.s3Bucket, Prefix: path, MaxKeys: 100000 }, (error: Error, data: AWS.S3.ListObjectsV2Output) => {
|
|
89
|
+
if (error) reject(error);
|
|
90
|
+
else {
|
|
91
|
+
const result: string[] = [];
|
|
92
|
+
data.Contents.forEach(v => { result.push(v.Key) });
|
|
93
|
+
resolve(result)
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static async S3Read(key: string): Promise<any> {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
this.S3().getObject({ Bucket: EnvironmentBase.s3Bucket, Key: key }, (error: Error, data: AWS.S3.GetObjectOutput) => {
|
|
102
|
+
if (error) resolve(null);
|
|
103
|
+
else resolve(data.Body.toString());
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class BasePermissions {
|
|
2
|
+
static forms = {
|
|
3
|
+
admin: { contentType: "Forms", action: "Admin" },
|
|
4
|
+
edit: { contentType: "Forms", action: "Edit" }
|
|
5
|
+
};
|
|
6
|
+
static links = {
|
|
7
|
+
edit: { contentType: "Links", action: "Edit" }
|
|
8
|
+
};
|
|
9
|
+
static pages = {
|
|
10
|
+
edit: { contentType: "Pages", action: "Edit" }
|
|
11
|
+
};
|
|
12
|
+
static settings = {
|
|
13
|
+
edit: { contentType: "Settings", action: "Edit" }
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Pool } from "./Pool";
|
|
2
|
+
import { PoolConnection, MysqlError, queryCallback } from "mysql";
|
|
3
|
+
import { LoggingHelper } from "./LoggingHelper";
|
|
4
|
+
|
|
5
|
+
export class DB {
|
|
6
|
+
|
|
7
|
+
// wraps in promise
|
|
8
|
+
static async getConnection() {
|
|
9
|
+
const promise: Promise<PoolConnection> = new Promise((resolve, reject) => {
|
|
10
|
+
Pool.current.getConnection((ex: MysqlError, conn: PoolConnection) => { if (ex) reject(ex); else resolve(conn); });
|
|
11
|
+
});;
|
|
12
|
+
const connection: PoolConnection = await promise;
|
|
13
|
+
return connection;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// wraps in promise
|
|
17
|
+
static async getQuery(connection: PoolConnection, sql: string, params: any[]) {
|
|
18
|
+
const promise: Promise<queryCallback> = new Promise((resolve, reject) => {
|
|
19
|
+
connection.query(sql, params, async (ex, rows) => {
|
|
20
|
+
if (ex) { LoggingHelper.getCurrent().error(ex); reject(ex); }
|
|
21
|
+
else { resolve(rows); }
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
const query: queryCallback = await promise;
|
|
25
|
+
return query;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static async query(sql: string, params: any[]) {
|
|
29
|
+
let result: any = null;
|
|
30
|
+
const connection = await this.getConnection();
|
|
31
|
+
try { result = await this.getQuery(connection, sql, params); }
|
|
32
|
+
catch (ex:any) { LoggingHelper.getCurrent().error(ex); }
|
|
33
|
+
finally { connection.release(); }
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public static async queryOne(sql: string, params: any[]) {
|
|
38
|
+
const result: any = await this.query(sql, params);
|
|
39
|
+
return result.length > 0 ? result[0] : null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import { DB } from "./DB";
|
|
4
|
+
|
|
5
|
+
export class DBCreator {
|
|
6
|
+
|
|
7
|
+
private static tables: { title: string, file: string }[] = [
|
|
8
|
+
{ title: "Links", file: "links.mysql" },
|
|
9
|
+
{ title: "Pages", file: "pages.mysql" },
|
|
10
|
+
{ title: "Settings", file: "settings.mysql" },
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
public static async init(selectedTables: string[]) {
|
|
14
|
+
dotenv.config();
|
|
15
|
+
|
|
16
|
+
const todo: { title: string, file: string }[] = [];
|
|
17
|
+
selectedTables.forEach(async st => {
|
|
18
|
+
this.tables.forEach(async t => {
|
|
19
|
+
if (t.title === st) todo.push(t);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
for (const td of todo) await this.runScript(td.title, "./src/apiBase/tools/dbScripts/" + td.file, false);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public static async runScript(title: string, file: string, customDelimeter: boolean) {
|
|
28
|
+
console.log("Creating '" + title + "'");
|
|
29
|
+
const sql = await fs.readFile(file, { encoding: "UTF-8" });
|
|
30
|
+
let del = /;(?=END)\s*$|;(?!\nEND)\s*$/gm;
|
|
31
|
+
if (customDelimeter) {
|
|
32
|
+
del = /\$\$$/gm;
|
|
33
|
+
}
|
|
34
|
+
const statements = sql.split(del);
|
|
35
|
+
for (const statement of statements) if (statement.length > 3) await DB.query(statement, []);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import AWS from 'aws-sdk'
|
|
2
|
+
import nodemailer from 'nodemailer'
|
|
3
|
+
import directTransport from 'nodemailer-direct-transport'
|
|
4
|
+
import { EnvironmentBase, IEmailPayload } from '.'
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
|
|
8
|
+
export class EmailHelper {
|
|
9
|
+
|
|
10
|
+
public static async sendTemplatedEmail(from: string, to: string, appName: string, appUrl: string, subject: string, contents: string, emailTemplate: "EmailTemplate.html" | "ChurchEmailTemplate.html" = "EmailTemplate.html") {
|
|
11
|
+
if (!appName) appName = "Chums";
|
|
12
|
+
if (!appUrl) appUrl = "https://chums.org";
|
|
13
|
+
|
|
14
|
+
const template = EmailHelper.readTemplate(emailTemplate);
|
|
15
|
+
const emailBody = template
|
|
16
|
+
.replace("{appLink}", "<a href=\"" + appUrl + "/\">" + appName + "</a>")
|
|
17
|
+
.replace("{contents}", contents);
|
|
18
|
+
await EmailHelper.sendEmail({ from, to, subject, body: emailBody });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static readTemplate(templateFile?: string) {
|
|
22
|
+
if (!templateFile) templateFile = "EmailTemplate.html";
|
|
23
|
+
const filePath = path.join(__dirname, "../../templates/" + templateFile);
|
|
24
|
+
const buffer = fs.readFileSync(filePath);
|
|
25
|
+
const contents = buffer.toString();
|
|
26
|
+
return contents;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public static sendEmail({ from, to, subject, body }: IEmailPayload) {
|
|
30
|
+
return new Promise(async (resolve, reject) => {
|
|
31
|
+
try {
|
|
32
|
+
let transporter: nodemailer.Transporter = nodemailer.createTransport(directTransport({
|
|
33
|
+
name: "churchapps.org"
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
if (EnvironmentBase.mailSystem === 'SES') {
|
|
37
|
+
AWS.config.update({ region: 'us-east-2' });
|
|
38
|
+
const ses = new AWS.SES({ apiVersion: '2010-12-01' });
|
|
39
|
+
transporter = nodemailer.createTransport({ SES: { ses, aws: AWS } })
|
|
40
|
+
}
|
|
41
|
+
if (EnvironmentBase.mailSystem === "SMTP") {
|
|
42
|
+
transporter = nodemailer.createTransport({
|
|
43
|
+
host: EnvironmentBase.smtpHost,
|
|
44
|
+
secure: EnvironmentBase.smtpSecure,
|
|
45
|
+
auth: {
|
|
46
|
+
user: EnvironmentBase.smtpUser,
|
|
47
|
+
pass: EnvironmentBase.smtpPass
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (EnvironmentBase.mailSystem === "") {
|
|
53
|
+
console.log("****Email server not configured: ");
|
|
54
|
+
console.log(subject)
|
|
55
|
+
console.log(body);
|
|
56
|
+
}
|
|
57
|
+
else await transporter.sendMail({ from, to, subject, html: body });
|
|
58
|
+
resolve(null);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
reject(err);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { EnvironmentBase } from ".";
|
|
3
|
+
|
|
4
|
+
export class EncryptionHelper {
|
|
5
|
+
private static algorithm = 'aes-256-ctr';
|
|
6
|
+
|
|
7
|
+
static encrypt = (plainValue: string) => {
|
|
8
|
+
const iv = crypto.randomBytes(16);
|
|
9
|
+
const cipher = crypto.createCipheriv(EncryptionHelper.algorithm, EnvironmentBase.encryptionKey, iv);
|
|
10
|
+
const encrypted = Buffer.concat([cipher.update(plainValue), cipher.final()]);
|
|
11
|
+
return iv.toString('base64') + "|" + encrypted.toString('base64');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static decrypt = (encryptedPair: string) => {
|
|
15
|
+
const parts = encryptedPair.split("|");
|
|
16
|
+
if (parts.length !== 2) return "";
|
|
17
|
+
else {
|
|
18
|
+
const iv = Buffer.from(parts[0], 'base64');
|
|
19
|
+
const content = Buffer.from(parts[1], 'base64');
|
|
20
|
+
const decipher = crypto.createDecipheriv(EncryptionHelper.algorithm, EnvironmentBase.encryptionKey, iv);
|
|
21
|
+
const decrpyted = Buffer.concat([decipher.update(content), decipher.final()]);
|
|
22
|
+
return decrpyted.toString();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
export class EnvironmentBase {
|
|
3
|
+
static appEnv: string;
|
|
4
|
+
static appName: string;
|
|
5
|
+
static s3Bucket: string;
|
|
6
|
+
static connectionString: string;
|
|
7
|
+
static contentRoot: string;
|
|
8
|
+
static encryptionKey: string;
|
|
9
|
+
static fileStore: string;
|
|
10
|
+
static jwtSecret: string;
|
|
11
|
+
|
|
12
|
+
static mailSystem: string;
|
|
13
|
+
static smtpHost: string;
|
|
14
|
+
static smtpPass: string;
|
|
15
|
+
static smtpSecure: boolean;
|
|
16
|
+
static smtpUser: string;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
static populateBase(jsonData: any) {
|
|
20
|
+
EnvironmentBase.appName = jsonData.appName;
|
|
21
|
+
EnvironmentBase.appEnv = jsonData.appEnv;
|
|
22
|
+
EnvironmentBase.connectionString = process.env.CONNECTION_STRING;
|
|
23
|
+
EnvironmentBase.contentRoot = jsonData.contentRoot;
|
|
24
|
+
EnvironmentBase.encryptionKey = process.env.ENCRYPTION_KEY;
|
|
25
|
+
EnvironmentBase.fileStore = jsonData.fileStore;
|
|
26
|
+
EnvironmentBase.jwtSecret = process.env.JWT_SECRET;
|
|
27
|
+
EnvironmentBase.mailSystem = jsonData.mailSystem;
|
|
28
|
+
EnvironmentBase.s3Bucket = jsonData.s3Bucket;
|
|
29
|
+
EnvironmentBase.smtpHost = process.env.SMTP_HOST;
|
|
30
|
+
EnvironmentBase.smtpPass = process.env.SMTP_PASS;
|
|
31
|
+
EnvironmentBase.smtpSecure = process.env.SMTP_SECURE === "true";
|
|
32
|
+
EnvironmentBase.smtpUser = process.env.SMTP_USER;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AwsHelper } from "./AwsHelper";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { EnvironmentBase } from ".";
|
|
5
|
+
|
|
6
|
+
export class FileStorageHelper {
|
|
7
|
+
private static rootPath = path.resolve("./content") + "/";
|
|
8
|
+
|
|
9
|
+
static list = async (filePath: string) => {
|
|
10
|
+
let result = []
|
|
11
|
+
switch (EnvironmentBase.fileStore) {
|
|
12
|
+
case "S3": result = await AwsHelper.S3List(filePath); break;
|
|
13
|
+
default: result = await FileStorageHelper.listLocal(filePath); break;
|
|
14
|
+
}
|
|
15
|
+
return result
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static move = async (oldKey: string, newKey: string) => {
|
|
19
|
+
switch (EnvironmentBase.fileStore) {
|
|
20
|
+
case "S3": await AwsHelper.S3Move(oldKey, newKey); break;
|
|
21
|
+
default: await FileStorageHelper.moveLocal(oldKey, newKey); break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static store = async (key: string, contentType: string, contents: Buffer) => {
|
|
26
|
+
switch (EnvironmentBase.fileStore) {
|
|
27
|
+
case "S3": await AwsHelper.S3Upload(key, contentType, contents); break;
|
|
28
|
+
default: await FileStorageHelper.storeLocal(key, contents); break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static remove = async (key: string) => {
|
|
33
|
+
switch (EnvironmentBase.fileStore) {
|
|
34
|
+
case "S3": await AwsHelper.S3Remove(key); break;
|
|
35
|
+
default: await FileStorageHelper.removeLocal(key); break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static removeFolder = async (key: string) => {
|
|
40
|
+
switch (EnvironmentBase.fileStore) {
|
|
41
|
+
case "S3": break; // no need on s3
|
|
42
|
+
default: await FileStorageHelper.removeLocalFolder(key); break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private static storeLocal = async (key: string, contents: Buffer) => {
|
|
47
|
+
const fileName = FileStorageHelper.rootPath + key;
|
|
48
|
+
const dirName = path.dirname(fileName);
|
|
49
|
+
if (!fs.existsSync(dirName)) fs.mkdirSync(dirName, { recursive: true });
|
|
50
|
+
fs.writeFileSync(fileName, contents);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private static moveLocal = async (oldKey: string, newKey: string) => {
|
|
54
|
+
fs.rename(oldKey, newKey, err => { throw err; });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private static removeLocal = async (key: string) => {
|
|
58
|
+
fs.unlinkSync(FileStorageHelper.rootPath + key);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private static removeLocalFolder = async (key: string) => {
|
|
62
|
+
fs.rmdirSync(FileStorageHelper.rootPath + key);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private static listLocal = async (filePath: string) => {
|
|
66
|
+
const fullPath = FileStorageHelper.rootPath + filePath;
|
|
67
|
+
if (!fs.existsSync(fullPath)) return [];
|
|
68
|
+
else return fs.readdirSync(FileStorageHelper.rootPath + filePath);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import winston from "winston";
|
|
2
|
+
import WinstonCloudWatch from "winston-cloudwatch";
|
|
3
|
+
import AWS from "aws-sdk";
|
|
4
|
+
import { EnvironmentBase } from ".";
|
|
5
|
+
|
|
6
|
+
export class LoggingHelper {
|
|
7
|
+
private static _current: LoggingHelper = null;
|
|
8
|
+
public static getCurrent = () => {
|
|
9
|
+
if (LoggingHelper._current === null) {
|
|
10
|
+
LoggingHelper._current = new LoggingHelper();
|
|
11
|
+
LoggingHelper._current.init("API");
|
|
12
|
+
}
|
|
13
|
+
return LoggingHelper._current;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private _logger: winston.Logger = null;
|
|
17
|
+
private wc: WinstonCloudWatch;
|
|
18
|
+
private pendingMessages = false;
|
|
19
|
+
private logGroupName = EnvironmentBase.appName + "_" + EnvironmentBase.appEnv;
|
|
20
|
+
private logDestination = "console";
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
public error(msg: string | object) {
|
|
25
|
+
if (this._logger === null) this.init("API");
|
|
26
|
+
this.pendingMessages = true;
|
|
27
|
+
if (EnvironmentBase.appEnv === "dev") console.log(msg);
|
|
28
|
+
this._logger.error(msg);
|
|
29
|
+
this._logger.error(new Error().stack.toString());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public info(msg: string | object) {
|
|
33
|
+
if (this._logger === null) this.init("API");
|
|
34
|
+
this.pendingMessages = true;
|
|
35
|
+
this._logger.info(msg);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public log(streamName: string, level: string, msg: string | object) {
|
|
39
|
+
if (this._logger === null) this.init(streamName);
|
|
40
|
+
this.pendingMessages = true;
|
|
41
|
+
if (level === "info") this._logger.info(msg);
|
|
42
|
+
else this._logger.error(msg);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
private init(streamName: string) {
|
|
47
|
+
this.pendingMessages = false;
|
|
48
|
+
AWS.config.update({ region: "us-east-2" });
|
|
49
|
+
if (EnvironmentBase.appEnv === "staging") this.logDestination = "cloudwatch";
|
|
50
|
+
else if (EnvironmentBase.appEnv === "prod") this.logDestination = "cloudwatch";
|
|
51
|
+
|
|
52
|
+
if (this.logDestination === "cloudwatch") {
|
|
53
|
+
this.wc = new WinstonCloudWatch({ logGroupName: this.logGroupName, logStreamName: streamName, name: this.logGroupName + "_" + streamName });
|
|
54
|
+
this._logger = winston.createLogger({ transports: [this.wc], format: winston.format.json(), });
|
|
55
|
+
} else this._logger = winston.createLogger({ transports: [new winston.transports.Console()], format: winston.format.json() });
|
|
56
|
+
this._logger.info("Logger initialized");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public flush() {
|
|
60
|
+
const promise = new Promise((resolve) => {
|
|
61
|
+
if (this.pendingMessages) {
|
|
62
|
+
if (this.wc) {
|
|
63
|
+
this.wc.kthxbye(() => {
|
|
64
|
+
// this._logger = null;
|
|
65
|
+
this.pendingMessages = false;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
resolve(null);
|
|
69
|
+
} else resolve(null);
|
|
70
|
+
});
|
|
71
|
+
return promise;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
}
|