@goatlab/node-backend 0.0.16 → 0.1.0
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/README.md +54 -38
- package/dist/container/Container.d.ts +441 -0
- package/dist/container/Container.js +895 -0
- package/dist/container/Container.js.map +1 -0
- package/dist/container/DistributedCacheInvalidator.d.ts +84 -0
- package/dist/container/DistributedCacheInvalidator.js +213 -0
- package/dist/container/DistributedCacheInvalidator.js.map +1 -0
- package/dist/container/LruCache.d.ts +14 -0
- package/dist/container/LruCache.js +23 -0
- package/dist/container/LruCache.js.map +1 -0
- package/dist/container/types.d.ts +128 -0
- package/dist/container/types.js +6 -0
- package/dist/container/types.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +53 -1
- package/dist/index.js.map +1 -1
- package/dist/server/bootstraps/getExpressTrpcApp.d.ts +17 -0
- package/dist/server/bootstraps/getExpressTrpcApp.js +98 -0
- package/dist/server/bootstraps/getExpressTrpcApp.js.map +1 -0
- package/dist/server/consts.d.ts +35 -0
- package/dist/server/consts.js +33 -0
- package/dist/server/consts.js.map +1 -0
- package/dist/server/context/context.model.d.ts +13 -0
- package/dist/server/context/context.model.js +3 -0
- package/dist/server/context/context.model.js.map +1 -0
- package/dist/server/context/request.context.d.ts +40 -0
- package/dist/server/context/request.context.js +65 -0
- package/dist/server/context/request.context.js.map +1 -0
- package/dist/server/context/trpc.context.d.ts +11 -0
- package/dist/server/context/trpc.context.js +67 -0
- package/dist/server/context/trpc.context.js.map +1 -0
- package/dist/server/initOpenApiDocs.d.ts +9 -0
- package/dist/server/initOpenApiDocs.js +18 -0
- package/dist/server/initOpenApiDocs.js.map +1 -0
- package/dist/server/middleware/cloudTaskDecrypt.middleware.d.ts +6 -0
- package/dist/server/middleware/cloudTaskDecrypt.middleware.js +44 -0
- package/dist/server/middleware/cloudTaskDecrypt.middleware.js.map +1 -0
- package/dist/server/middleware/error.middleware.d.ts +17 -0
- package/dist/server/middleware/error.middleware.js +66 -0
- package/dist/server/middleware/error.middleware.js.map +1 -0
- package/dist/server/middleware/handleRequest.middleware.d.ts +7 -0
- package/dist/server/middleware/handleRequest.middleware.js +40 -0
- package/dist/server/middleware/handleRequest.middleware.js.map +1 -0
- package/dist/server/middleware/logger/cloudRun.logger.d.ts +27 -0
- package/dist/server/middleware/logger/cloudRun.logger.js +87 -0
- package/dist/server/middleware/logger/cloudRun.logger.js.map +1 -0
- package/dist/server/middleware/logger/logger.service.d.ts +6 -0
- package/dist/server/middleware/logger/logger.service.js +17 -0
- package/dist/server/middleware/logger/logger.service.js.map +1 -0
- package/dist/server/middleware/logs.middleware.d.ts +7 -0
- package/dist/server/middleware/logs.middleware.js +130 -0
- package/dist/server/middleware/logs.middleware.js.map +1 -0
- package/dist/server/middleware/requireAuthenticated.d.ts +2 -0
- package/dist/server/middleware/requireAuthenticated.js +13 -0
- package/dist/server/middleware/requireAuthenticated.js.map +1 -0
- package/dist/server/middleware/trpcError.middleware.d.ts +4 -0
- package/dist/server/middleware/trpcError.middleware.js +38 -0
- package/dist/server/middleware/trpcError.middleware.js.map +1 -0
- package/dist/server/schemas/user.schema.d.ts +109 -0
- package/dist/server/schemas/user.schema.js +28 -0
- package/dist/server/schemas/user.schema.js.map +1 -0
- package/dist/server/sentry/getSentry.d.ts +6 -0
- package/dist/server/sentry/getSentry.js +45 -0
- package/dist/server/sentry/getSentry.js.map +1 -0
- package/dist/server/sentry/sentry.service.d.ts +34 -0
- package/dist/server/sentry/sentry.service.js +110 -0
- package/dist/server/sentry/sentry.service.js.map +1 -0
- package/dist/server/services/email/email.model.d.ts +84 -0
- package/dist/server/services/email/email.model.js +62 -0
- package/dist/server/services/email/email.model.js.map +1 -0
- package/dist/server/services/email/email.service.d.ts +23 -0
- package/dist/server/services/email/email.service.js +139 -0
- package/dist/server/services/email/email.service.js.map +1 -0
- package/dist/server/services/gcp/getGcpServiceAccountFromBase64.d.ts +15 -0
- package/dist/server/services/gcp/getGcpServiceAccountFromBase64.js +9 -0
- package/dist/server/services/gcp/getGcpServiceAccountFromBase64.js.map +1 -0
- package/dist/server/services/secrets/secret.service.d.ts +31 -0
- package/dist/server/services/secrets/secret.service.js +172 -0
- package/dist/server/services/secrets/secret.service.js.map +1 -0
- package/dist/server/services/sendgrid/sendgrid.model.d.ts +118 -0
- package/dist/server/services/sendgrid/sendgrid.model.js +3 -0
- package/dist/server/services/sendgrid/sendgrid.model.js.map +1 -0
- package/dist/server/services/sendgrid/sendgridApi.service.d.ts +13 -0
- package/dist/server/services/sendgrid/sendgridApi.service.js +79 -0
- package/dist/server/services/sendgrid/sendgridApi.service.js.map +1 -0
- package/dist/server/services/sendgrid/sendgridHooks.model.d.ts +27 -0
- package/dist/server/services/sendgrid/sendgridHooks.model.js +19 -0
- package/dist/server/services/sendgrid/sendgridHooks.model.js.map +1 -0
- package/dist/server/services/translations/template.util.d.ts +7 -0
- package/dist/server/services/translations/template.util.js +11 -0
- package/dist/server/services/translations/template.util.js.map +1 -0
- package/dist/server/services/translations/translation.model.d.ts +4 -0
- package/dist/server/services/translations/translation.model.js +6 -0
- package/dist/server/services/translations/translation.model.js.map +1 -0
- package/dist/server/services/translations/translation.service.d.ts +25 -0
- package/dist/server/services/translations/translation.service.js +97 -0
- package/dist/server/services/translations/translation.service.js.map +1 -0
- package/dist/server/services/util/benchmarker.d.ts +13 -0
- package/dist/server/services/util/benchmarker.js +34 -0
- package/dist/server/services/util/benchmarker.js.map +1 -0
- package/dist/server/services/util/pagination.d.ts +50 -0
- package/dist/server/services/util/pagination.js +57 -0
- package/dist/server/services/util/pagination.js.map +1 -0
- package/dist/server/services/util/url.service.d.ts +75 -0
- package/dist/server/services/util/url.service.js +139 -0
- package/dist/server/services/util/url.service.js.map +1 -0
- package/dist/server/test/express.mock.d.ts +6 -0
- package/dist/server/test/express.mock.js +49 -0
- package/dist/server/test/express.mock.js.map +1 -0
- package/dist/server/test/firebase.mock.d.ts +4 -0
- package/dist/server/test/firebase.mock.js +30 -0
- package/dist/server/test/firebase.mock.js.map +1 -0
- package/dist/server/test/mock.model.d.ts +5 -0
- package/dist/server/test/mock.model.js +3 -0
- package/dist/server/test/mock.model.js.map +1 -0
- package/dist/server/test/trpc.mock.d.ts +6 -0
- package/dist/server/test/trpc.mock.js +14 -0
- package/dist/server/test/trpc.mock.js.map +1 -0
- package/dist/server/trpc.d.ts +364 -0
- package/dist/server/trpc.js +87 -0
- package/dist/server/trpc.js.map +1 -0
- package/dist/server/types/Envinronment.d.ts +1 -0
- package/dist/server/types/Envinronment.js +3 -0
- package/dist/server/types/Envinronment.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +33 -3
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SentryService = void 0;
|
|
4
|
+
const js_utils_1 = require("@goatlab/js-utils");
|
|
5
|
+
const sentrySeverityMap = {
|
|
6
|
+
debug: 'log',
|
|
7
|
+
info: 'log',
|
|
8
|
+
log: 'log',
|
|
9
|
+
warning: 'warn',
|
|
10
|
+
error: 'error',
|
|
11
|
+
fatal: 'error',
|
|
12
|
+
};
|
|
13
|
+
class SentryService {
|
|
14
|
+
config;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
}
|
|
18
|
+
//@Memo.syncMethod()
|
|
19
|
+
sentry() {
|
|
20
|
+
// Lazy-loading `@sentry/node`
|
|
21
|
+
const sentry = require('@sentry/node');
|
|
22
|
+
if (!this.config.dsn) {
|
|
23
|
+
return sentry;
|
|
24
|
+
}
|
|
25
|
+
console.log('SentryService init...');
|
|
26
|
+
sentry.init({
|
|
27
|
+
maxValueLength: 2000, // Default is 250 characters
|
|
28
|
+
...this.config,
|
|
29
|
+
});
|
|
30
|
+
return sentry;
|
|
31
|
+
}
|
|
32
|
+
init() {
|
|
33
|
+
this.sentry();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* For GDPR reasons we never send more information than just User ID.
|
|
37
|
+
*/
|
|
38
|
+
setUserId(id) {
|
|
39
|
+
this.sentry().getCurrentScope().setUser({
|
|
40
|
+
id,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Does console.error(err)
|
|
45
|
+
* Returns "eventId" or undefined (if error was not reported).
|
|
46
|
+
*/
|
|
47
|
+
captureException(error, logError = true) {
|
|
48
|
+
// Console.error(err)
|
|
49
|
+
// Using request-aware logger here
|
|
50
|
+
if (logError) {
|
|
51
|
+
this.config.logger?.error('captureException:', error);
|
|
52
|
+
}
|
|
53
|
+
if (error?.data?.report === false) {
|
|
54
|
+
// Skip reporting the error
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// This is to avoid Sentry cutting err.message to 253 characters
|
|
58
|
+
// It will log additional "breadcrumb object" before the error
|
|
59
|
+
// It's a Breadcrumb, not a console.log, because console.log are NOT automatically attached as Breadcrumbs in cron-job environments (outside of Express)
|
|
60
|
+
this.sentry().addBreadcrumb({
|
|
61
|
+
message: js_utils_1.Inspect.any(error, {
|
|
62
|
+
includeErrorData: true,
|
|
63
|
+
colors: false,
|
|
64
|
+
}),
|
|
65
|
+
// Data: (err as AppError).data, // included in message
|
|
66
|
+
});
|
|
67
|
+
return this.sentry().captureException(js_utils_1.Errors.anyToError(error, Error, {
|
|
68
|
+
stringifyFn: js_utils_1.Inspect.anyStringifyFn,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns "eventId"
|
|
73
|
+
*/
|
|
74
|
+
captureMessage(message, level) {
|
|
75
|
+
this.config.logger?.[sentrySeverityMap[level || 'error'] || 'log']('captureMessage:', message);
|
|
76
|
+
return this.sentry().captureMessage(message, level);
|
|
77
|
+
}
|
|
78
|
+
addBreadcrumb(breadcrumb) {
|
|
79
|
+
this.sentry().addBreadcrumb(breadcrumb);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Currently it will only use `logger.error` ("error" level) and ignore `log` and `warn`.
|
|
83
|
+
*
|
|
84
|
+
* For each `logger.error` - it'll do a captureException.
|
|
85
|
+
*
|
|
86
|
+
* @experimental
|
|
87
|
+
*/
|
|
88
|
+
getCommonLogger() {
|
|
89
|
+
return {
|
|
90
|
+
log() { }, // Noop
|
|
91
|
+
warn() { }, // Noop
|
|
92
|
+
error: (...args) => {
|
|
93
|
+
const message = args
|
|
94
|
+
.map((arg) => js_utils_1.Inspect.any(arg, {
|
|
95
|
+
includeErrorData: true,
|
|
96
|
+
colors: false,
|
|
97
|
+
}))
|
|
98
|
+
.join(' ');
|
|
99
|
+
this.sentry().addBreadcrumb({
|
|
100
|
+
message,
|
|
101
|
+
});
|
|
102
|
+
this.sentry().captureException(js_utils_1.Errors.anyToError(args.length === 1 ? args[0] : args, Error, {
|
|
103
|
+
stringifyFn: js_utils_1.Inspect.anyStringifyFn,
|
|
104
|
+
}));
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.SentryService = SentryService;
|
|
110
|
+
//# sourceMappingURL=sentry.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.service.js","sourceRoot":"","sources":["../../../src/server/sentry/sentry.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare enum EmailCategory {
|
|
3
|
+
SIMPLE = "SIMPLE",
|
|
4
|
+
TEST_EMAIL = "TEST_EMAIL",
|
|
5
|
+
EMAIL_VERIFICATION = "EMAIL_VERIFICATION",
|
|
6
|
+
SEND_ONCE = "SEND_ONCE",
|
|
7
|
+
OTP = "OTP",
|
|
8
|
+
MEETING = "MEETING",
|
|
9
|
+
ORGANIZATION_INVITATION = "ORGANIZATION_INVITATION",
|
|
10
|
+
PDF_EXAM = "PDF_EXAM"
|
|
11
|
+
}
|
|
12
|
+
export interface EmailAddress {
|
|
13
|
+
email: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface EmailTest {
|
|
17
|
+
to: string;
|
|
18
|
+
subject: string;
|
|
19
|
+
mjml: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const testEmailTemplate: z.ZodObject<{
|
|
22
|
+
to: z.ZodString;
|
|
23
|
+
subject: z.ZodString;
|
|
24
|
+
mjml: z.ZodString;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
to?: string;
|
|
27
|
+
subject?: string;
|
|
28
|
+
mjml?: string;
|
|
29
|
+
}, {
|
|
30
|
+
to?: string;
|
|
31
|
+
subject?: string;
|
|
32
|
+
mjml?: string;
|
|
33
|
+
}>;
|
|
34
|
+
export interface EmailAttachment {
|
|
35
|
+
Content: string;
|
|
36
|
+
Type: string;
|
|
37
|
+
FileName: string;
|
|
38
|
+
}
|
|
39
|
+
export declare enum Layout {
|
|
40
|
+
default = "layouts/default.ejs"
|
|
41
|
+
}
|
|
42
|
+
export declare enum Content {
|
|
43
|
+
simple = "simple/simple.ejs",
|
|
44
|
+
doubleAction = "doubleAction/doubleAction.ejs"
|
|
45
|
+
}
|
|
46
|
+
export type Theme = {
|
|
47
|
+
logo: string;
|
|
48
|
+
logoLink: string;
|
|
49
|
+
primaryColor: string;
|
|
50
|
+
buttonTextColor: string;
|
|
51
|
+
privacyPolicyLink: string;
|
|
52
|
+
unsubscribeLink: string;
|
|
53
|
+
facebook: string;
|
|
54
|
+
instagram: string;
|
|
55
|
+
twitter: string;
|
|
56
|
+
appName: string;
|
|
57
|
+
};
|
|
58
|
+
export interface EmailVerificationResp {
|
|
59
|
+
res?: boolean;
|
|
60
|
+
suggestion?: string;
|
|
61
|
+
}
|
|
62
|
+
interface SimpleTemplate {
|
|
63
|
+
layout: Layout.default;
|
|
64
|
+
content: Content.simple;
|
|
65
|
+
categories: [EmailCategory.SIMPLE | EmailCategory.OTP | EmailCategory.MEETING];
|
|
66
|
+
theme?: Theme;
|
|
67
|
+
placeholders: {
|
|
68
|
+
title?: string;
|
|
69
|
+
imgSrc?: string;
|
|
70
|
+
imgAlt?: string;
|
|
71
|
+
greeting: string;
|
|
72
|
+
body: string;
|
|
73
|
+
footer: string;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export type EmailTemplates = SimpleTemplate;
|
|
77
|
+
export interface SendEmailFromTemplateParams {
|
|
78
|
+
template: EmailTemplates;
|
|
79
|
+
to: string;
|
|
80
|
+
subject: string;
|
|
81
|
+
attachments?: EmailAttachment[];
|
|
82
|
+
archive?: boolean;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Content = exports.Layout = exports.testEmailTemplate = exports.EmailCategory = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
// export const themeVariables = {
|
|
6
|
+
// [Theme.Gealium]: {
|
|
7
|
+
// logo: `https://www.${env.BASE_DOMAIN}/assets/images/logo-wide-black.png`,
|
|
8
|
+
// logoLink: `https://www.${env.BASE_DOMAIN}/`,
|
|
9
|
+
// primaryColor: '#4db7a3',
|
|
10
|
+
// buttonTextColor: '#ffffff',
|
|
11
|
+
// privacyPolicyLink: 'https://www.gealium.com/about/privacy',
|
|
12
|
+
// unsubscribeLink: 'https://www.gealium.com',
|
|
13
|
+
// facebook: 'Gealium',
|
|
14
|
+
// instagram: 'gealium_com',
|
|
15
|
+
// twitter: 'https://www.gealium.com',
|
|
16
|
+
// appName: 'Gealium',
|
|
17
|
+
// },
|
|
18
|
+
// [Theme.Agrosocial]: {
|
|
19
|
+
// logo: `https://storage.googleapis.com/public-agrosocial-prod/assets/AGSemailcode.png`,
|
|
20
|
+
// logoLink: `https://www.${env.BASE_DOMAIN}/`,
|
|
21
|
+
// primaryColor: '#4db7a3',
|
|
22
|
+
// buttonTextColor: '#ffffff',
|
|
23
|
+
// privacyPolicyLink: 'https://www.agrosocial.com/termsandprivacy',
|
|
24
|
+
// unsubscribeLink: 'https://www.agrosocial.com',
|
|
25
|
+
// facebook: 'agrosocial',
|
|
26
|
+
// instagram: 'agrosocial',
|
|
27
|
+
// twitter: 'https://www.agrosocial.com',
|
|
28
|
+
// appName: 'AgroSocial',
|
|
29
|
+
// },
|
|
30
|
+
// }
|
|
31
|
+
// export const getThemeFromEnv = (): Theme => {
|
|
32
|
+
// if (env.APP_NAME.toLowerCase() === 'agrosocial') {
|
|
33
|
+
// return Theme.Agrosocial
|
|
34
|
+
// }
|
|
35
|
+
// return Theme.Gealium
|
|
36
|
+
// }
|
|
37
|
+
var EmailCategory;
|
|
38
|
+
(function (EmailCategory) {
|
|
39
|
+
EmailCategory["SIMPLE"] = "SIMPLE";
|
|
40
|
+
EmailCategory["TEST_EMAIL"] = "TEST_EMAIL";
|
|
41
|
+
EmailCategory["EMAIL_VERIFICATION"] = "EMAIL_VERIFICATION";
|
|
42
|
+
EmailCategory["SEND_ONCE"] = "SEND_ONCE";
|
|
43
|
+
EmailCategory["OTP"] = "OTP";
|
|
44
|
+
EmailCategory["MEETING"] = "MEETING";
|
|
45
|
+
EmailCategory["ORGANIZATION_INVITATION"] = "ORGANIZATION_INVITATION";
|
|
46
|
+
EmailCategory["PDF_EXAM"] = "PDF_EXAM";
|
|
47
|
+
})(EmailCategory || (exports.EmailCategory = EmailCategory = {}));
|
|
48
|
+
exports.testEmailTemplate = zod_1.z.object({
|
|
49
|
+
to: zod_1.z.string(),
|
|
50
|
+
subject: zod_1.z.string(),
|
|
51
|
+
mjml: zod_1.z.string(),
|
|
52
|
+
});
|
|
53
|
+
var Layout;
|
|
54
|
+
(function (Layout) {
|
|
55
|
+
Layout["default"] = "layouts/default.ejs";
|
|
56
|
+
})(Layout || (exports.Layout = Layout = {}));
|
|
57
|
+
var Content;
|
|
58
|
+
(function (Content) {
|
|
59
|
+
Content["simple"] = "simple/simple.ejs";
|
|
60
|
+
Content["doubleAction"] = "doubleAction/doubleAction.ejs";
|
|
61
|
+
})(Content || (exports.Content = Content = {}));
|
|
62
|
+
//# sourceMappingURL=email.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.model.js","sourceRoot":"","sources":["../../../../src/server/services/email/email.model.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEvB,kCAAkC;AAClC,uBAAuB;AACvB,gFAAgF;AAChF,mDAAmD;AACnD,+BAA+B;AAC/B,kCAAkC;AAClC,kEAAkE;AAClE,kDAAkD;AAClD,2BAA2B;AAC3B,gCAAgC;AAChC,0CAA0C;AAC1C,0BAA0B;AAC1B,OAAO;AACP,0BAA0B;AAC1B,6FAA6F;AAC7F,mDAAmD;AACnD,+BAA+B;AAC/B,kCAAkC;AAClC,uEAAuE;AACvE,qDAAqD;AACrD,8BAA8B;AAC9B,+BAA+B;AAC/B,6CAA6C;AAC7C,6BAA6B;AAC7B,OAAO;AACP,IAAI;AAEJ,gDAAgD;AAChD,uDAAuD;AACvD,8BAA8B;AAC9B,MAAM;AAEN,yBAAyB;AACzB,IAAI;AAEJ,IAAY,aASX;AATD,WAAY,aAAa;IACvB,kCAAiB,CAAA;IACjB,0CAAyB,CAAA;IACzB,0DAAyC,CAAA;IACzC,wCAAuB,CAAA;IACvB,4BAAW,CAAA;IACX,oCAAmB,CAAA;IACnB,oEAAmD,CAAA;IACnD,sCAAqB,CAAA;AACvB,CAAC,EATW,aAAa,6BAAb,aAAa,QASxB;AAaY,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE;IACd,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAA;AAWF,IAAY,MAEX;AAFD,WAAY,MAAM;IAChB,yCAA+B,CAAA;AACjC,CAAC,EAFW,MAAM,sBAAN,MAAM,QAEjB;AAED,IAAY,OAGX;AAHD,WAAY,OAAO;IACjB,uCAA4B,CAAA;IAC5B,yDAA8C,CAAA;AAChD,CAAC,EAHW,OAAO,uBAAP,OAAO,QAGlB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SendGridEmailResponse } from '../sendgrid/sendgrid.model';
|
|
2
|
+
import type { EmailTest, SendEmailFromTemplateParams, Theme } from './email.model';
|
|
3
|
+
import { SendgridService } from '../sendgrid/sendgridApi.service';
|
|
4
|
+
export declare class EmailService {
|
|
5
|
+
private shouldSendEmail;
|
|
6
|
+
private baseDomain;
|
|
7
|
+
private fromName;
|
|
8
|
+
private emailTransport;
|
|
9
|
+
private emailArchive;
|
|
10
|
+
private theme;
|
|
11
|
+
constructor({ fromName, shouldSendEmail, baseDomain, emailTransport, emailArchive, theme, }: {
|
|
12
|
+
fromName: string;
|
|
13
|
+
shouldSendEmail: boolean;
|
|
14
|
+
baseDomain?: string;
|
|
15
|
+
emailTransport: SendgridService;
|
|
16
|
+
emailArchive?: string;
|
|
17
|
+
theme: Theme;
|
|
18
|
+
});
|
|
19
|
+
private compileTemplate;
|
|
20
|
+
private compileMjml;
|
|
21
|
+
sendEmailFromTemplate({ template, to, subject, attachments, archive, }: SendEmailFromTemplateParams): Promise<SendGridEmailResponse>;
|
|
22
|
+
sendEmailTemplateTest(email: EmailTest): Promise<SendGridEmailResponse>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmailService = void 0;
|
|
4
|
+
const js_utils_1 = require("@goatlab/js-utils");
|
|
5
|
+
const ejs_1 = require("ejs");
|
|
6
|
+
const mjml_1 = require("mjml");
|
|
7
|
+
const consts_1 = require("../../consts");
|
|
8
|
+
const email_model_1 = require("./email.model");
|
|
9
|
+
class EmailService {
|
|
10
|
+
shouldSendEmail = true;
|
|
11
|
+
baseDomain = '@goatlab.com';
|
|
12
|
+
fromName = '';
|
|
13
|
+
emailTransport;
|
|
14
|
+
emailArchive;
|
|
15
|
+
theme;
|
|
16
|
+
constructor({ fromName, shouldSendEmail, baseDomain, emailTransport, emailArchive, theme, }) {
|
|
17
|
+
this.shouldSendEmail = shouldSendEmail;
|
|
18
|
+
this.fromName = fromName;
|
|
19
|
+
if (baseDomain) {
|
|
20
|
+
this.baseDomain = baseDomain;
|
|
21
|
+
}
|
|
22
|
+
this.emailTransport = emailTransport;
|
|
23
|
+
if (emailArchive?.length) {
|
|
24
|
+
this.emailArchive = emailArchive;
|
|
25
|
+
}
|
|
26
|
+
if (theme) {
|
|
27
|
+
this.theme = theme;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async compileTemplate(template) {
|
|
31
|
+
const innerContent = await (0, ejs_1.renderFile)(`${consts_1.config.templateDir}/${template.content}`, {
|
|
32
|
+
...template.placeholders,
|
|
33
|
+
theme: this.theme,
|
|
34
|
+
});
|
|
35
|
+
const html = await (0, ejs_1.renderFile)(`${consts_1.config.templateDir}/${template.layout}`, {
|
|
36
|
+
...template.placeholders,
|
|
37
|
+
theme: this.theme,
|
|
38
|
+
content: innerContent,
|
|
39
|
+
});
|
|
40
|
+
return html;
|
|
41
|
+
}
|
|
42
|
+
compileMjml(mjml) {
|
|
43
|
+
const compiledMjMl = (0, mjml_1.default)(mjml, {
|
|
44
|
+
keepComments: false,
|
|
45
|
+
});
|
|
46
|
+
if (compiledMjMl.errors.length) {
|
|
47
|
+
console.log(compiledMjMl.errors);
|
|
48
|
+
throw new Error('Template could not compile');
|
|
49
|
+
}
|
|
50
|
+
return compiledMjMl.html;
|
|
51
|
+
}
|
|
52
|
+
async sendEmailFromTemplate({ template, to, subject, attachments, archive, }) {
|
|
53
|
+
if (!this.shouldSendEmail) {
|
|
54
|
+
return {
|
|
55
|
+
isSuccess: true,
|
|
56
|
+
statusCode: 1,
|
|
57
|
+
body: 'Email not sent. No emails are sent in test mode',
|
|
58
|
+
headers: {},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const mjml = await this.compileTemplate(template);
|
|
62
|
+
const html = this.compileMjml(mjml);
|
|
63
|
+
const bcc = [];
|
|
64
|
+
if (archive && this.emailArchive?.length) {
|
|
65
|
+
bcc.push({
|
|
66
|
+
email: this.emailArchive,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// This will not work if we are running in other environments (AWS/AZURE) check before using any other env
|
|
70
|
+
const isRunningInGCP =
|
|
71
|
+
// Linux container
|
|
72
|
+
!!(process.platform === 'linux' &&
|
|
73
|
+
// Cloud run
|
|
74
|
+
(process.env.K_SERVICE ??
|
|
75
|
+
// Cloud run job
|
|
76
|
+
process.env.CLOUD_RUN_JOB));
|
|
77
|
+
// Avoid sending emails to real users in dev, test and local prod
|
|
78
|
+
// Prod emails will only go out from linux and (GCP or CIRCLECI)
|
|
79
|
+
const shouldSendEmailToRealUser = to.endsWith(`@${this.baseDomain}`) ||
|
|
80
|
+
(this.shouldSendEmail && isRunningInGCP);
|
|
81
|
+
// Exit if we are testing emails and TEST_EMAIL_ADDRESS is not set
|
|
82
|
+
if (!process.env.TEST_EMAIL_ADDRESS && !shouldSendEmailToRealUser) {
|
|
83
|
+
console.log('Email not sent. TEST_EMAIL_ADDRESS env variable is missing');
|
|
84
|
+
return {
|
|
85
|
+
isSuccess: false,
|
|
86
|
+
statusCode: 1,
|
|
87
|
+
body: 'Email not sent. TEST_EMAIL_ADDRESS env variable is missing',
|
|
88
|
+
headers: {},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const recipient = shouldSendEmailToRealUser
|
|
92
|
+
? {
|
|
93
|
+
email: to,
|
|
94
|
+
}
|
|
95
|
+
: {
|
|
96
|
+
email: process.env.TEST_EMAIL_ADDRESS || '',
|
|
97
|
+
};
|
|
98
|
+
if (!this.emailTransport) {
|
|
99
|
+
throw new Error('No email transport (Sendgrid) defined');
|
|
100
|
+
}
|
|
101
|
+
return await this.emailTransport?.sendFinalizedEmail({
|
|
102
|
+
html,
|
|
103
|
+
fromEmail: `info@${this.baseDomain}`,
|
|
104
|
+
fromName: js_utils_1.Strings.capitalize(this.fromName),
|
|
105
|
+
subject,
|
|
106
|
+
replyTo: `no_reply@${this.baseDomain}`,
|
|
107
|
+
recipients: [recipient],
|
|
108
|
+
attachments,
|
|
109
|
+
categories: template.categories,
|
|
110
|
+
bcc,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async sendEmailTemplateTest(email) {
|
|
114
|
+
const html = this.compileMjml(email.mjml);
|
|
115
|
+
const [username, domain] = email.to.split('@');
|
|
116
|
+
// Emails will only go out to domain accounts
|
|
117
|
+
if (domain && domain !== this.baseDomain) {
|
|
118
|
+
throw new Error(`Cannot send emails to ${domain} accounts`);
|
|
119
|
+
}
|
|
120
|
+
if (!this.emailTransport) {
|
|
121
|
+
throw new Error('No email transport (Sendgrid) defined');
|
|
122
|
+
}
|
|
123
|
+
return await this.emailTransport?.sendFinalizedEmail({
|
|
124
|
+
html,
|
|
125
|
+
fromEmail: `info@${this.baseDomain}`,
|
|
126
|
+
fromName: js_utils_1.Strings.capitalize(this.fromName),
|
|
127
|
+
subject: email.subject,
|
|
128
|
+
replyTo: `no_reply@${this.baseDomain}`,
|
|
129
|
+
recipients: [
|
|
130
|
+
{
|
|
131
|
+
email: `${username}@${this.baseDomain}`,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
categories: [email_model_1.EmailCategory.TEST_EMAIL],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.EmailService = EmailService;
|
|
139
|
+
//# sourceMappingURL=email.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.service.js","sourceRoot":"","sources":["../../../../src/server/services/email/email.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface GCPServiceAccount {
|
|
2
|
+
[k: string]: any;
|
|
3
|
+
keyFilename: string;
|
|
4
|
+
project_id: string;
|
|
5
|
+
client_id: string;
|
|
6
|
+
client_email: string;
|
|
7
|
+
private_key: string;
|
|
8
|
+
private_key_id: string;
|
|
9
|
+
auth_uri: string;
|
|
10
|
+
type: string;
|
|
11
|
+
token_uri: string;
|
|
12
|
+
auth_provider_x509_cert_url: string;
|
|
13
|
+
client_x509_cert_url: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function getGcpServiceAccountFromBase64(base64: string): undefined | GCPServiceAccount;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGcpServiceAccountFromBase64 = getGcpServiceAccountFromBase64;
|
|
4
|
+
function getGcpServiceAccountFromBase64(base64) {
|
|
5
|
+
// Create Google credentials from Secret
|
|
6
|
+
const serviceAccount = JSON.parse(Buffer.from(base64, 'base64').toString('utf8'));
|
|
7
|
+
return serviceAccount;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=getGcpServiceAccountFromBase64.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getGcpServiceAccountFromBase64.js","sourceRoot":"","sources":["../../../../src/server/services/gcp/getGcpServiceAccountFromBase64.ts"],"names":[],"mappings":";;AAeA,wEASC;AATD,SAAgB,8BAA8B,CAC5C,MAAc;IAEd,wCAAwC;IACxC,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAC/B,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC/C,CAAA;IAED,OAAO,cAAmC,CAAA;AAC5C,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type SecretProvider = 'GCP' | 'FILE' | 'VAULT';
|
|
2
|
+
interface VaultConfig {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
mount?: string;
|
|
6
|
+
namespace?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SecretService<SecretType> {
|
|
9
|
+
provider: SecretProvider;
|
|
10
|
+
location: string;
|
|
11
|
+
encryptionKey: string;
|
|
12
|
+
vaultConfig?: VaultConfig;
|
|
13
|
+
constructor({ provider, location, encryptionKey, vaultConfig, }: {
|
|
14
|
+
provider: SecretProvider;
|
|
15
|
+
location: string;
|
|
16
|
+
encryptionKey: string;
|
|
17
|
+
vaultConfig?: VaultConfig;
|
|
18
|
+
});
|
|
19
|
+
loadSecretsFromFile(): SecretType;
|
|
20
|
+
loadSecretsFromGCP(): SecretType;
|
|
21
|
+
loadEncryptionKeyFromGCP(): string;
|
|
22
|
+
loadSecretsFromVault(): Promise<SecretType>;
|
|
23
|
+
storeSecretsToVault(secrets: Partial<SecretType>): Promise<void>;
|
|
24
|
+
loadSecrets(): SecretType | Promise<SecretType>;
|
|
25
|
+
loadEncryptionKey(): string;
|
|
26
|
+
getSecret(secretName: keyof SecretType): Promise<string>;
|
|
27
|
+
getSecretJson<T = any>(secretName: keyof SecretType): Promise<T>;
|
|
28
|
+
getSecretSync(secretName: keyof SecretType): string;
|
|
29
|
+
getSecretJsonSync<T = any>(secretName: keyof SecretType): T;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SecretService = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const undici_1 = require("undici");
|
|
6
|
+
const node_utils_1 = require("@goatlab/node-utils");
|
|
7
|
+
const colors_1 = require("kleur/colors");
|
|
8
|
+
const memoryCache = {};
|
|
9
|
+
class SecretService {
|
|
10
|
+
provider;
|
|
11
|
+
location;
|
|
12
|
+
encryptionKey;
|
|
13
|
+
vaultConfig;
|
|
14
|
+
constructor({ provider, location, encryptionKey, vaultConfig, }) {
|
|
15
|
+
this.provider = provider;
|
|
16
|
+
this.location = location;
|
|
17
|
+
this.encryptionKey = encryptionKey;
|
|
18
|
+
this.vaultConfig = vaultConfig;
|
|
19
|
+
}
|
|
20
|
+
// We should cache this call
|
|
21
|
+
loadSecretsFromFile() {
|
|
22
|
+
if (memoryCache[this.location]) {
|
|
23
|
+
return memoryCache[this.location];
|
|
24
|
+
}
|
|
25
|
+
if (!fs.existsSync(this.location)) {
|
|
26
|
+
throw new Error(`Secret file "${this.location}" does not exist`);
|
|
27
|
+
}
|
|
28
|
+
const start = process.hrtime.bigint();
|
|
29
|
+
const fileContents = fs.readFileSync(this.location, 'utf-8');
|
|
30
|
+
const secretEncryptedObject = JSON.parse(fileContents);
|
|
31
|
+
console.log(`🔐 Secrets loaded: ${(0, colors_1.magenta)(this.location.split('/').slice(-2).join('/'))}`);
|
|
32
|
+
try {
|
|
33
|
+
const decripted = node_utils_1.Security.decryptObject(secretEncryptedObject, this.encryptionKey);
|
|
34
|
+
memoryCache[this.location] = decripted;
|
|
35
|
+
const durationMs = Number(process.hrtime.bigint() - start) / 1_000_000;
|
|
36
|
+
console.log(`⏱️ loadSecrets(${this.location}) took ${durationMs.toFixed(3)}ms`);
|
|
37
|
+
return memoryCache[this.location];
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(err);
|
|
41
|
+
throw new Error(`loadSecrets failed to decrypt: ${this.location}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
loadSecretsFromGCP() {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
loadEncryptionKeyFromGCP() {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
async loadSecretsFromVault() {
|
|
51
|
+
if (!this.vaultConfig) {
|
|
52
|
+
throw new Error('Vault configuration is required for VAULT provider');
|
|
53
|
+
}
|
|
54
|
+
const cacheKey = `vault_${this.vaultConfig.endpoint}_${this.location}`;
|
|
55
|
+
if (memoryCache[cacheKey]) {
|
|
56
|
+
return memoryCache[cacheKey];
|
|
57
|
+
}
|
|
58
|
+
const start = process.hrtime.bigint();
|
|
59
|
+
try {
|
|
60
|
+
const vaultUrl = `${this.vaultConfig.endpoint}/v1/${this.vaultConfig.mount || 'secret'}/data/${this.location}`;
|
|
61
|
+
const headers = {
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
};
|
|
64
|
+
// Add token authentication
|
|
65
|
+
if (this.vaultConfig.token) {
|
|
66
|
+
headers['X-Vault-Token'] = this.vaultConfig.token;
|
|
67
|
+
}
|
|
68
|
+
// Add namespace if specified
|
|
69
|
+
if (this.vaultConfig.namespace) {
|
|
70
|
+
headers['X-Vault-Namespace'] = this.vaultConfig.namespace;
|
|
71
|
+
}
|
|
72
|
+
const response = await (0, undici_1.fetch)(vaultUrl, {
|
|
73
|
+
method: 'GET',
|
|
74
|
+
headers,
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
throw new Error(`Vault request failed: ${response.status} ${response.statusText}`);
|
|
78
|
+
}
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
// Vault KV v2 stores data in data.data
|
|
81
|
+
const secrets = data.data?.data || data.data || {};
|
|
82
|
+
memoryCache[cacheKey] = secrets;
|
|
83
|
+
const durationMs = Number(process.hrtime.bigint() - start) / 1_000_000;
|
|
84
|
+
console.log(`🔐 Secrets loaded from Vault: ${(0, colors_1.magenta)(this.location)} (${durationMs.toFixed(3)}ms)`);
|
|
85
|
+
return secrets;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error('Failed to load secrets from Vault:', error.message);
|
|
89
|
+
throw new Error(`loadSecretsFromVault failed: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async storeSecretsToVault(secrets) {
|
|
93
|
+
if (!this.vaultConfig) {
|
|
94
|
+
throw new Error('Vault configuration is required for VAULT provider');
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const vaultUrl = `${this.vaultConfig.endpoint}/v1/${this.vaultConfig.mount || 'secret'}/data/${this.location}`;
|
|
98
|
+
const headers = {
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
};
|
|
101
|
+
// Add token authentication
|
|
102
|
+
if (this.vaultConfig.token) {
|
|
103
|
+
headers['X-Vault-Token'] = this.vaultConfig.token;
|
|
104
|
+
}
|
|
105
|
+
// Add namespace if specified
|
|
106
|
+
if (this.vaultConfig.namespace) {
|
|
107
|
+
headers['X-Vault-Namespace'] = this.vaultConfig.namespace;
|
|
108
|
+
}
|
|
109
|
+
// For Vault KV v2, data needs to be wrapped in a data object
|
|
110
|
+
const payload = {
|
|
111
|
+
data: secrets,
|
|
112
|
+
};
|
|
113
|
+
const response = await (0, undici_1.fetch)(vaultUrl, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers,
|
|
116
|
+
body: JSON.stringify(payload),
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
throw new Error(`Vault store request failed: ${response.status} ${response.statusText}`);
|
|
120
|
+
}
|
|
121
|
+
// Clear cache to force reload on next access
|
|
122
|
+
const cacheKey = `vault_${this.vaultConfig.endpoint}_${this.location}`;
|
|
123
|
+
delete memoryCache[cacheKey];
|
|
124
|
+
console.log(`🔐 Secrets stored to Vault: ${(0, colors_1.magenta)(this.location)}`);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error('Failed to store secrets to Vault:', error.message);
|
|
128
|
+
throw new Error(`storeSecretsToVault failed: ${error.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
loadSecrets() {
|
|
132
|
+
if (this.provider === 'GCP') {
|
|
133
|
+
return this.loadSecretsFromGCP();
|
|
134
|
+
}
|
|
135
|
+
if (this.provider === 'VAULT') {
|
|
136
|
+
return this.loadSecretsFromVault();
|
|
137
|
+
}
|
|
138
|
+
return this.loadSecretsFromFile();
|
|
139
|
+
}
|
|
140
|
+
loadEncryptionKey() {
|
|
141
|
+
return this.encryptionKey;
|
|
142
|
+
}
|
|
143
|
+
async getSecret(secretName) {
|
|
144
|
+
const secrets = await this.loadSecrets();
|
|
145
|
+
const secret = secrets[secretName];
|
|
146
|
+
if (!secret) {
|
|
147
|
+
throw new Error(`Secret ${secretName.toString()} does not exist in ${this.location} env`);
|
|
148
|
+
}
|
|
149
|
+
return secret;
|
|
150
|
+
}
|
|
151
|
+
async getSecretJson(secretName) {
|
|
152
|
+
const secretValue = await this.getSecret(secretName);
|
|
153
|
+
return JSON.parse(secretValue);
|
|
154
|
+
}
|
|
155
|
+
// Synchronous versions for backward compatibility (FILE provider only)
|
|
156
|
+
getSecretSync(secretName) {
|
|
157
|
+
if (this.provider === 'VAULT') {
|
|
158
|
+
throw new Error('Use async getSecret() method for Vault provider');
|
|
159
|
+
}
|
|
160
|
+
const secrets = this.loadSecrets();
|
|
161
|
+
const secret = secrets[secretName];
|
|
162
|
+
if (!secret) {
|
|
163
|
+
throw new Error(`Secret ${secretName.toString()} does not exist in ${this.location} env`);
|
|
164
|
+
}
|
|
165
|
+
return secret;
|
|
166
|
+
}
|
|
167
|
+
getSecretJsonSync(secretName) {
|
|
168
|
+
return JSON.parse(this.getSecretSync(secretName));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.SecretService = SecretService;
|
|
172
|
+
//# sourceMappingURL=secret.service.js.map
|