@kava/kava-api-core 1.0.0 → 1.0.2

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.
Files changed (133) hide show
  1. package/.github/workflows/publish.yml +40 -0
  2. package/dist/{auth.util.d.ts → auth/auth.d.ts} +1 -5
  3. package/dist/{auth.util.js → auth/auth.js} +38 -30
  4. package/dist/auth/auth.js.map +1 -0
  5. package/dist/auth/index.d.ts +1 -0
  6. package/dist/auth/index.js +2 -0
  7. package/dist/auth/index.js.map +1 -0
  8. package/dist/{context.util.js → context/context.js} +1 -1
  9. package/dist/context/context.js.map +1 -0
  10. package/dist/context/index.d.ts +1 -0
  11. package/dist/context/index.js +2 -0
  12. package/dist/context/index.js.map +1 -0
  13. package/dist/{controller.util.js → controller/controller.js} +1 -1
  14. package/dist/controller/controller.js.map +1 -0
  15. package/dist/controller/index.d.ts +1 -0
  16. package/dist/controller/index.js +2 -0
  17. package/dist/controller/index.js.map +1 -0
  18. package/dist/{conversion.util.js → conversion/conversion.js} +1 -1
  19. package/dist/conversion/conversion.js.map +1 -0
  20. package/dist/conversion/index.d.ts +1 -0
  21. package/dist/conversion/index.js +2 -0
  22. package/dist/conversion/index.js.map +1 -0
  23. package/dist/{db.util.d.ts → db/db.d.ts} +9 -5
  24. package/dist/{db.util.js → db/db.js} +40 -29
  25. package/dist/db/db.js.map +1 -0
  26. package/dist/db/index.d.ts +1 -0
  27. package/dist/db/index.js +2 -0
  28. package/dist/db/index.js.map +1 -0
  29. package/dist/index.d.ts +14 -13
  30. package/dist/index.js +14 -13
  31. package/dist/index.js.map +1 -1
  32. package/dist/logger/index.d.ts +1 -0
  33. package/dist/logger/index.js +2 -0
  34. package/dist/logger/index.js.map +1 -0
  35. package/dist/{logger.util.js → logger/logger.js} +16 -7
  36. package/dist/logger/logger.js.map +1 -0
  37. package/dist/mail/index.d.ts +1 -0
  38. package/dist/mail/index.js +2 -0
  39. package/dist/mail/index.js.map +1 -0
  40. package/dist/{mail.util.js → mail/mail.js} +1 -1
  41. package/dist/mail/mail.js.map +1 -0
  42. package/dist/middleware/index.d.ts +1 -0
  43. package/dist/middleware/index.js +2 -0
  44. package/dist/middleware/index.js.map +1 -0
  45. package/dist/{middleware.util.js → middleware/middleware.js} +1 -1
  46. package/dist/middleware/middleware.js.map +1 -0
  47. package/dist/model/index.d.ts +1 -0
  48. package/dist/model/index.js +2 -0
  49. package/dist/model/index.js.map +1 -0
  50. package/dist/{model.util.js → model/model.js} +1 -1
  51. package/dist/model/model.js.map +1 -0
  52. package/dist/permission/index.d.ts +1 -0
  53. package/dist/permission/index.js +2 -0
  54. package/dist/permission/index.js.map +1 -0
  55. package/dist/{permission.util.js → permission/permission.js} +1 -1
  56. package/dist/permission/permission.js.map +1 -0
  57. package/dist/registry/index.d.ts +1 -0
  58. package/dist/registry/index.js +2 -0
  59. package/dist/registry/index.js.map +1 -0
  60. package/dist/registry/registry.d.ts +28 -0
  61. package/dist/registry/registry.js +19 -0
  62. package/dist/registry/registry.js.map +1 -0
  63. package/dist/route/index.d.ts +1 -0
  64. package/dist/route/index.js +2 -0
  65. package/dist/route/index.js.map +1 -0
  66. package/dist/{route.util.js → route/route.js} +1 -1
  67. package/dist/route/route.js.map +1 -0
  68. package/dist/storage/index.d.ts +1 -0
  69. package/dist/storage/index.js +2 -0
  70. package/dist/storage/index.js.map +1 -0
  71. package/dist/{storage.util.js → storage/storage.js} +2 -2
  72. package/dist/storage/storage.js.map +1 -0
  73. package/dist/validation/index.d.ts +1 -0
  74. package/dist/validation/index.js +2 -0
  75. package/dist/validation/index.js.map +1 -0
  76. package/dist/{validation.util.js → validation/validation.js} +1 -1
  77. package/dist/validation/validation.js.map +1 -0
  78. package/package.json +2 -2
  79. package/src/{auth.util.ts → auth/auth.ts} +255 -241
  80. package/src/auth/index.ts +1 -0
  81. package/src/{context.util.ts → context/context.ts} +17 -17
  82. package/src/context/index.ts +1 -0
  83. package/src/{controller.util.ts → controller/controller.ts} +236 -236
  84. package/src/controller/index.ts +1 -0
  85. package/src/{conversion.util.ts → conversion/conversion.ts} +64 -64
  86. package/src/conversion/index.ts +1 -0
  87. package/src/{db.util.ts → db/db.ts} +420 -405
  88. package/src/db/index.ts +1 -0
  89. package/src/index.ts +14 -13
  90. package/src/logger/index.ts +1 -0
  91. package/src/{logger.util.ts → logger/logger.ts} +176 -169
  92. package/src/mail/index.ts +1 -0
  93. package/src/{mail.util.ts → mail/mail.ts} +85 -85
  94. package/src/middleware/index.ts +1 -0
  95. package/src/{middleware.util.ts → middleware/middleware.ts} +288 -288
  96. package/src/model/index.ts +1 -0
  97. package/src/{model.util.ts → model/model.ts} +2210 -2210
  98. package/src/permission/index.ts +1 -0
  99. package/src/{permission.util.ts → permission/permission.ts} +136 -136
  100. package/src/registry/index.ts +1 -0
  101. package/src/registry/registry.ts +37 -0
  102. package/src/route/index.ts +1 -0
  103. package/src/{route.util.ts → route/route.ts} +11 -11
  104. package/src/storage/index.ts +1 -0
  105. package/src/{storage.util.ts → storage/storage.ts} +101 -101
  106. package/src/validation/index.ts +1 -0
  107. package/src/{validation.util.ts → validation/validation.ts} +338 -338
  108. package/tsconfig.json +1 -1
  109. package/bun.lock +0 -160
  110. package/dist/auth.util.js.map +0 -1
  111. package/dist/context.util.js.map +0 -1
  112. package/dist/controller.util.js.map +0 -1
  113. package/dist/conversion.util.js.map +0 -1
  114. package/dist/db.util.js.map +0 -1
  115. package/dist/logger.util.js.map +0 -1
  116. package/dist/mail.util.js.map +0 -1
  117. package/dist/middleware.util.js.map +0 -1
  118. package/dist/model.util.js.map +0 -1
  119. package/dist/permission.util.js.map +0 -1
  120. package/dist/route.util.js.map +0 -1
  121. package/dist/storage.util.js.map +0 -1
  122. package/dist/validation.util.js.map +0 -1
  123. /package/dist/{context.util.d.ts → context/context.d.ts} +0 -0
  124. /package/dist/{controller.util.d.ts → controller/controller.d.ts} +0 -0
  125. /package/dist/{conversion.util.d.ts → conversion/conversion.d.ts} +0 -0
  126. /package/dist/{logger.util.d.ts → logger/logger.d.ts} +0 -0
  127. /package/dist/{mail.util.d.ts → mail/mail.d.ts} +0 -0
  128. /package/dist/{middleware.util.d.ts → middleware/middleware.d.ts} +0 -0
  129. /package/dist/{model.util.d.ts → model/model.d.ts} +0 -0
  130. /package/dist/{permission.util.d.ts → permission/permission.d.ts} +0 -0
  131. /package/dist/{route.util.d.ts → route/route.d.ts} +0 -0
  132. /package/dist/{storage.util.d.ts → storage/storage.d.ts} +0 -0
  133. /package/dist/{validation.util.d.ts → validation/validation.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ export * from "./db";
package/src/index.ts CHANGED
@@ -1,13 +1,14 @@
1
- export * from "./auth.util";
2
- export * from "./controller.util";
3
- export * from "./conversion.util";
4
- export * from "./context.util";
5
- export * from "./db.util";
6
- export * from "./middleware.util";
7
- export * from "./model.util";
8
- export * from "./permission.util";
9
- export * from "./route.util";
10
- export * from "./storage.util";
11
- export * from "./validation.util";
12
- export * from "./mail.util";
13
- export * from "./logger.util";
1
+ export * from "./auth";
2
+ export * from "./controller";
3
+ export * from "./conversion";
4
+ export * from "./context";
5
+ export * from "./db";
6
+ export * from "./middleware";
7
+ export * from "./model";
8
+ export * from "./permission";
9
+ export * from "./route";
10
+ export * from "./storage";
11
+ export * from "./validation";
12
+ export * from "./mail";
13
+ export * from "./logger";
14
+ export * from "./registry";
@@ -0,0 +1 @@
1
+ export * from "./logger";
@@ -1,170 +1,177 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
-
4
-
5
-
6
- type LogType = "start" | "info" | "error" | "warning" | "cron" | "queue" | "queueError" | "cronError" | "socket" | "socketError";
7
-
8
- export interface AccessLog {
9
- method : string
10
- path : string
11
- status : number
12
- latency : number
13
- ip ?: string | null
14
- agent ?: string | null
15
- at ?: string
16
- }
17
-
18
- export interface ErrorLog {
19
- service ?: string
20
- key ?: string
21
- feature ?: string
22
- error : string | null
23
- reference ?: string | null
24
- at ?: string
25
- }
26
-
27
-
28
-
29
-
30
- const colors: Record<LogType | "default", string> = {
31
- default : "\x1b[0m", // default
32
- start : "\x1b[32m", // green
33
- info : "\x1b[36m", // cyan
34
- error : "\x1b[31m", // red
35
- warning : "\x1b[33m", // yellow
36
- queue : "\x1b[34m", // blue
37
- queueError : "\x1b[31m", // red
38
- cron : "\x1b[35m", // magenta
39
- cronError : "\x1b[31m", // red
40
- socket : "\x1b[35m", // blue
41
- socketError : "\x1b[31m", // red
42
- };
43
-
44
- const prefixes: Record<LogType, string> = {
45
- start : "START",
46
- info : "INFO",
47
- error : "ERROR",
48
- warning : "WARNING",
49
- cron : "CRON",
50
- queue : "QUEUE",
51
- socket : "SOCKET",
52
- queueError : "QUEUE ERROR",
53
- cronError : "CRON ERROR",
54
- socketError : "SOCKET ERROR",
55
- };
56
-
57
- function log(type: LogType, msg: string) {
58
- const color = colors[type];
59
- const prefix = prefixes[type];
60
- // eslint-disable-next-line no-console
61
- console.log(`${color}[${prefix}]${colors.default}`, msg);
62
- }
63
-
64
- export const logger = {
65
- start : (msg: string) => log("start", msg),
66
- info : (msg: string) => log("info", msg),
67
- warning : (msg: string) => log("warning", msg),
68
- queue : (msg: string) => log("queue", msg),
69
- cron : (msg: string) => log("cron", msg),
70
- socket : (msg: string) => log("socket", msg),
71
-
72
- access : (msg: AccessLog) => logAccess(msg),
73
-
74
- error: (msg: string, payload?: ErrorLog) => {
75
- log("error", msg)
76
- payload && logError({...payload, service: payload.service || 'app'})
77
- },
78
- queueError: (msg: string, payload?: ErrorLog) => {
79
- log("queueError", msg)
80
- payload && logError({...payload, service: payload.service || 'queue'})
81
- },
82
- cronError: (msg: string, payload?: ErrorLog) => {
83
- log("cronError", msg)
84
- payload && logError({...payload, service: payload.service || 'cron'})
85
- },
86
- socketError: (msg: string, payload?: ErrorLog) => {
87
- log("socketError", msg)
88
- payload && logError({...payload, service: payload.service || 'socket'})
89
- },
90
- };
91
-
92
-
93
-
94
-
95
-
96
- type DriverName = "file" | "da"
97
-
98
-
99
- const ACCESS_LOG_DRIVER = process.env.ACCESS_LOG_DRIVER || "file"
100
- const ACCESS_LOG_LOG_DIR = process.env.ACCESS_LOG_DIR || "storage/logs/access"
101
- const ACCESS_LOG_QUEUE = process.env.ACCESS_LOG_QUEUE || "access-log"
102
-
103
-
104
- const ERROR_LOG_DRIVER = process.env.ERROR_LOG_DRIVER || "file"
105
- const ERROR_LOG_LOG_DIR = process.env.ERROR_LOG_DIR || "storage/logs/error"
106
- const ERROR_LOG_QUEUE = process.env.ERROR_LOG_QUEUE_PREFIX || "error-log"
107
-
108
-
109
-
110
- // =====================
111
- // ## Access Log Drivers
112
- // =====================
113
- const filePath = () => {
114
- const d = new Date().toISOString().slice(0, 10)
115
- return path.resolve( ACCESS_LOG_LOG_DIR, `access-${d}.log`)
116
- }
117
-
118
- const handlers: Record<DriverName, (log: AccessLog) => Promise<void>> = {
119
- file: async (log) => {
120
- const dir = path.resolve(ACCESS_LOG_LOG_DIR);
121
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
122
-
123
- fs.appendFile(filePath(), JSON.stringify(log) + "\n", () => {})
124
- },
125
- da: async (log) => {
126
- // try {
127
- // await queue.add(ACCESS_LOG_QUEUE, log)
128
- // } catch {}
129
- }
130
- }
131
-
132
- const activeDrivers: DriverName[] = (ACCESS_LOG_DRIVER).split(",").map(v => v.trim()).filter((v): v is DriverName => v in handlers)
133
-
134
- function logAccess(payload: AccessLog) {
135
- for (const d of activeDrivers) {
136
- handlers[d](payload)
137
- }
138
- }
139
-
140
-
141
-
142
- // =====================
143
- // ## Error Log Drivers
144
- // =====================
145
- const errorFilePath = () => {
146
- const d = new Date().toISOString().slice(0, 10)
147
- return path.resolve(ERROR_LOG_LOG_DIR, `error-${d}.log`)
148
- }
149
-
150
- const errorHandlers: Record<DriverName, (log: ErrorLog) => Promise<void>> = {
151
- file: async (log) => {
152
- const dir = path.resolve(ERROR_LOG_LOG_DIR);
153
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
154
-
155
- fs.appendFile(errorFilePath(), JSON.stringify(log) + "\n", () => {});
156
- },
157
- da: async (log) => {
158
- // try {
159
- // await queue.add(ERROR_LOG_QUEUE, log)
160
- // } catch {}
161
- }
162
- }
163
-
164
- const activeErrorDrivers: DriverName[] = ERROR_LOG_DRIVER.split(",").map(v => v.trim()).filter((v): v is DriverName => v in errorHandlers)
165
-
166
- function logError(payload: ErrorLog) {
167
- for (const d of activeErrorDrivers) {
168
- errorHandlers[d](payload)
169
- }
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { registry } from '@utils/registry';
4
+
5
+
6
+
7
+ type LogType = "start" | "info" | "error" | "warning" | "cron" | "queue" | "queueError" | "cronError" | "socket" | "socketError";
8
+
9
+ export interface AccessLog {
10
+ method : string
11
+ path : string
12
+ status : number
13
+ latency : number
14
+ ip ?: string | null
15
+ agent ?: string | null
16
+ at ?: string
17
+ }
18
+
19
+ export interface ErrorLog {
20
+ service ?: string
21
+ key ?: string
22
+ feature ?: string
23
+ error : string | null
24
+ reference ?: string | null
25
+ at ?: string
26
+ }
27
+
28
+
29
+
30
+
31
+ const colors: Record<LogType | "default", string> = {
32
+ default : "\x1b[0m", // default
33
+ start : "\x1b[32m", // green
34
+ info : "\x1b[36m", // cyan
35
+ error : "\x1b[31m", // red
36
+ warning : "\x1b[33m", // yellow
37
+ queue : "\x1b[34m", // blue
38
+ queueError : "\x1b[31m", // red
39
+ cron : "\x1b[35m", // magenta
40
+ cronError : "\x1b[31m", // red
41
+ socket : "\x1b[35m", // blue
42
+ socketError : "\x1b[31m", // red
43
+ };
44
+
45
+ const prefixes: Record<LogType, string> = {
46
+ start : "START",
47
+ info : "INFO",
48
+ error : "ERROR",
49
+ warning : "WARNING",
50
+ cron : "CRON",
51
+ queue : "QUEUE",
52
+ socket : "SOCKET",
53
+ queueError : "QUEUE ERROR",
54
+ cronError : "CRON ERROR",
55
+ socketError : "SOCKET ERROR",
56
+ };
57
+
58
+ function log(type: LogType, msg: string) {
59
+ const color = colors[type];
60
+ const prefix = prefixes[type];
61
+ // eslint-disable-next-line no-console
62
+ console.log(`${color}[${prefix}]${colors.default}`, msg);
63
+ }
64
+
65
+ export const logger = {
66
+ start : (msg: string) => log("start", msg),
67
+ info : (msg: string) => log("info", msg),
68
+ warning : (msg: string) => log("warning", msg),
69
+ queue : (msg: string) => log("queue", msg),
70
+ cron : (msg: string) => log("cron", msg),
71
+ socket : (msg: string) => log("socket", msg),
72
+
73
+ access : (msg: AccessLog) => logAccess(msg),
74
+
75
+ error: (msg: string, payload?: ErrorLog) => {
76
+ log("error", msg)
77
+ payload && logError({...payload, service: payload.service || 'app'})
78
+ },
79
+ queueError: (msg: string, payload?: ErrorLog) => {
80
+ log("queueError", msg)
81
+ payload && logError({...payload, service: payload.service || 'queue'})
82
+ },
83
+ cronError: (msg: string, payload?: ErrorLog) => {
84
+ log("cronError", msg)
85
+ payload && logError({...payload, service: payload.service || 'cron'})
86
+ },
87
+ socketError: (msg: string, payload?: ErrorLog) => {
88
+ log("socketError", msg)
89
+ payload && logError({...payload, service: payload.service || 'socket'})
90
+ },
91
+ };
92
+
93
+
94
+
95
+
96
+
97
+ type DriverName = "file" | "da"
98
+
99
+
100
+ const ACCESS_LOG_DRIVER = process.env.ACCESS_LOG_DRIVER || "file"
101
+ const ACCESS_LOG_LOG_DIR = process.env.ACCESS_LOG_DIR || "storage/logs/access"
102
+ const ACCESS_LOG_QUEUE = process.env.ACCESS_LOG_QUEUE || "access-log"
103
+
104
+
105
+ const ERROR_LOG_DRIVER = process.env.ERROR_LOG_DRIVER || "file"
106
+ const ERROR_LOG_LOG_DIR = process.env.ERROR_LOG_DIR || "storage/logs/error"
107
+ const ERROR_LOG_QUEUE = process.env.ERROR_LOG_QUEUE_PREFIX || "error-log"
108
+
109
+
110
+
111
+ // =====================
112
+ // ## Access Log Drivers
113
+ // =====================
114
+ const filePath = () => {
115
+ const d = new Date().toISOString().slice(0, 10)
116
+ return path.resolve( ACCESS_LOG_LOG_DIR, `access-${d}.log`)
117
+ }
118
+
119
+ const handlers: Record<DriverName, (log: AccessLog) => Promise<void>> = {
120
+ file: async (log) => {
121
+ const dir = path.resolve(ACCESS_LOG_LOG_DIR);
122
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
123
+
124
+ fs.appendFile(filePath(), JSON.stringify(log) + "\n", () => {})
125
+ },
126
+ da: async (log) => {
127
+ const queue = registry.get('queue')
128
+ if (queue) {
129
+ try {
130
+ await queue.add(ACCESS_LOG_QUEUE, log)
131
+ } catch {}
132
+ }
133
+ }
134
+ }
135
+
136
+ const activeDrivers: DriverName[] = (ACCESS_LOG_DRIVER).split(",").map(v => v.trim()).filter((v): v is DriverName => v in handlers)
137
+
138
+ function logAccess(payload: AccessLog) {
139
+ for (const d of activeDrivers) {
140
+ handlers[d](payload)
141
+ }
142
+ }
143
+
144
+
145
+
146
+ // =====================
147
+ // ## Error Log Drivers
148
+ // =====================
149
+ const errorFilePath = () => {
150
+ const d = new Date().toISOString().slice(0, 10)
151
+ return path.resolve(ERROR_LOG_LOG_DIR, `error-${d}.log`)
152
+ }
153
+
154
+ const errorHandlers: Record<DriverName, (log: ErrorLog) => Promise<void>> = {
155
+ file: async (log) => {
156
+ const dir = path.resolve(ERROR_LOG_LOG_DIR);
157
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
158
+
159
+ fs.appendFile(errorFilePath(), JSON.stringify(log) + "\n", () => {});
160
+ },
161
+ da: async (log) => {
162
+ const queue = registry.get('queue')
163
+ if (queue) {
164
+ try {
165
+ await queue.add(ERROR_LOG_QUEUE, log)
166
+ } catch {}
167
+ }
168
+ }
169
+ }
170
+
171
+ const activeErrorDrivers: DriverName[] = ERROR_LOG_DRIVER.split(",").map(v => v.trim()).filter((v): v is DriverName => v in errorHandlers)
172
+
173
+ function logError(payload: ErrorLog) {
174
+ for (const d of activeErrorDrivers) {
175
+ errorHandlers[d](payload)
176
+ }
170
177
  }
@@ -0,0 +1 @@
1
+ export * from "./mail";
@@ -1,86 +1,86 @@
1
- import { readFileSync } from "fs";
2
- import { join } from "path";
3
- import nodemailer, { SentMessageInfo } from "nodemailer";
4
- import { logger } from "@utils";
5
-
6
-
7
-
8
- export interface SendMailOptions {
9
- to : string;
10
- subject : string;
11
- content ?: string;
12
- text ?: string;
13
- attachments ?: {
14
- filename : string;
15
- path : string;
16
- }[];
17
- }
18
-
19
-
20
-
21
- // =============================>
22
- // ## Mail: Send mail
23
- // =============================>
24
- export async function sendMail(options: {
25
- to : string;
26
- subject : string;
27
- text ?: string;
28
- content ?: string;
29
- attachments ?: { filename: string; path: string }[];
30
- }) {
31
- const transporter = nodemailer.createTransport({
32
- host : process.env.MAIL_HOST,
33
- port : Number(process.env.MAIL_PORT),
34
- secure : Number(process.env.MAIL_PORT) === 465,
35
- auth : {
36
- user : process.env.MAIL_USERNAME,
37
- pass : process.env.MAIL_PASSWORD,
38
- },
39
- });
40
-
41
- const info = (await transporter.sendMail({
42
- from : `${process.env.MAIL_FROM_NAME || process.env.APP_NAME} <${process.env.MAIL_FROM_ADDRESS || process.env.MAIL_USERNAME}>`,
43
- to : options.to,
44
- subject : options.subject,
45
- text : options.text,
46
- html : options.content,
47
- attachments : options.attachments,
48
- })) as SentMessageInfo;
49
-
50
- logger.info(`Email sent successfully: ${info.messageId}`)
51
- return info;
52
- }
53
-
54
-
55
-
56
- // =============================>
57
- // ## Mail: Render mail template
58
- // =============================>
59
- export function renderMailTemplate(template: string, options: Record<string, string>) {
60
- const templateDir = join(import.meta.dir, "./../outputs/mails/templates");
61
-
62
- const contentPath = join(templateDir, `${template}.mail.stub`);
63
- let content = readFileSync(contentPath, "utf-8");
64
-
65
- for (const [key, value] of Object.entries(options)) {
66
- const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
67
- content = content.replace(regex, value);
68
- }
69
-
70
- let layout = readFileSync(join(templateDir, "layout.mail.stub"), "utf-8");
71
-
72
- const globalVars = {
73
- ...options,
74
- date : "20-10-2025",
75
- app_name : process.env.APP_NAME || "",
76
- };
77
-
78
- for (const [key, value] of Object.entries(globalVars)) {
79
- const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
80
- layout = layout.replace(regex, value);
81
- }
82
-
83
- layout = layout.replace("{{content}}", content);
84
-
85
- return layout;
1
+ import { readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import nodemailer, { SentMessageInfo } from "nodemailer";
4
+ import { logger } from "@utils";
5
+
6
+
7
+
8
+ export interface SendMailOptions {
9
+ to : string;
10
+ subject : string;
11
+ content ?: string;
12
+ text ?: string;
13
+ attachments ?: {
14
+ filename : string;
15
+ path : string;
16
+ }[];
17
+ }
18
+
19
+
20
+
21
+ // =============================>
22
+ // ## Mail: Send mail
23
+ // =============================>
24
+ export async function sendMail(options: {
25
+ to : string;
26
+ subject : string;
27
+ text ?: string;
28
+ content ?: string;
29
+ attachments ?: { filename: string; path: string }[];
30
+ }) {
31
+ const transporter = nodemailer.createTransport({
32
+ host : process.env.MAIL_HOST,
33
+ port : Number(process.env.MAIL_PORT),
34
+ secure : Number(process.env.MAIL_PORT) === 465,
35
+ auth : {
36
+ user : process.env.MAIL_USERNAME,
37
+ pass : process.env.MAIL_PASSWORD,
38
+ },
39
+ });
40
+
41
+ const info = (await transporter.sendMail({
42
+ from : `${process.env.MAIL_FROM_NAME || process.env.APP_NAME} <${process.env.MAIL_FROM_ADDRESS || process.env.MAIL_USERNAME}>`,
43
+ to : options.to,
44
+ subject : options.subject,
45
+ text : options.text,
46
+ html : options.content,
47
+ attachments : options.attachments,
48
+ })) as SentMessageInfo;
49
+
50
+ logger.info(`Email sent successfully: ${info.messageId}`)
51
+ return info;
52
+ }
53
+
54
+
55
+
56
+ // =============================>
57
+ // ## Mail: Render mail template
58
+ // =============================>
59
+ export function renderMailTemplate(template: string, options: Record<string, string>) {
60
+ const templateDir = join(import.meta.dir, "./../outputs/mails/templates");
61
+
62
+ const contentPath = join(templateDir, `${template}.mail.stub`);
63
+ let content = readFileSync(contentPath, "utf-8");
64
+
65
+ for (const [key, value] of Object.entries(options)) {
66
+ const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
67
+ content = content.replace(regex, value);
68
+ }
69
+
70
+ let layout = readFileSync(join(templateDir, "layout.mail.stub"), "utf-8");
71
+
72
+ const globalVars = {
73
+ ...options,
74
+ date : "20-10-2025",
75
+ app_name : process.env.APP_NAME || "",
76
+ };
77
+
78
+ for (const [key, value] of Object.entries(globalVars)) {
79
+ const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
80
+ layout = layout.replace(regex, value);
81
+ }
82
+
83
+ layout = layout.replace("{{content}}", content);
84
+
85
+ return layout;
86
86
  }
@@ -0,0 +1 @@
1
+ export * from "./middleware";