startx 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.
Files changed (181) hide show
  1. package/.editorconfig +20 -0
  2. package/.prettierignore +24 -0
  3. package/.prettierrc.js +52 -0
  4. package/.vscode/settings.json +3 -0
  5. package/LICENSE +21 -0
  6. package/apps/core-server/.env.example +24 -0
  7. package/apps/core-server/Dockerfile +61 -0
  8. package/apps/core-server/eslint.config.mjs +47 -0
  9. package/apps/core-server/package.json +73 -0
  10. package/apps/core-server/src/config/custom-type.ts +54 -0
  11. package/apps/core-server/src/events/index.ts +37 -0
  12. package/apps/core-server/src/index.ts +19 -0
  13. package/apps/core-server/src/middlewares/auth-middleware.ts +50 -0
  14. package/apps/core-server/src/middlewares/cors-middleware.ts +6 -0
  15. package/apps/core-server/src/middlewares/error-middleware.ts +23 -0
  16. package/apps/core-server/src/middlewares/logger-middleware.ts +21 -0
  17. package/apps/core-server/src/middlewares/notfound-middleware.ts +14 -0
  18. package/apps/core-server/src/middlewares/serve-static.ts +24 -0
  19. package/apps/core-server/src/routes/files/router.ts +7 -0
  20. package/apps/core-server/src/routes/server.ts +36 -0
  21. package/apps/core-server/tsconfig.json +10 -0
  22. package/apps/core-server/tsdown.config.ts +14 -0
  23. package/biome.json +62 -0
  24. package/configs/eslint-config/package.json +60 -0
  25. package/configs/eslint-config/plugins.d.ts +1 -0
  26. package/configs/eslint-config/src/configs/base.ts +237 -0
  27. package/configs/eslint-config/src/configs/frontend.ts +62 -0
  28. package/configs/eslint-config/src/configs/node.ts +10 -0
  29. package/configs/eslint-config/src/plugin.ts +25 -0
  30. package/configs/eslint-config/src/rules/index.ts +30 -0
  31. package/configs/eslint-config/src/rules/no-argument-spread.test.ts +47 -0
  32. package/configs/eslint-config/src/rules/no-argument-spread.ts +96 -0
  33. package/configs/eslint-config/src/rules/no-dynamic-import-template.ts +32 -0
  34. package/configs/eslint-config/src/rules/no-internal-package-import.ts +40 -0
  35. package/configs/eslint-config/src/rules/no-interpolation-in-regular-string.ts +32 -0
  36. package/configs/eslint-config/src/rules/no-json-parse-json-stringify.test.ts +34 -0
  37. package/configs/eslint-config/src/rules/no-json-parse-json-stringify.ts +49 -0
  38. package/configs/eslint-config/src/rules/no-plain-errors.ts +50 -0
  39. package/configs/eslint-config/src/rules/no-skipped-tests.ts +61 -0
  40. package/configs/eslint-config/src/rules/no-top-level-relative-imports-in-backend-module.ts +27 -0
  41. package/configs/eslint-config/src/rules/no-type-unsafe-event-emitter.ts +33 -0
  42. package/configs/eslint-config/src/rules/no-uncaught-json-parse.test.ts +21 -0
  43. package/configs/eslint-config/src/rules/no-uncaught-json-parse.ts +45 -0
  44. package/configs/eslint-config/src/rules/no-untyped-config-class-field.ts +26 -0
  45. package/configs/eslint-config/src/rules/no-unused-param-catch-clause.ts +33 -0
  46. package/configs/eslint-config/src/rules/no-useless-catch-throw.test.ts +34 -0
  47. package/configs/eslint-config/src/rules/no-useless-catch-throw.ts +47 -0
  48. package/configs/eslint-config/src/utils/json.ts +21 -0
  49. package/configs/eslint-config/tsconfig.json +8 -0
  50. package/configs/eslint-config/tsdown.config.ts +11 -0
  51. package/configs/eslint-config/vitest.config.ts +3 -0
  52. package/configs/tsdown-config/package.json +14 -0
  53. package/configs/tsdown-config/src/config/tsdown.base.ts +13 -0
  54. package/configs/typescript-config/package.json +10 -0
  55. package/configs/typescript-config/tsconfig.common.json +32 -0
  56. package/configs/typescript-config/tsconfig.frontend.json +14 -0
  57. package/configs/typescript-config/tsconfig.node.json +9 -0
  58. package/configs/vitest-config/package.json +25 -0
  59. package/configs/vitest-config/src/base.ts +34 -0
  60. package/configs/vitest-config/src/frontend.ts +15 -0
  61. package/configs/vitest-config/src/node.ts +5 -0
  62. package/configs/vitest-config/tsconfig.json +7 -0
  63. package/package.json +47 -0
  64. package/packages/@repo/constants/eslint.config.mjs +21 -0
  65. package/packages/@repo/constants/package.json +19 -0
  66. package/packages/@repo/constants/src/api.ts +1 -0
  67. package/packages/@repo/constants/src/index.ts +8 -0
  68. package/packages/@repo/constants/src/time.ts +23 -0
  69. package/packages/@repo/constants/tsconfig.json +7 -0
  70. package/packages/@repo/db/eslint.config.mjs +21 -0
  71. package/packages/@repo/db/package.json +30 -0
  72. package/packages/@repo/db/src/functions.ts +122 -0
  73. package/packages/@repo/db/src/index.ts +20 -0
  74. package/packages/@repo/db/src/schema/common.ts +49 -0
  75. package/packages/@repo/db/src/schema/index.ts +1 -0
  76. package/packages/@repo/db/tsconfig.json +13 -0
  77. package/packages/@repo/lib/eslint.config.mjs +49 -0
  78. package/packages/@repo/lib/package.json +57 -0
  79. package/packages/@repo/lib/src/bucket-module/file-storage.ts +49 -0
  80. package/packages/@repo/lib/src/bucket-module/s3-storage.ts +114 -0
  81. package/packages/@repo/lib/src/bucket-module/utils.ts +11 -0
  82. package/packages/@repo/lib/src/command-module.ts +77 -0
  83. package/packages/@repo/lib/src/constants.ts +3 -0
  84. package/packages/@repo/lib/src/cookie-module.ts +42 -0
  85. package/packages/@repo/lib/src/custom-type.ts +54 -0
  86. package/packages/@repo/lib/src/env.ts +13 -0
  87. package/packages/@repo/lib/src/error-handlers-module/index.ts +11 -0
  88. package/packages/@repo/lib/src/file-system/index.ts +90 -0
  89. package/packages/@repo/lib/src/hashing-module.ts +9 -0
  90. package/packages/@repo/lib/src/index.ts +27 -0
  91. package/packages/@repo/lib/src/logger-module/log-config.ts +16 -0
  92. package/packages/@repo/lib/src/logger-module/logger.ts +78 -0
  93. package/packages/@repo/lib/src/logger-module/memory-profiler.ts +65 -0
  94. package/packages/@repo/lib/src/mail-module/api.ts +0 -0
  95. package/packages/@repo/lib/src/mail-module/mock.ts +8 -0
  96. package/packages/@repo/lib/src/mail-module/nodemailer.ts +45 -0
  97. package/packages/@repo/lib/src/notification-module/index.ts +172 -0
  98. package/packages/@repo/lib/src/notification-module/push-notification.ts +90 -0
  99. package/packages/@repo/lib/src/oauth2-client.ts +109 -0
  100. package/packages/@repo/lib/src/otp-module.ts +98 -0
  101. package/packages/@repo/lib/src/pagination-module.ts +49 -0
  102. package/packages/@repo/lib/src/token-module.ts +35 -0
  103. package/packages/@repo/lib/src/user-session.ts +117 -0
  104. package/packages/@repo/lib/src/utils.ts +42 -0
  105. package/packages/@repo/lib/src/validation-module.ts +187 -0
  106. package/packages/@repo/lib/tsconfig.json +7 -0
  107. package/packages/@repo/mail/package.json +29 -0
  108. package/packages/@repo/mail/src/emails/admin/OtpEmail.tsx +168 -0
  109. package/packages/@repo/mail/src/index.ts +13 -0
  110. package/packages/@repo/mail/tsconfig.build.json +14 -0
  111. package/packages/@repo/mail/tsconfig.json +13 -0
  112. package/packages/@repo/mail/tsdown.config.ts +9 -0
  113. package/packages/@repo/redis/eslint.config.mjs +8 -0
  114. package/packages/@repo/redis/package.json +31 -0
  115. package/packages/@repo/redis/src/index.ts +2 -0
  116. package/packages/@repo/redis/src/lib/redis-client.ts +23 -0
  117. package/packages/@repo/redis/src/lib/redis-module.ts +3 -0
  118. package/packages/@repo/redis/tsconfig.json +12 -0
  119. package/packages/ui/components.json +17 -0
  120. package/packages/ui/eslint.config.mjs +18 -0
  121. package/packages/ui/package.json +67 -0
  122. package/packages/ui/postcss.config.mjs +9 -0
  123. package/packages/ui/src/components/custom/form-wrapper.tsx +551 -0
  124. package/packages/ui/src/components/custom/grid-component.tsx +23 -0
  125. package/packages/ui/src/components/custom/hover-tool.tsx +38 -0
  126. package/packages/ui/src/components/custom/image-picker.tsx +109 -0
  127. package/packages/ui/src/components/custom/no-content.tsx +37 -0
  128. package/packages/ui/src/components/custom/page-container.tsx +24 -0
  129. package/packages/ui/src/components/custom/page-section.tsx +59 -0
  130. package/packages/ui/src/components/custom/simple-popover.tsx +29 -0
  131. package/packages/ui/src/components/custom/switch-component.tsx +20 -0
  132. package/packages/ui/src/components/custom/theme-provider.tsx +74 -0
  133. package/packages/ui/src/components/custom/typography.tsx +111 -0
  134. package/packages/ui/src/components/extensions/carousel.tsx +392 -0
  135. package/packages/ui/src/components/hooks/event/use-click.tsx +39 -0
  136. package/packages/ui/src/components/hooks/time/useDebounce.tsx +21 -0
  137. package/packages/ui/src/components/hooks/time/useInterval.tsx +35 -0
  138. package/packages/ui/src/components/hooks/time/useTimeout.tsx +19 -0
  139. package/packages/ui/src/components/hooks/time/useTimer.tsx +51 -0
  140. package/packages/ui/src/components/hooks/use-media-query.tsx +19 -0
  141. package/packages/ui/src/components/hooks/use-persistent-storage.tsx +52 -0
  142. package/packages/ui/src/components/hooks/use-update-effect.tsx +13 -0
  143. package/packages/ui/src/components/hooks/use-window-dimension.tsx +30 -0
  144. package/packages/ui/src/components/lib/utils.ts +242 -0
  145. package/packages/ui/src/components/lucide.tsx +3 -0
  146. package/packages/ui/src/components/sonner.tsx +1 -0
  147. package/packages/ui/src/components/ui/alert-dialog.tsx +116 -0
  148. package/packages/ui/src/components/ui/avatar.tsx +53 -0
  149. package/packages/ui/src/components/ui/badge.tsx +46 -0
  150. package/packages/ui/src/components/ui/breadcrumb.tsx +109 -0
  151. package/packages/ui/src/components/ui/button.tsx +96 -0
  152. package/packages/ui/src/components/ui/card.tsx +92 -0
  153. package/packages/ui/src/components/ui/carousel.tsx +243 -0
  154. package/packages/ui/src/components/ui/checkbox.tsx +32 -0
  155. package/packages/ui/src/components/ui/command.tsx +155 -0
  156. package/packages/ui/src/components/ui/dialog.tsx +127 -0
  157. package/packages/ui/src/components/ui/dropdown-menu.tsx +226 -0
  158. package/packages/ui/src/components/ui/form.tsx +165 -0
  159. package/packages/ui/src/components/ui/input-otp.tsx +76 -0
  160. package/packages/ui/src/components/ui/input.tsx +21 -0
  161. package/packages/ui/src/components/ui/label.tsx +24 -0
  162. package/packages/ui/src/components/ui/multiple-select.tsx +510 -0
  163. package/packages/ui/src/components/ui/popover.tsx +42 -0
  164. package/packages/ui/src/components/ui/select.tsx +170 -0
  165. package/packages/ui/src/components/ui/separator.tsx +28 -0
  166. package/packages/ui/src/components/ui/sheet.tsx +130 -0
  167. package/packages/ui/src/components/ui/skeleton.tsx +13 -0
  168. package/packages/ui/src/components/ui/spinner.tsx +16 -0
  169. package/packages/ui/src/components/ui/switch.tsx +28 -0
  170. package/packages/ui/src/components/ui/table.tsx +116 -0
  171. package/packages/ui/src/components/ui/tabs.tsx +54 -0
  172. package/packages/ui/src/components/ui/textarea.tsx +18 -0
  173. package/packages/ui/src/components/ui/timeline.tsx +118 -0
  174. package/packages/ui/src/components/ui/tooltip.tsx +30 -0
  175. package/packages/ui/src/components/util/n-formattor.ts +22 -0
  176. package/packages/ui/src/components/util/storage.ts +37 -0
  177. package/packages/ui/src/globals.css +87 -0
  178. package/packages/ui/tailwind.config.ts +94 -0
  179. package/packages/ui/tsconfig.json +12 -0
  180. package/pnpm-workspace.yaml +43 -0
  181. package/turbo.json +77 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * TODO: connect to bucket
