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.
- package/.editorconfig +20 -0
- package/.prettierignore +24 -0
- package/.prettierrc.js +52 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/apps/core-server/.env.example +24 -0
- package/apps/core-server/Dockerfile +61 -0
- package/apps/core-server/eslint.config.mjs +47 -0
- package/apps/core-server/package.json +73 -0
- package/apps/core-server/src/config/custom-type.ts +54 -0
- package/apps/core-server/src/events/index.ts +37 -0
- package/apps/core-server/src/index.ts +19 -0
- package/apps/core-server/src/middlewares/auth-middleware.ts +50 -0
- package/apps/core-server/src/middlewares/cors-middleware.ts +6 -0
- package/apps/core-server/src/middlewares/error-middleware.ts +23 -0
- package/apps/core-server/src/middlewares/logger-middleware.ts +21 -0
- package/apps/core-server/src/middlewares/notfound-middleware.ts +14 -0
- package/apps/core-server/src/middlewares/serve-static.ts +24 -0
- package/apps/core-server/src/routes/files/router.ts +7 -0
- package/apps/core-server/src/routes/server.ts +36 -0
- package/apps/core-server/tsconfig.json +10 -0
- package/apps/core-server/tsdown.config.ts +14 -0
- package/biome.json +62 -0
- package/configs/eslint-config/package.json +60 -0
- package/configs/eslint-config/plugins.d.ts +1 -0
- package/configs/eslint-config/src/configs/base.ts +237 -0
- package/configs/eslint-config/src/configs/frontend.ts +62 -0
- package/configs/eslint-config/src/configs/node.ts +10 -0
- package/configs/eslint-config/src/plugin.ts +25 -0
- package/configs/eslint-config/src/rules/index.ts +30 -0
- package/configs/eslint-config/src/rules/no-argument-spread.test.ts +47 -0
- package/configs/eslint-config/src/rules/no-argument-spread.ts +96 -0
- package/configs/eslint-config/src/rules/no-dynamic-import-template.ts +32 -0
- package/configs/eslint-config/src/rules/no-internal-package-import.ts +40 -0
- package/configs/eslint-config/src/rules/no-interpolation-in-regular-string.ts +32 -0
- package/configs/eslint-config/src/rules/no-json-parse-json-stringify.test.ts +34 -0
- package/configs/eslint-config/src/rules/no-json-parse-json-stringify.ts +49 -0
- package/configs/eslint-config/src/rules/no-plain-errors.ts +50 -0
- package/configs/eslint-config/src/rules/no-skipped-tests.ts +61 -0
- package/configs/eslint-config/src/rules/no-top-level-relative-imports-in-backend-module.ts +27 -0
- package/configs/eslint-config/src/rules/no-type-unsafe-event-emitter.ts +33 -0
- package/configs/eslint-config/src/rules/no-uncaught-json-parse.test.ts +21 -0
- package/configs/eslint-config/src/rules/no-uncaught-json-parse.ts +45 -0
- package/configs/eslint-config/src/rules/no-untyped-config-class-field.ts +26 -0
- package/configs/eslint-config/src/rules/no-unused-param-catch-clause.ts +33 -0
- package/configs/eslint-config/src/rules/no-useless-catch-throw.test.ts +34 -0
- package/configs/eslint-config/src/rules/no-useless-catch-throw.ts +47 -0
- package/configs/eslint-config/src/utils/json.ts +21 -0
- package/configs/eslint-config/tsconfig.json +8 -0
- package/configs/eslint-config/tsdown.config.ts +11 -0
- package/configs/eslint-config/vitest.config.ts +3 -0
- package/configs/tsdown-config/package.json +14 -0
- package/configs/tsdown-config/src/config/tsdown.base.ts +13 -0
- package/configs/typescript-config/package.json +10 -0
- package/configs/typescript-config/tsconfig.common.json +32 -0
- package/configs/typescript-config/tsconfig.frontend.json +14 -0
- package/configs/typescript-config/tsconfig.node.json +9 -0
- package/configs/vitest-config/package.json +25 -0
- package/configs/vitest-config/src/base.ts +34 -0
- package/configs/vitest-config/src/frontend.ts +15 -0
- package/configs/vitest-config/src/node.ts +5 -0
- package/configs/vitest-config/tsconfig.json +7 -0
- package/package.json +47 -0
- package/packages/@repo/constants/eslint.config.mjs +21 -0
- package/packages/@repo/constants/package.json +19 -0
- package/packages/@repo/constants/src/api.ts +1 -0
- package/packages/@repo/constants/src/index.ts +8 -0
- package/packages/@repo/constants/src/time.ts +23 -0
- package/packages/@repo/constants/tsconfig.json +7 -0
- package/packages/@repo/db/eslint.config.mjs +21 -0
- package/packages/@repo/db/package.json +30 -0
- package/packages/@repo/db/src/functions.ts +122 -0
- package/packages/@repo/db/src/index.ts +20 -0
- package/packages/@repo/db/src/schema/common.ts +49 -0
- package/packages/@repo/db/src/schema/index.ts +1 -0
- package/packages/@repo/db/tsconfig.json +13 -0
- package/packages/@repo/lib/eslint.config.mjs +49 -0
- package/packages/@repo/lib/package.json +57 -0
- package/packages/@repo/lib/src/bucket-module/file-storage.ts +49 -0
- package/packages/@repo/lib/src/bucket-module/s3-storage.ts +114 -0
- package/packages/@repo/lib/src/bucket-module/utils.ts +11 -0
- package/packages/@repo/lib/src/command-module.ts +77 -0
- package/packages/@repo/lib/src/constants.ts +3 -0
- package/packages/@repo/lib/src/cookie-module.ts +42 -0
- package/packages/@repo/lib/src/custom-type.ts +54 -0
- package/packages/@repo/lib/src/env.ts +13 -0
- package/packages/@repo/lib/src/error-handlers-module/index.ts +11 -0
- package/packages/@repo/lib/src/file-system/index.ts +90 -0
- package/packages/@repo/lib/src/hashing-module.ts +9 -0
- package/packages/@repo/lib/src/index.ts +27 -0
- package/packages/@repo/lib/src/logger-module/log-config.ts +16 -0
- package/packages/@repo/lib/src/logger-module/logger.ts +78 -0
- package/packages/@repo/lib/src/logger-module/memory-profiler.ts +65 -0
- package/packages/@repo/lib/src/mail-module/api.ts +0 -0
- package/packages/@repo/lib/src/mail-module/mock.ts +8 -0
- package/packages/@repo/lib/src/mail-module/nodemailer.ts +45 -0
- package/packages/@repo/lib/src/notification-module/index.ts +172 -0
- package/packages/@repo/lib/src/notification-module/push-notification.ts +90 -0
- package/packages/@repo/lib/src/oauth2-client.ts +109 -0
- package/packages/@repo/lib/src/otp-module.ts +98 -0
- package/packages/@repo/lib/src/pagination-module.ts +49 -0
- package/packages/@repo/lib/src/token-module.ts +35 -0
- package/packages/@repo/lib/src/user-session.ts +117 -0
- package/packages/@repo/lib/src/utils.ts +42 -0
- package/packages/@repo/lib/src/validation-module.ts +187 -0
- package/packages/@repo/lib/tsconfig.json +7 -0
- package/packages/@repo/mail/package.json +29 -0
- package/packages/@repo/mail/src/emails/admin/OtpEmail.tsx +168 -0
- package/packages/@repo/mail/src/index.ts +13 -0
- package/packages/@repo/mail/tsconfig.build.json +14 -0
- package/packages/@repo/mail/tsconfig.json +13 -0
- package/packages/@repo/mail/tsdown.config.ts +9 -0
- package/packages/@repo/redis/eslint.config.mjs +8 -0
- package/packages/@repo/redis/package.json +31 -0
- package/packages/@repo/redis/src/index.ts +2 -0
- package/packages/@repo/redis/src/lib/redis-client.ts +23 -0
- package/packages/@repo/redis/src/lib/redis-module.ts +3 -0
- package/packages/@repo/redis/tsconfig.json +12 -0
- package/packages/ui/components.json +17 -0
- package/packages/ui/eslint.config.mjs +18 -0
- package/packages/ui/package.json +67 -0
- package/packages/ui/postcss.config.mjs +9 -0
- package/packages/ui/src/components/custom/form-wrapper.tsx +551 -0
- package/packages/ui/src/components/custom/grid-component.tsx +23 -0
- package/packages/ui/src/components/custom/hover-tool.tsx +38 -0
- package/packages/ui/src/components/custom/image-picker.tsx +109 -0
- package/packages/ui/src/components/custom/no-content.tsx +37 -0
- package/packages/ui/src/components/custom/page-container.tsx +24 -0
- package/packages/ui/src/components/custom/page-section.tsx +59 -0
- package/packages/ui/src/components/custom/simple-popover.tsx +29 -0
- package/packages/ui/src/components/custom/switch-component.tsx +20 -0
- package/packages/ui/src/components/custom/theme-provider.tsx +74 -0
- package/packages/ui/src/components/custom/typography.tsx +111 -0
- package/packages/ui/src/components/extensions/carousel.tsx +392 -0
- package/packages/ui/src/components/hooks/event/use-click.tsx +39 -0
- package/packages/ui/src/components/hooks/time/useDebounce.tsx +21 -0
- package/packages/ui/src/components/hooks/time/useInterval.tsx +35 -0
- package/packages/ui/src/components/hooks/time/useTimeout.tsx +19 -0
- package/packages/ui/src/components/hooks/time/useTimer.tsx +51 -0
- package/packages/ui/src/components/hooks/use-media-query.tsx +19 -0
- package/packages/ui/src/components/hooks/use-persistent-storage.tsx +52 -0
- package/packages/ui/src/components/hooks/use-update-effect.tsx +13 -0
- package/packages/ui/src/components/hooks/use-window-dimension.tsx +30 -0
- package/packages/ui/src/components/lib/utils.ts +242 -0
- package/packages/ui/src/components/lucide.tsx +3 -0
- package/packages/ui/src/components/sonner.tsx +1 -0
- package/packages/ui/src/components/ui/alert-dialog.tsx +116 -0
- package/packages/ui/src/components/ui/avatar.tsx +53 -0
- package/packages/ui/src/components/ui/badge.tsx +46 -0
- package/packages/ui/src/components/ui/breadcrumb.tsx +109 -0
- package/packages/ui/src/components/ui/button.tsx +96 -0
- package/packages/ui/src/components/ui/card.tsx +92 -0
- package/packages/ui/src/components/ui/carousel.tsx +243 -0
- package/packages/ui/src/components/ui/checkbox.tsx +32 -0
- package/packages/ui/src/components/ui/command.tsx +155 -0
- package/packages/ui/src/components/ui/dialog.tsx +127 -0
- package/packages/ui/src/components/ui/dropdown-menu.tsx +226 -0
- package/packages/ui/src/components/ui/form.tsx +165 -0
- package/packages/ui/src/components/ui/input-otp.tsx +76 -0
- package/packages/ui/src/components/ui/input.tsx +21 -0
- package/packages/ui/src/components/ui/label.tsx +24 -0
- package/packages/ui/src/components/ui/multiple-select.tsx +510 -0
- package/packages/ui/src/components/ui/popover.tsx +42 -0
- package/packages/ui/src/components/ui/select.tsx +170 -0
- package/packages/ui/src/components/ui/separator.tsx +28 -0
- package/packages/ui/src/components/ui/sheet.tsx +130 -0
- package/packages/ui/src/components/ui/skeleton.tsx +13 -0
- package/packages/ui/src/components/ui/spinner.tsx +16 -0
- package/packages/ui/src/components/ui/switch.tsx +28 -0
- package/packages/ui/src/components/ui/table.tsx +116 -0
- package/packages/ui/src/components/ui/tabs.tsx +54 -0
- package/packages/ui/src/components/ui/textarea.tsx +18 -0
- package/packages/ui/src/components/ui/timeline.tsx +118 -0
- package/packages/ui/src/components/ui/tooltip.tsx +30 -0
- package/packages/ui/src/components/util/n-formattor.ts +22 -0
- package/packages/ui/src/components/util/storage.ts +37 -0
- package/packages/ui/src/globals.css +87 -0
- package/packages/ui/tailwind.config.ts +94 -0
- package/packages/ui/tsconfig.json +12 -0
- package/pnpm-workspace.yaml +43 -0
- package/turbo.json +77 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export function __dirname() {
|
|
5
|
+
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
|
6
|
+
return path.resolve(process.cwd(), "../../");
|
|
7
|
+
}
|
|
8
|
+
return process.cwd();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @description Utility class for generating random strings and numbers
|
|
13
|
+
*/
|
|
14
|
+
export class Random {
|
|
15
|
+
/**
|
|
16
|
+
* @description Generate a random UUID
|
|
17
|
+
*/
|
|
18
|
+
static generateUUID() {
|
|
19
|
+
return crypto.randomUUID();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @description Generate a random string
|
|
24
|
+
* @param length
|
|
25
|
+
* @param encoding (default: 'hex')
|
|
26
|
+
*/
|
|
27
|
+
static generateString(length: number, encoding: BufferEncoding = "hex") {
|
|
28
|
+
return crypto.randomBytes(length).toString(encoding);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @description Generate a random number
|
|
33
|
+
* @param digits (default: 6)
|
|
34
|
+
*/
|
|
35
|
+
static generateNumber(digits: number = 6) {
|
|
36
|
+
return crypto.randomInt(10 ** (digits - 1), 10 ** digits);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static generateBoolean() {
|
|
40
|
+
return crypto.randomInt(0, 2) === 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import vine from "@vinejs/vine";
|
|
2
|
+
import type { Infer, SchemaTypes } from "@vinejs/vine/types";
|
|
3
|
+
import type { NextFunction, Request, Response } from "express";
|
|
4
|
+
|
|
5
|
+
import { logger } from "./logger-module/logger";
|
|
6
|
+
|
|
7
|
+
export async function validateBody<T extends SchemaTypes>(
|
|
8
|
+
schema: T,
|
|
9
|
+
validate: any,
|
|
10
|
+
): Promise<{
|
|
11
|
+
data?: Infer<T>;
|
|
12
|
+
error: string[];
|
|
13
|
+
}> {
|
|
14
|
+
try {
|
|
15
|
+
const validator = vine.compile(schema as any);
|
|
16
|
+
const payload = (await validator.validate(validate)) as Infer<T>;
|
|
17
|
+
return { data: payload, error: [] };
|
|
18
|
+
} catch (error: any) {
|
|
19
|
+
return {
|
|
20
|
+
error: error.messages.map((e: any) => {
|
|
21
|
+
return e.message;
|
|
22
|
+
}) as string[],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function bodyValidator<T extends SchemaTypes>(schema: T) {
|
|
28
|
+
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
29
|
+
const originalMethod = descriptor.value;
|
|
30
|
+
|
|
31
|
+
descriptor.value = async function (
|
|
32
|
+
req: Request<unknown, unknown, Infer<T>>,
|
|
33
|
+
res: Response,
|
|
34
|
+
next: NextFunction,
|
|
35
|
+
) {
|
|
36
|
+
const { error, data } = await validateBody(schema, req.body);
|
|
37
|
+
logger.info(`Body: ${JSON.stringify(req.body, null, 2)} `, {
|
|
38
|
+
logType: "requestBody",
|
|
39
|
+
});
|
|
40
|
+
if (!data || error.length > 0) {
|
|
41
|
+
logger.error(error.join("\n"), { logType: "validationErrors" });
|
|
42
|
+
return res.status(422).json({ message: error.join("\n") });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
req.body = data; // Make req.body type-safe
|
|
46
|
+
return originalMethod.apply(this, [req, res, next]);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return descriptor;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function paramsValidator(schema: SchemaTypes) {
|
|
53
|
+
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
54
|
+
const originalMethod = descriptor.value;
|
|
55
|
+
|
|
56
|
+
descriptor.value = async function (req: Request, res: Response, next: NextFunction) {
|
|
57
|
+
const { error, data } = await validateBody(schema, req.params);
|
|
58
|
+
if (!data || error.length > 0) {
|
|
59
|
+
logger.error(error.join("\n"), { logType: "validationErrors" });
|
|
60
|
+
return res.status(422).json({ message: error.join("\n") });
|
|
61
|
+
}
|
|
62
|
+
req.params = data; // Make req.body type-safe
|
|
63
|
+
return originalMethod.apply(this, [req, res, next]);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return descriptor;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function queryValidator(schema: SchemaTypes) {
|
|
71
|
+
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
72
|
+
const originalMethod = descriptor.value;
|
|
73
|
+
|
|
74
|
+
descriptor.value = async function (req: Request, res: Response, next: NextFunction) {
|
|
75
|
+
const { error, data } = await validateBody(schema, req.query);
|
|
76
|
+
if (!data || error.length > 0) {
|
|
77
|
+
logger.error(error.join("\n"), { logType: "validationErrors" });
|
|
78
|
+
return res.status(422).json({ message: error.join("\n") });
|
|
79
|
+
}
|
|
80
|
+
req.query = data; // Make req.body type-safe
|
|
81
|
+
return originalMethod.apply(this, [req, res, next]);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return descriptor;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// export function authValidator({ optional = false }: { optional?: boolean } | undefined = {}) {
|
|
89
|
+
// return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
90
|
+
// const originalMethod = descriptor.value;
|
|
91
|
+
// descriptor.value = async function (req: Request, res: Response, next: NextFunction) {
|
|
92
|
+
// try {
|
|
93
|
+
// const accessToken = req.headers["authorization"]?.split(" ")[1];
|
|
94
|
+
// if (optional && (!accessToken || !TokenModule.verifyAccessToken(accessToken))) {
|
|
95
|
+
// return originalMethod.apply(this, [req, res, next]);
|
|
96
|
+
// }
|
|
97
|
+
// if (!accessToken) {
|
|
98
|
+
// res.status(401).json({ message: "access token missing" });
|
|
99
|
+
// return;
|
|
100
|
+
// }
|
|
101
|
+
// const payload = TokenModule.verifyAccessToken(accessToken);
|
|
102
|
+
|
|
103
|
+
// if (!payload) {
|
|
104
|
+
// res.status(401).json({ message: "invalid access token" });
|
|
105
|
+
// return;
|
|
106
|
+
// }
|
|
107
|
+
// req.user = {
|
|
108
|
+
// id: payload.userID,
|
|
109
|
+
// email: payload.email
|
|
110
|
+
// };
|
|
111
|
+
// return originalMethod.apply(this, [req, res, next]);
|
|
112
|
+
// } catch (error) {
|
|
113
|
+
// next(error);
|
|
114
|
+
// }
|
|
115
|
+
// };
|
|
116
|
+
// };
|
|
117
|
+
// }
|
|
118
|
+
|
|
119
|
+
export function mediaBodyValidator(schema: SchemaTypes, optional = false) {
|
|
120
|
+
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
121
|
+
const originalMethod = descriptor.value;
|
|
122
|
+
|
|
123
|
+
descriptor.value = async function (req: Request, res: Response, next: NextFunction) {
|
|
124
|
+
const files = req.files;
|
|
125
|
+
if (!files && !optional) {
|
|
126
|
+
// throw new ErrorResponse("Add at least one file", 422);
|
|
127
|
+
logger.error("Add at least one file", { logType: "validationErrors" });
|
|
128
|
+
return res.status(422).json({ message: "Add at least one file" });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const isJSON = (str: any) => {
|
|
132
|
+
try {
|
|
133
|
+
const value = JSON.parse(str as string);
|
|
134
|
+
|
|
135
|
+
if (typeof value === "object" || value === null || typeof value === "boolean") {
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
return str;
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.log(e)
|
|
141
|
+
return str;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const parseData = Object.entries(req.body as object).map(([key, value]) => {
|
|
146
|
+
return [key, isJSON(value)];
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const parsedData = Object.fromEntries(parseData);
|
|
150
|
+
logger.info(`Body: ${JSON.stringify(parsedData, null, 2)} `, {
|
|
151
|
+
logType: "requestBody",
|
|
152
|
+
});
|
|
153
|
+
const { error, data } = await validateBody(schema, parsedData);
|
|
154
|
+
|
|
155
|
+
if (!data || error.length > 0) {
|
|
156
|
+
logger.error(error.join("\n"), { logType: "validationErrors" });
|
|
157
|
+
return res.status(422).json({ message: error.join("\n") });
|
|
158
|
+
}
|
|
159
|
+
req.body = data;
|
|
160
|
+
req.files = files;
|
|
161
|
+
return originalMethod.apply(this, [req, res, next]);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
return descriptor;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const validateId = vine.object({
|
|
169
|
+
id: vine.string().uuid(),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
export const paginationValidator = vine.object({
|
|
173
|
+
page: vine
|
|
174
|
+
.number()
|
|
175
|
+
.positive()
|
|
176
|
+
.parse((e: any) => (!e ? 1 : e))
|
|
177
|
+
.optional(),
|
|
178
|
+
limit: vine
|
|
179
|
+
.number()
|
|
180
|
+
.positive()
|
|
181
|
+
.parse((e: any) => (!e ? 10 : e))
|
|
182
|
+
.optional(),
|
|
183
|
+
query: vine
|
|
184
|
+
.string()
|
|
185
|
+
.parse((e: any) => (!e ? "" : e))
|
|
186
|
+
.optional(),
|
|
187
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/email",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "email dev --port 3014 --dir ./src"
|
|
8
|
+
},
|
|
9
|
+
"exports": "./src/index.ts",
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@react-email/preview-server": "catalog:",
|
|
15
|
+
"@types/react": "catalog:",
|
|
16
|
+
"@types/react-dom": "catalog:",
|
|
17
|
+
"react-email": "catalog:",
|
|
18
|
+
"tsdown-config": "workspace:*"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@react-email/components": "catalog:",
|
|
22
|
+
"@react-email/render": "catalog:",
|
|
23
|
+
"@react-email/tailwind": "catalog:",
|
|
24
|
+
"eslint-config": "workspace:*",
|
|
25
|
+
"react": "catalog:",
|
|
26
|
+
"react-dom": "catalog:",
|
|
27
|
+
"typescript-config": "workspace:*"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body,
|
|
3
|
+
Container,
|
|
4
|
+
Font,
|
|
5
|
+
Head,
|
|
6
|
+
Heading,
|
|
7
|
+
Hr,
|
|
8
|
+
Html,
|
|
9
|
+
Img,
|
|
10
|
+
Link,
|
|
11
|
+
Preview,
|
|
12
|
+
Section,
|
|
13
|
+
Text,
|
|
14
|
+
} from "@react-email/components";
|
|
15
|
+
|
|
16
|
+
export default function VerifyEmailOtp({
|
|
17
|
+
verificationCode = "596853",
|
|
18
|
+
}: {
|
|
19
|
+
verificationCode: string;
|
|
20
|
+
}) {
|
|
21
|
+
return (
|
|
22
|
+
<Html>
|
|
23
|
+
<Head>
|
|
24
|
+
<Font
|
|
25
|
+
fontFamily="Public Sans"
|
|
26
|
+
fallbackFontFamily="Helvetica"
|
|
27
|
+
webFont={{
|
|
28
|
+
url: "https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600&display=swap",
|
|
29
|
+
format: "woff2",
|
|
30
|
+
}}
|
|
31
|
+
fontWeight={400}
|
|
32
|
+
fontStyle="normal"
|
|
33
|
+
/>
|
|
34
|
+
</Head>{" "}
|
|
35
|
+
<Body style={main}>
|
|
36
|
+
<Preview>App verification</Preview>
|
|
37
|
+
<Container style={container}>
|
|
38
|
+
<Section style={coverSection}>
|
|
39
|
+
<Section style={{ paddingInline: "35px", paddingTop: "12px" }}>
|
|
40
|
+
<Img
|
|
41
|
+
src={`${"https://app.com"}/logo/app-logo.png`}
|
|
42
|
+
alt="App"
|
|
43
|
+
// width="64"
|
|
44
|
+
height="24px"
|
|
45
|
+
/>
|
|
46
|
+
</Section>
|
|
47
|
+
<Section style={upperSection}>
|
|
48
|
+
<Heading style={h1}>Verify your email address</Heading>
|
|
49
|
+
<Text style={mainText}>Your One-Time Password (OTP) for logging into App is:</Text>
|
|
50
|
+
<Section style={verificationSection}>
|
|
51
|
+
<Text style={verifyText}>Verification code</Text>
|
|
52
|
+
|
|
53
|
+
<Text style={codeText}>{verificationCode}</Text>
|
|
54
|
+
</Section>
|
|
55
|
+
</Section>
|
|
56
|
+
<Hr />
|
|
57
|
+
<Section style={lowerSection}>
|
|
58
|
+
<Text style={cautionText}>
|
|
59
|
+
This OTP is valid for 10 minutes. Please do not share this code with anyone for
|
|
60
|
+
security reasons. If you did not request this, please ignore this email or contact
|
|
61
|
+
our support team immediately.
|
|
62
|
+
</Text>
|
|
63
|
+
</Section>
|
|
64
|
+
</Section>
|
|
65
|
+
<Text style={footerText}>
|
|
66
|
+
This message was produced and distributed by App, Ltd.{" "}
|
|
67
|
+
<Link href="https://app.com" target="_blank" style={link}>
|
|
68
|
+
App.com
|
|
69
|
+
</Link>
|
|
70
|
+
<br />
|
|
71
|
+
View our{" "}
|
|
72
|
+
<Link href="https://app.com/privacy-policy" target="_blank" style={link}>
|
|
73
|
+
privacy policy
|
|
74
|
+
</Link>
|
|
75
|
+
.
|
|
76
|
+
</Text>
|
|
77
|
+
</Container>
|
|
78
|
+
</Body>
|
|
79
|
+
</Html>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const main = {
|
|
84
|
+
backgroundColor: "#fff",
|
|
85
|
+
color: "#212121",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const container = {
|
|
89
|
+
padding: "20px",
|
|
90
|
+
margin: "0 auto",
|
|
91
|
+
backgroundColor: "#eee",
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const h1 = {
|
|
95
|
+
color: "#333",
|
|
96
|
+
fontFamily:
|
|
97
|
+
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
|
98
|
+
fontSize: "20px",
|
|
99
|
+
fontWeight: "bold",
|
|
100
|
+
marginBottom: "15px",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const link = {
|
|
104
|
+
color: "#2754C5",
|
|
105
|
+
fontFamily:
|
|
106
|
+
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
|
107
|
+
fontSize: "14px",
|
|
108
|
+
textDecoration: "underline",
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const text = {
|
|
112
|
+
color: "#333",
|
|
113
|
+
fontFamily:
|
|
114
|
+
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
|
115
|
+
fontSize: "14px",
|
|
116
|
+
margin: "24px 0",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const imageSection = {
|
|
120
|
+
backgroundColor: "#fff",
|
|
121
|
+
display: "flex",
|
|
122
|
+
padding: "20px 0",
|
|
123
|
+
alignItems: "center",
|
|
124
|
+
justifyContent: "center",
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const coverSection = { backgroundColor: "#fff" };
|
|
128
|
+
|
|
129
|
+
const upperSection = { padding: "12px 35px" };
|
|
130
|
+
|
|
131
|
+
const lowerSection = { padding: "25px 35px" };
|
|
132
|
+
|
|
133
|
+
const footerText = {
|
|
134
|
+
...text,
|
|
135
|
+
fontSize: "12px",
|
|
136
|
+
padding: "0 20px",
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const verifyText = {
|
|
140
|
+
...text,
|
|
141
|
+
margin: 0,
|
|
142
|
+
fontWeight: "bold",
|
|
143
|
+
textAlign: "center" as const,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const codeText = {
|
|
147
|
+
...text,
|
|
148
|
+
fontWeight: "bold",
|
|
149
|
+
fontSize: "36px",
|
|
150
|
+
margin: "10px 0",
|
|
151
|
+
textAlign: "center" as const,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const validityText = {
|
|
155
|
+
...text,
|
|
156
|
+
margin: "0px",
|
|
157
|
+
textAlign: "center" as const,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const verificationSection = {
|
|
161
|
+
display: "flex",
|
|
162
|
+
alignItems: "center",
|
|
163
|
+
justifyContent: "center",
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const mainText = { ...text, marginBottom: "14px" };
|
|
167
|
+
|
|
168
|
+
const cautionText = { ...text, margin: "0px" };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { render } from "@react-email/render";
|
|
2
|
+
|
|
3
|
+
import VerifyEmailOtp from "./emails/admin/OtpEmail.js";
|
|
4
|
+
|
|
5
|
+
export class AdminEmailTemplate {
|
|
6
|
+
static getOtpEmail = async (props: { otp: string }) => {
|
|
7
|
+
return await render(
|
|
8
|
+
VerifyEmailOtp({
|
|
9
|
+
verificationCode: props.otp,
|
|
10
|
+
}),
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["./tsconfig.json", "typescript-config/tsconfig.build.json"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"composite": true,
|
|
5
|
+
"rootDir": ".",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"tsBuildInfoFile": "dist/build.tsbuildinfo",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"preserveSymlinks": false
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*"],
|
|
13
|
+
"exclude": ["src/**/__tests__/**"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "typescript-config/tsconfig.common.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": ".",
|
|
5
|
+
"types": ["node"],
|
|
6
|
+
"baseUrl": "src",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"tsBuildInfoFile": "dist/typecheck.tsbuildinfo",
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"emitDecoratorMetadata": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*.ts"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/redis",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"clean": "rimraf dist .turbo",
|
|
7
|
+
"watch:dev": "pnpm watch",
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"format": "biome format --write .",
|
|
10
|
+
"format:check": "biome ci .",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"lint:fix": "eslint . --fix",
|
|
13
|
+
"watch": "tsc -p tsconfig.build.json --watch"
|
|
14
|
+
},
|
|
15
|
+
"exports": "./src/index.ts",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"eslint-config": "workspace:*",
|
|
18
|
+
"typescript-config": "workspace:*",
|
|
19
|
+
"@types/eslint": "^8.56.5",
|
|
20
|
+
"@types/node": "^20.16.5",
|
|
21
|
+
"@types/pg": "^8.11.10",
|
|
22
|
+
"eslint": "catalog:",
|
|
23
|
+
"tsc-alias": "^1.8.10",
|
|
24
|
+
"typescript": "5.5.4"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"drizzle-orm": "^0.36.0",
|
|
28
|
+
"ioredis": "^5.4.1",
|
|
29
|
+
"pg": "^8.13.1"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Redis } from "ioredis";
|
|
2
|
+
|
|
3
|
+
export const redisClient = new Redis({
|
|
4
|
+
host: process.env.REDIS_HOST,
|
|
5
|
+
port: Number(process.env.REDIS_PORT),
|
|
6
|
+
username: process.env.REDIS_USERNAME,
|
|
7
|
+
password: process.env.REDIS_PASSWORD,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
function setEventListeners() {
|
|
11
|
+
redisClient.on("error", (error: string) => {
|
|
12
|
+
console.log("Could not establish a connection with redis. " + error);
|
|
13
|
+
});
|
|
14
|
+
redisClient.on("connect", (error: string) => {
|
|
15
|
+
if (error) {
|
|
16
|
+
console.log("Could not establish a connection with redis. " + error);
|
|
17
|
+
}
|
|
18
|
+
console.log("Connected to redis successfully");
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setEventListeners();
|
|
23
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "typescript-config/tsconfig.common.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": ".",
|
|
5
|
+
"types": ["node"],
|
|
6
|
+
"baseUrl": "src",
|
|
7
|
+
"tsBuildInfoFile": "dist/typecheck.tsbuildinfo",
|
|
8
|
+
"experimentalDecorators": true,
|
|
9
|
+
"emitDecoratorMetadata": true
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/**/*.ts"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "default",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "tailwind.config.ts",
|
|
8
|
+
"css": "src/globals.css",
|
|
9
|
+
"baseColor": "zinc",
|
|
10
|
+
"cssVariables": true
|
|
11
|
+
},
|
|
12
|
+
"aliases": {
|
|
13
|
+
"components": "@/components",
|
|
14
|
+
|
|
15
|
+
"utils": "@/lib/utils"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from "eslint/config";
|
|
2
|
+
import { frontendConfig } from "eslint-config/frontend";
|
|
3
|
+
|
|
4
|
+
export default defineConfig(frontendConfig, {
|
|
5
|
+
rules: {
|
|
6
|
+
"no-empty": "warn",
|
|
7
|
+
"@typescript-eslint/require-await": "warn",
|
|
8
|
+
"@typescript-eslint/no-empty-object-type": "warn",
|
|
9
|
+
"@typescript-eslint/naming-convention": "warn",
|
|
10
|
+
"@typescript-eslint/no-unsafe-function-type": "warn",
|
|
11
|
+
"@typescript-eslint/no-unsafe-call": "warn",
|
|
12
|
+
"@typescript-eslint/no-unsafe-member-access": "warn",
|
|
13
|
+
"@typescript-eslint/no-unsafe-return": "warn",
|
|
14
|
+
"@typescript-eslint/no-unsafe-assignment": "warn",
|
|
15
|
+
"@typescript-eslint/no-unused-vars": "warn",
|
|
16
|
+
"import-x/default": "off",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@repo/ui",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"clean": "rimraf dist .turbo",
|
|
7
|
+
"watch:dev": "pnpm watch",
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"format": "biome format --write .",
|
|
10
|
+
"format:check": "biome ci .",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"lint:fix": "eslint . --fix",
|
|
13
|
+
"watch": "tsc -p tsconfig.build.json --watch"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"react": "^19.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "catalog:",
|
|
20
|
+
"@types/react": "catalog:",
|
|
21
|
+
"autoprefixer": "^10",
|
|
22
|
+
"eslint-config": "workspace:*",
|
|
23
|
+
"postcss": "^8.4.49",
|
|
24
|
+
"postcss-load-config": "^6",
|
|
25
|
+
"tailwindcss": "catalog:",
|
|
26
|
+
"typescript": "catalog:",
|
|
27
|
+
"typescript-config": "workspace:*",
|
|
28
|
+
"vite": "catalog:"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@hookform/resolvers": "^3.9.1",
|
|
32
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
33
|
+
"@radix-ui/react-avatar": "^1.1.1",
|
|
34
|
+
"@radix-ui/react-checkbox": "^1.1.2",
|
|
35
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
36
|
+
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
|
37
|
+
"@radix-ui/react-label": "^2.1.0",
|
|
38
|
+
"@radix-ui/react-popover": "^1.1.2",
|
|
39
|
+
"@radix-ui/react-select": "^2.1.2",
|
|
40
|
+
"@radix-ui/react-separator": "^1.1.0",
|
|
41
|
+
"@radix-ui/react-slot": "^1.2.3",
|
|
42
|
+
"@radix-ui/react-switch": "^1.1.1",
|
|
43
|
+
"@radix-ui/react-tabs": "^1.1.1",
|
|
44
|
+
"@radix-ui/react-tooltip": "^1.1.3",
|
|
45
|
+
"@tailwindcss/postcss": "^4",
|
|
46
|
+
"class-variance-authority": "^0.7.0",
|
|
47
|
+
"clsx": "^2.1.1",
|
|
48
|
+
"cmdk": "^1.1.1",
|
|
49
|
+
"date-fns": "^4.1.0",
|
|
50
|
+
"embla-carousel": "^8.5.2",
|
|
51
|
+
"embla-carousel-react": "^8.5.2",
|
|
52
|
+
"input-otp": "^1.4.1",
|
|
53
|
+
"lucide-react": "catalog:",
|
|
54
|
+
"react-hook-form": "^7.53.1",
|
|
55
|
+
"react-icons": "^5.5.0",
|
|
56
|
+
"sonner": "^1.7.0",
|
|
57
|
+
"tailwind-merge": "^2.3.0",
|
|
58
|
+
"tailwindcss-animate": "^1.0.7",
|
|
59
|
+
"zod": "^3.23.8"
|
|
60
|
+
},
|
|
61
|
+
"exports": {
|
|
62
|
+
"./globals.css": "./src/globals.css",
|
|
63
|
+
"./postcss.config": "./postcss.config.mjs",
|
|
64
|
+
"./tailwind.config": "./tailwind.config.ts",
|
|
65
|
+
"./components/*": "./src/components/*"
|
|
66
|
+
}
|
|
67
|
+
}
|