3
+ */
4
+ import {
5
+ DeleteObjectCommand,
6
+ PutObjectCommand,
7
+ type PutObjectCommandInput,
8
+ S3Client,
9
+ } from "@aws-sdk/client-s3";
10
+
11
+ const s3Endpoint = process.env.S3_ENDPOINT;
12
+
13
+ const credentials = {
14
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
15
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
16
+ };
17
+
18
+ // Create an S3 service client object.
19
+ const s3Client = new S3Client({
20
+ credentials,
21
+ region: process.env.AWS_REGION,
22
+ endpoint: s3Endpoint,
23
+ });
24
+
25
+ const bucket = process.env.AWS_BUCKET;
26
+
27
+ export type UploadFile = {
28
+ name: string;
29
+ mimetype: string;
30
+ data: Buffer;
31
+ };
32
+
33
+ export class S3Bucket {
34
+ static async uploadFile(file: UploadFile, path: string) {
35
+ let key = `${path}`;
36
+ if (!key.includes(".")) {
37
+ key += ".jpeg";
38
+ }
39
+ const params = {
40
+ Bucket: bucket,
41
+ Key: key,
42
+ Body: file.data,
43
+ ContentType: file.mimetype,
44
+ } as PutObjectCommandInput;
45
+ const command = new PutObjectCommand(params);
46
+ await s3Client.send(command);
47
+ const fileUrl = S3Bucket.getAwsUrl(key);
48
+ return fileUrl;
49
+ }
50
+
51
+ static getAwsUrl(path: string) {
52
+ return `https://${bucket}.s3.${process.env.AWS_REGION}.amazonaws.com/${path}`;
53
+ }
54
+
55
+ static getKeyFromUrl(url: string) {
56
+ const key = url.split(`https://${bucket}.s3.${process.env.AWS_REGION}.amazonaws.com/`)[1];
57
+ return key;
58
+ }
59
+
60
+ static async uploadFiles({ path, files }: { path: string; files: UploadFile[] }) {
61
+ try {
62
+ if (!path) throw new Error("Path must be provided.");
63
+ if (!files) throw new Error("File must be provided.");
64
+ // const buffers: UploadFile[] = [];
65
+ // for (const key in file) {
66
+ // const item = file[key];
67
+ // if (Array.isArray(item)) item.forEach((e) => buffers.push(e));
68
+ // else buffers.push(item!);
69
+ // }
70
+
71
+ // let upload = [] as { url: string; type: string; name: string }[];
72
+ const upload = await Promise.all(
73
+ files.map(async (file) => {
74
+ const key = file.name;
75
+ const params: PutObjectCommandInput = {
76
+ Bucket: bucket,
77
+ Key: `${path}/${key}`,
78
+ Body: file.data,
79
+ ContentType: file.mimetype,
80
+ };
81
+ const command = new PutObjectCommand(params);
82
+ await s3Client.send(command);
83
+ return {
84
+ url: S3Bucket.getAwsUrl(`${path}/${key}`),
85
+ type: file.mimetype,
86
+ name: file.name,
87
+ };
88
+ }),
89
+ );
90
+ return upload;
91
+ // if (upload.length > 0) return upload;
92
+ // else throw new Error("Something went wrong.");
93
+ } catch (error) {
94
+ console.log(error);
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ // ! don't use
100
+ static async deleteFile(path: string) {
101
+ try {
102
+ const params = {
103
+ Bucket: bucket,
104
+ Key: path,
105
+ };
106
+
107
+ const command = new DeleteObjectCommand(params);
108
+ await s3Client.send(command);
109
+ } catch (error) {
110
+ console.log(error);
111
+ throw error;
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,11 @@
1
+ import { S3Bucket } from "./s3-storage.js";
2
+
3
+ export const pathGenerator = {
4
+ asset: {
5
+ base: "image/assets",
6
+ supportImage: "image/assets/bemyguest_support.png",
7
+ },
8
+ profile: (userId: string) => `user/${userId}/profile`,
9
+ };
10
+
11
+ export const defaultAvatarUrl = `${S3Bucket.getAwsUrl("image/assets/avatar_image.png")}`;
@@ -0,0 +1,77 @@
1
+ import { logger } from "./logger-module/logger.js";
2
+
3
+ /**
4
+ * @description Custom command class that extends for creating custom commands
5
+ * @constructor
6
+ * @param name
7
+ * @param description
8
+ * @param fn
9
+ */
10
+ export class Command {
11
+ name: string;
12
+ description?: string;
13
+ fn: (flags: string[]) => void;
14
+ constructor({
15
+ description = "",
16
+ fn,
17
+ name,
18
+ }: {
19
+ name: string;
20
+ description?: string;
21
+ fn: (flags: string[]) => void;
22
+ }) {
23
+ this.name = name;
24
+ this.description = description;
25
+ this.fn = fn;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * @description Custom command executer class
31
+ */
32
+ export class CommandExecuter {
33
+ /**
34
+ * @description Available commands
35
+ */
36
+ static commands = new Map<string, Command>();
37
+
38
+ /**
39
+ * @description Register a new command
40
+ * @param command
41
+ */
42
+ static register(command: Command) {
43
+ this.commands.set(command.name, command);
44
+ }
45
+ static registerAll(commands: Command[]) {
46
+ for (const command of commands) {
47
+ this.register(command);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * @description execute the command from console
53
+ * @param string
54
+ */
55
+ static async start() {
56
+ const command = process.argv[2];
57
+ const flags = process.argv.slice(3);
58
+ if (command === undefined) {
59
+ CommandExecuter.listCommands();
60
+ } else if (this.commands.has(command)) {
61
+ this.commands.get(command)?.fn(flags);
62
+ await new Promise((resolve) => setTimeout(resolve, 1000));
63
+ logger.info(`Command executed: ${command}`);
64
+ process.exit();
65
+ } else {
66
+ console.log(`Unknown command: ${command}`);
67
+ CommandExecuter.listCommands();
68
+ }
69
+ }
70
+
71
+ static listCommands() {
72
+ console.log("Available commands:");
73
+ for (const command of this.commands.values()) {
74
+ console.log(`- ${command.name}: ${command.description}`);
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,3 @@
1
+ export const constants = {
2
+ sessionDuration: 60 * 60 * 6
3
+ };
@@ -0,0 +1,42 @@
1
+ import type { CookieOptions } from "express";
2
+
3
+ const constants = {
4
+ refreshToken: {
5
+ cookie: "",
6
+ cookieDomain: "",
7
+ secureCookie: false,
8
+ maxAge: 0,
9
+ },
10
+ initialize() {
11
+ const prodMaxAge = 1000 * 60 * 60 * 24 * 30;
12
+ const stagingMaxAge = 1000 * 60 * 60 * 24 * 365;
13
+ const prodEnv = process.env.NODE_ENV === "production";
14
+ const stagingEnv = process.env.NODE_ENV === "staging";
15
+
16
+ if (prodEnv) {
17
+ this.refreshToken.cookie = "refresh-token";
18
+ this.refreshToken.cookieDomain = process.env.COOKIE_DOMAIN;
19
+ this.refreshToken.secureCookie = true;
20
+ this.refreshToken.maxAge = prodMaxAge;
21
+ } else {
22
+ this.refreshToken.cookie = "refresh-token-staging";
23
+ this.refreshToken.cookieDomain = stagingEnv
24
+ ? process.env.COOKIE_DOMAIN
25
+ : process.env.COOKIE_DOMAIN || "localhost";
26
+ this.refreshToken.secureCookie = stagingEnv ? true : false;
27
+ this.refreshToken.maxAge = stagingMaxAge;
28
+ }
29
+ },
30
+ };
31
+ constants.initialize();
32
+
33
+ export function getCookieOptions(refreshToken: string): [string, string, CookieOptions] {
34
+ const cookieOptions: CookieOptions = {
35
+ domain: constants.refreshToken.cookieDomain,
36
+ maxAge: constants.refreshToken.maxAge,
37
+ sameSite: "lax",
38
+ httpOnly: true,
39
+ secure: constants.refreshToken.secureCookie,
40
+ };
41
+ return [constants.refreshToken.cookie, refreshToken, cookieOptions];
42
+ }
@@ -0,0 +1,54 @@
1
+ /* eslint-disable @typescript-eslint/no-namespace */
2
+ import type { SessionUser } from "./user-session.js";
3
+
4
+ declare global {
5
+ namespace NodeJS {
6
+ export interface ProcessEnv {
7
+ // from .env
8
+ COOKIE_DOMAIN: string;
9
+ NODE_ENV: "development" | "staging" | "production" | "test";
10
+ DATABASE_URL: string;
11
+ DATABASE_AUTH_TOKEN: string;
12
+ ACCESS_TOKEN_SECRET: string;
13
+ REFRESH_TOKEN_SECRET: string;
14
+ OAUTH_STATE_TOKEN_SECRET: string;
15
+ SUPPORT_MAIL: string;
16
+ AWS_ACCESS_KEY_ID: string;
17
+ AWS_SECRET_ACCESS_KEY: string;
18
+ AWS_REGION: string;
19
+ AWS_BUCKET: string;
20
+ GOOGLE_CLIENT_ID: string;
21
+ GOOGLE_CLIENT_SECRET: string;
22
+ GOOGLE_REDIRECT_URI: string;
23
+
24
+ CLIENT_URL: string;
25
+ SERVER_URL: string;
26
+ REDIS_URI: string;
27
+ REDIS_PORT: string;
28
+ REDIS_USERNAME: string;
29
+ REDIS_PASSWORD: string;
30
+ FIREBASE_PROJECT_ID: string;
31
+ STRIPE_WEBHOOK_SECRET: string;
32
+ STRIPE_SECRET_KEY: string;
33
+
34
+ SMTP_HOST: string;
35
+ SMTP_PORT: string;
36
+ SMTP_USER: string;
37
+ SMTP_PASSWORD: string;
38
+ SENDER_MAIL: string;
39
+ }
40
+ }
41
+
42
+ namespace Express {
43
+ export interface Request {
44
+ user: SessionUser;
45
+ }
46
+ }
47
+ }
48
+
49
+ declare module "http" {
50
+ interface IncomingMessage {
51
+ user: SessionUser;
52
+ body: any;
53
+ }
54
+ }
@@ -0,0 +1,13 @@
1
+ import * as dotenv from "dotenv";
2
+ import path from "path";
3
+
4
+ import { __dirname } from "./utils.js";
5
+
6
+ if (process.env.NODE_ENV === "test") {
7
+ console.log("loading test env", process.cwd(), __dirname());
8
+ dotenv.config({ path: path.join(__dirname(), ".env.test") });
9
+ } else {
10
+ dotenv.config();
11
+ dotenv.config({ path: path.join(__dirname(), ".env") });
12
+ }
13
+ export {dotenv};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @description Custom error class that extends the built-in Error class for error responses
3
+ */
4
+ export class ErrorResponse extends Error {
5
+ constructor(public message: string, public statusCode: number) {
6
+ super(message); // Call the parent constructor with the message
7
+
8
+ // Set the prototype chain explicitly for older environments (optional)
9
+ Object.setPrototypeOf(this, ErrorResponse.prototype);
10
+ }
11
+ }
@@ -0,0 +1,90 @@
1
+ import fs from "fs/promises";
2
+ import fsExists from "fs.promises.exists";
3
+ import path from "path";
4
+
5
+ import { __dirname } from "../utils.js";
6
+ export class FStool {
7
+ root = __dirname();
8
+ async writeFile({ file, content }: { file: string; content: string }) {
9
+ await fs.writeFile(path.join(this.root, file), content);
10
+ }
11
+ async writeJSONFile({ file, content, dir }: { file: string; content: object; dir?: string }) {
12
+ file = `${file}.json`;
13
+ let destination = path.join(this.root, file);
14
+ if (dir) {
15
+ const dirPath = path.join(dir, file);
16
+ destination = path.join(this.root, dir, file);
17
+ await this.ensurePathExists({ dir: path.dirname(dirPath) });
18
+ }
19
+ await fs.writeFile(destination, JSON.stringify(content, null, 2));
20
+ }
21
+ async readFile({ file }: { file: string }) {
22
+ return await fs.readFile(path.join(this.root, file), "utf-8");
23
+ }
24
+ async readJSONFile({ file, dir }: { file: string; dir?: string }) {
25
+ try {
26
+ file = `${file}.json`;
27
+ return JSON.parse(await fs.readFile(path.join(this.root, dir || "", file), "utf-8")) as object;
28
+ } catch (err) {
29
+ console.error(err)
30
+ return null;
31
+ }
32
+ }
33
+ async ensurePathExists({ dir }: { dir: string }) {
34
+ dir = path.join(this.root, dir);
35
+ if (!(await fsExists(dir))) {
36
+ await fs.mkdir(dir, { recursive: true });
37
+ }
38
+ }
39
+ async avoidOverriding({ file }: { file: string }) {
40
+ file = path.join(this.root, file);
41
+ if (await fsExists(file)) {
42
+ throw new Error(`File ${file} already exists`);
43
+ }
44
+ }
45
+ async removeFile({ file }: { file: string }) {
46
+ file = path.join(this.root, file);
47
+ if (await fsExists(file)) {
48
+ await fs.unlink(file);
49
+ }
50
+ }
51
+ async removeDirectory({ dir, target }: { dir: string; target: string }) {
52
+ dir = path.join(this.root, dir, target);
53
+ if (await fsExists(dir)) {
54
+ await fs.rm(dir, { recursive: true });
55
+ }
56
+ }
57
+
58
+ async copyDirectory({ from, to, dir }: { from: string; to: string; dir: string }) {
59
+ from = path.join(this.root, dir, from);
60
+ to = path.join(this.root, dir, to);
61
+ if (await fsExists(from)) {
62
+ await fs.cp(from, to, { recursive: true });
63
+ }
64
+ }
65
+
66
+ async listDirectories({ dir }: { dir: string }) {
67
+ try {
68
+ return await fs
69
+ .readdir(path.join(this.root, dir), { withFileTypes: true })
70
+ .then((files) => files.filter((file) => file.isDirectory()))
71
+ .then((files) => files.map((file) => file.name));
72
+ } catch (error) {
73
+ console.warn(error);
74
+ return [];
75
+ }
76
+ }
77
+ async listFiles({ dir }: { dir: string }) {
78
+ try {
79
+ return await fs
80
+ .readdir(path.join(this.root, dir), { withFileTypes: true })
81
+ .then((files) => files.filter((file) => !file.isDirectory()))
82
+ .then((files) => files.map((file) => file.name));
83
+ } catch (error) {
84
+ console.warn(error);
85
+ return [];
86
+ }
87
+ }
88
+ }
89
+
90
+ export const fsTool = new FStool();
@@ -0,0 +1,9 @@
1
+ import bcrypt from "bcryptjs";
2
+ export class HashingModule {
3
+ static async hash(password: string) {
4
+ return await bcrypt.hash(password, 10);
5
+ }
6
+ static async compare(password: string, hash: string) {
7
+ return await bcrypt.compare(password, hash);
8
+ }
9
+ }
@@ -0,0 +1,27 @@
1
+ // eslint-disable-next-line import-x/export
2
+ export type * from "./custom-type.js";
3
+ export * from "./utils.js";
4
+ export * from "./logger-module/logger.js";
5
+ export * from "./notification-module/push-notification.js";
6
+ export * from "./token-module.js";
7
+ export * from "./bucket-module/file-storage.js";
8
+ export * from "./bucket-module/s3-storage.js";
9
+ export * from "./bucket-module/utils.js";
10
+ // export * from "./compression-module/compression-module.js";
11
+
12
+ // export * from "./env.js";
13
+ export * from "./file-system/index.js";
14
+ export * from "./mail-module/mock.js";
15
+ export * from "./mail-module/nodemailer.js";
16
+
17
+ export * from "./cookie-module.js";
18
+ export * from "./hashing-module.js";
19
+
20
+ export * from "./oauth2-client.js";
21
+ export * from "./otp-module.js";
22
+ export * from "./pagination-module.js";
23
+ export * from "./error-handlers-module/index.js";
24
+ export * from "./validation-module.js";
25
+ export * from "./command-module.js";
26
+ export * from "./logger-module/memory-profiler.js";
27
+ export * from "./user-session.js";
@@ -0,0 +1,16 @@
1
+ export type LogConfig = {
2
+ routeInfo: boolean;
3
+ requestBody: boolean;
4
+ validationErrors: boolean;
5
+ queryParams: boolean;
6
+ responseBody: boolean;
7
+ steps: boolean;
8
+ };
9
+ export const logConfig: LogConfig = {
10
+ routeInfo: false,
11
+ responseBody: false,
12
+ validationErrors: false,
13
+ requestBody: false,
14
+ queryParams: false,
15
+ steps: false,
16
+ };
@@ -0,0 +1,78 @@
1
+ import { createLogger, format, transports } from "winston";
2
+
3
+ import { type LogConfig, logConfig } from "./log-config.js";
4
+
5
+ const { combine, timestamp, printf, errors, colorize } = format;
6
+
7
+ // Custom format to include error stack traces
8
+ const customFormat = printf(({ level, message, timestamp, stack }: any) => {
9
+ return `${timestamp} [${level}]: ${stack ?? message}`;
10
+ });
11
+
12
+ const logConfigFilter = format((info) => {
13
+ if (!info.logType || logConfig[info.logType as keyof LogConfig]) {
14
+ return info;
15
+ }
16
+ return false;
17
+ });
18
+
19
+ // Create level-specific filters
20
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
21
+ const levelFilter = (level: string) =>
22
+ format((info) => {
23
+ if (info.level === level) {
24
+ return info;
25
+ }
26
+ return false;
27
+ })();
28
+
29
+ // Configure the logger
30
+ export const logger = createLogger({
31
+ format: combine(
32
+ timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
33
+ errors({ stack: true }), // Capture stack traces
34
+ customFormat,
35
+ ),
36
+ transports: [
37
+ // Console transport - shows all levels
38
+ new transports.Console({
39
+ format: combine(logConfigFilter(), colorize(), customFormat),
40
+ }),
41
+
42
+ // Standard log file for all levels
43
+ // new DailyRotateFile({
44
+ // filename: path.join(__dirname(), "logs", "combined-%DATE%.log"),
45
+ // datePattern: "DD-MM-YYYY",
46
+ // maxFiles: "30d",
47
+ // format: combine(logConfigFilter(), customFormat),
48
+ // json: true,
49
+ // }),
50
+
51
+ // // File transport for 'info' logs only
52
+ // new DailyRotateFile({
53
+ // filename: path.join(__dirname(), "logs", "info-%DATE%.log"),
54
+ // datePattern: "DD-MM-YYYY",
55
+ // maxFiles: "30d",
56
+ // format: combine(logConfigFilter(), levelFilter("info"), customFormat),
57
+ // json: true,
58
+ // }),
59
+
60
+ // // File transport for 'error' logs only
61
+ // new DailyRotateFile({
62
+ // filename: path.join(__dirname(), "logs", "error-%DATE%.log"),
63
+ // datePattern: "DD-MM-YYYY",
64
+ // maxFiles: "30d",
65
+ // format: combine(logConfigFilter(), levelFilter("error"), customFormat),
66
+ // json: true,
67
+ // }),
68
+
69
+ // // File transport for 'warn' logs only
70
+ // new DailyRotateFile({
71
+ // filename: path.join(__dirname(), "logs", "warn-%DATE%.log"),
72
+ // datePattern: "DD-MM-YYYY",
73
+ // maxFiles: "30d",
74
+ // format: combine(logConfigFilter(), levelFilter("warn"), customFormat),
75
+ // json: true,
76
+ // }),
77
+ ],
78
+ });
@@ -0,0 +1,65 @@
1
+
2
+ export class MemoryProfiler {
3
+ private startUsage: number | null = null;
4
+ private lastUsage: number | null = null;
5
+ private totalDelta = 0;
6
+ private state: "off" | "on" = "on";
7
+ private startTime: number | null = null;
8
+ private lastTime: number | null = null;
9
+
10
+ setState(state: "on" | "off") {
11
+ this.state = state;
12
+ }
13
+
14
+ start(): void {
15
+ const usage = process.memoryUsage().heapUsed;
16
+ const now = Date.now();
17
+ this.startUsage = usage;
18
+ this.lastUsage = usage;
19
+ this.startTime = now;
20
+ this.lastTime = now;
21
+ this.totalDelta = 0;
22
+
23
+ if (this.state === "off") return;
24
+ console.log(`[Profiler] Started at ${this.formatMB(usage)}`);
25
+ }
26
+
27
+ at(label: string = ""): void {
28
+ if (this.startUsage === null || this.startTime === null) {
29
+ throw new Error("Profiler not started. Call .start() first.");
30
+ }
31
+
32
+ const usage = process.memoryUsage().heapUsed;
33
+ const now = Date.now();
34
+
35
+ const deltaFromStart = usage - this.startUsage;
36
+ const deltaFromLast = usage - (this.lastUsage ?? usage);
37
+
38
+ const timeFromStart = now - this.startTime;
39
+ const timeFromLast = now - (this.lastTime ?? now);
40
+
41
+ this.totalDelta = deltaFromStart;
42
+
43
+ if (this.state === "on") {
44
+ console.log(
45
+ `[Profiler] ${label ? label + " - " : ""}` +
46
+ `since start: ${this.formatMB(deltaFromStart)} | ${this.formatMS(timeFromStart)}, ` +
47
+ `since last: ${this.formatMB(deltaFromLast)} | ${this.formatMS(timeFromLast)}, ` +
48
+ `current heap: ${this.formatMB(usage)}`
49
+ );
50
+ }
51
+
52
+ this.lastUsage = usage;
53
+ this.lastTime = now;
54
+ }
55
+
56
+ private formatMB(bytes: number): string {
57
+ return (bytes / 1024 / 1024).toFixed(2) + " MB";
58
+ }
59
+
60
+ private formatMS(ms: number): string {
61
+ if (ms < 1000) return `${ms} ms`;
62
+ const sec = (ms / 1000).toFixed(2);
63
+ return `${sec} s`;
64
+ }
65
+ }
File without changes
@@ -0,0 +1,8 @@
1
+ import { logger } from "../logger-module/logger";
2
+
3
+ class MockMailService {
4
+ static sendMail({ text, to }: { to: string; text: string }) {
5
+ logger.info(`Email sent: ${to} ${text}`);
6
+ }
7
+ }
8
+ export { MockMailService };
@@ -0,0 +1,45 @@
1
+ import type { Transporter } from "nodemailer";
2
+ import * as nodemailer from "nodemailer";
3
+
4
+ import { logger } from "../logger-module/logger";
5
+
6
+ class SMTPMailService {
7
+ private static transporter: Transporter;
8
+
9
+ // Initialize the transporter only once
10
+ private static initializeTransporter() {
11
+ if (!this.transporter) {
12
+ this.transporter = nodemailer.createTransport({
13
+ host: process.env.SMTP_HOST,
14
+ port: Number.parseInt(process.env.SMTP_PORT) ?? 587,
15
+ secure: process.env.SMTP_MAIL_ENCRYPTION === "ssl",
16
+ auth: {
17
+ user: process.env.SMTP_USER,
18
+ pass: process.env.SMTP_PASSWORD,
19
+ },
20
+ });
21
+ }
22
+ }
23
+
24
+ static async sendMail(to: string, subject: string, text: string, html?: string): Promise<void> {
25
+ this.initializeTransporter();
26
+
27
+ const mailOptions = {
28
+ from: `Bemyguest <${process.env.SENDER_MAIL}>`,
29
+ to,
30
+ subject,
31
+ text,
32
+ html,
33
+ };
34
+
35
+ try {
36
+ const info = (await this.transporter.sendMail(mailOptions)) as { messageId: string };
37
+ logger.info(`Email sent: ${info.messageId}`);
38
+ } catch (error) {
39
+ logger.error("Error sending email:", error);
40
+ throw new Error("Failed to send email");
41
+ }
42
+ }
43
+ }
44
+
45
+ export { SMTPMailService };