create-zhx-monorepo 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 +34 -0
- package/bin/index.js +65 -0
- package/package.json +18 -0
- package/templates/monorepo-starter/.vscode/settings.json +3 -0
- package/templates/monorepo-starter/README.md +42 -0
- package/templates/monorepo-starter/apps/web/components.json +20 -0
- package/templates/monorepo-starter/apps/web/eslint.config.mjs +4 -0
- package/templates/monorepo-starter/apps/web/next-env.d.ts +6 -0
- package/templates/monorepo-starter/apps/web/next.config.mjs +6 -0
- package/templates/monorepo-starter/apps/web/package.json +31 -0
- package/templates/monorepo-starter/apps/web/postcss.config.mjs +1 -0
- package/templates/monorepo-starter/apps/web/public/.gitkeep +0 -0
- package/templates/monorepo-starter/apps/web/sitemap.config.cjs +6 -0
- package/templates/monorepo-starter/apps/web/src/app/(auth)/layout.tsx +7 -0
- package/templates/monorepo-starter/apps/web/src/app/(root)/layout.tsx +15 -0
- package/templates/monorepo-starter/apps/web/src/app/(root)/page.tsx +14 -0
- package/templates/monorepo-starter/apps/web/src/app/globals.css +1 -0
- package/templates/monorepo-starter/apps/web/src/app/layout.tsx +24 -0
- package/templates/monorepo-starter/apps/web/src/components/Footer.tsx +9 -0
- package/templates/monorepo-starter/apps/web/src/components/Header.tsx +11 -0
- package/templates/monorepo-starter/apps/web/src/components/ThemeSwitch.tsx +34 -0
- package/templates/monorepo-starter/apps/web/src/hooks/.gitkeep +0 -0
- package/templates/monorepo-starter/apps/web/src/lib/.gitkeep +0 -0
- package/templates/monorepo-starter/apps/web/src/providers/index.tsx +10 -0
- package/templates/monorepo-starter/apps/web/src/providers/theme.tsx +19 -0
- package/templates/monorepo-starter/apps/web/src/types/index.d.ts +16 -0
- package/templates/monorepo-starter/apps/web/tsconfig.json +20 -0
- package/templates/monorepo-starter/eslint.config.mjs +12 -0
- package/templates/monorepo-starter/package.json +23 -0
- package/templates/monorepo-starter/packages/config/eslint/base.js +31 -0
- package/templates/monorepo-starter/packages/config/eslint/nest.js +26 -0
- package/templates/monorepo-starter/packages/config/eslint/next.js +39 -0
- package/templates/monorepo-starter/packages/config/eslint/react.js +30 -0
- package/templates/monorepo-starter/packages/config/package.json +32 -0
- package/templates/monorepo-starter/packages/config/typescript/base.json +31 -0
- package/templates/monorepo-starter/packages/config/typescript/nest.json +14 -0
- package/templates/monorepo-starter/packages/config/typescript/next.json +10 -0
- package/templates/monorepo-starter/packages/config/typescript/react.json +9 -0
- package/templates/monorepo-starter/packages/ui/components.json +20 -0
- package/templates/monorepo-starter/packages/ui/eslint.config.mjs +4 -0
- package/templates/monorepo-starter/packages/ui/package.json +38 -0
- package/templates/monorepo-starter/packages/ui/postcss.config.mjs +6 -0
- package/templates/monorepo-starter/packages/ui/src/components/button.tsx +71 -0
- package/templates/monorepo-starter/packages/ui/src/hooks/.gitkeep +0 -0
- package/templates/monorepo-starter/packages/ui/src/lib/utils.ts +6 -0
- package/templates/monorepo-starter/packages/ui/src/styles/globals.css +182 -0
- package/templates/monorepo-starter/packages/ui/tsconfig.json +13 -0
- package/templates/monorepo-starter/packages/ui/tsconfig.lint.json +8 -0
- package/templates/monorepo-starter/pnpm-lock.yaml +12441 -0
- package/templates/monorepo-starter/pnpm-workspace.yaml +17 -0
- package/templates/monorepo-starter/server/.env.example +64 -0
- package/templates/monorepo-starter/server/README.md +63 -0
- package/templates/monorepo-starter/server/eslint.config.mjs +4 -0
- package/templates/monorepo-starter/server/nest-cli.json +12 -0
- package/templates/monorepo-starter/server/package.json +97 -0
- package/templates/monorepo-starter/server/prisma/generated/browser.ts +54 -0
- package/templates/monorepo-starter/server/prisma/generated/client.ts +76 -0
- package/templates/monorepo-starter/server/prisma/generated/commonInputTypes.ts +577 -0
- package/templates/monorepo-starter/server/prisma/generated/enums.ts +68 -0
- package/templates/monorepo-starter/server/prisma/generated/internal/class.ts +250 -0
- package/templates/monorepo-starter/server/prisma/generated/internal/prismaNamespace.ts +1436 -0
- package/templates/monorepo-starter/server/prisma/generated/internal/prismaNamespaceBrowser.ts +227 -0
- package/templates/monorepo-starter/server/prisma/generated/models/BackupCode.ts +1375 -0
- package/templates/monorepo-starter/server/prisma/generated/models/Notification.ts +1587 -0
- package/templates/monorepo-starter/server/prisma/generated/models/Otp.ts +1488 -0
- package/templates/monorepo-starter/server/prisma/generated/models/RefreshToken.ts +1515 -0
- package/templates/monorepo-starter/server/prisma/generated/models/RoleAssignment.ts +1385 -0
- package/templates/monorepo-starter/server/prisma/generated/models/SecuritySetting.ts +1422 -0
- package/templates/monorepo-starter/server/prisma/generated/models/User.ts +2498 -0
- package/templates/monorepo-starter/server/prisma/generated/models.ts +18 -0
- package/templates/monorepo-starter/server/prisma/migrations/20251218164821_init/migration.sql +210 -0
- package/templates/monorepo-starter/server/prisma/migrations/migration_lock.toml +3 -0
- package/templates/monorepo-starter/server/prisma/schema.prisma +193 -0
- package/templates/monorepo-starter/server/prisma.config.ts +13 -0
- package/templates/monorepo-starter/server/scripts/generate.sh +14 -0
- package/templates/monorepo-starter/server/src/app.module.ts +49 -0
- package/templates/monorepo-starter/server/src/lib/decorators/logger.decorator.ts +20 -0
- package/templates/monorepo-starter/server/src/lib/decorators/public.decorator.ts +4 -0
- package/templates/monorepo-starter/server/src/lib/decorators/roles.decorator.ts +5 -0
- package/templates/monorepo-starter/server/src/lib/dto/auth.dto.ts +64 -0
- package/templates/monorepo-starter/server/src/lib/dto/security-setting.dto.ts +21 -0
- package/templates/monorepo-starter/server/src/lib/filters/exceptions.filter.ts +62 -0
- package/templates/monorepo-starter/server/src/lib/guards/auth.guard.ts +104 -0
- package/templates/monorepo-starter/server/src/lib/interceptors/response.interceptor.ts +33 -0
- package/templates/monorepo-starter/server/src/lib/pipes/validation.pipe.ts +7 -0
- package/templates/monorepo-starter/server/src/lib/schemas/env.schema.ts +99 -0
- package/templates/monorepo-starter/server/src/lib/utils/cookie.util.ts +23 -0
- package/templates/monorepo-starter/server/src/lib/utils/general.util.ts +24 -0
- package/templates/monorepo-starter/server/src/main.ts +41 -0
- package/templates/monorepo-starter/server/src/modules/auth/auth.controller.ts +74 -0
- package/templates/monorepo-starter/server/src/modules/auth/auth.module.ts +16 -0
- package/templates/monorepo-starter/server/src/modules/auth/auth.service.ts +525 -0
- package/templates/monorepo-starter/server/src/modules/auth/oauth.controller.ts +58 -0
- package/templates/monorepo-starter/server/src/modules/auth/oauth.service.ts +165 -0
- package/templates/monorepo-starter/server/src/modules/auth/otp.service.ts +133 -0
- package/templates/monorepo-starter/server/src/modules/auth/role.service.ts +99 -0
- package/templates/monorepo-starter/server/src/modules/auth/security-setting.service.ts +102 -0
- package/templates/monorepo-starter/server/src/modules/env/env.module.ts +9 -0
- package/templates/monorepo-starter/server/src/modules/env/env.service.ts +22 -0
- package/templates/monorepo-starter/server/src/modules/logger/logger.module.ts +12 -0
- package/templates/monorepo-starter/server/src/modules/logger/logger.service.ts +37 -0
- package/templates/monorepo-starter/server/src/modules/logger/winston.config.ts +32 -0
- package/templates/monorepo-starter/server/src/modules/notification/notification.module.ts +11 -0
- package/templates/monorepo-starter/server/src/modules/notification/notification.service.ts +118 -0
- package/templates/monorepo-starter/server/src/modules/prisma/prisma.extension.ts +72 -0
- package/templates/monorepo-starter/server/src/modules/prisma/prisma.module.ts +9 -0
- package/templates/monorepo-starter/server/src/modules/prisma/prisma.service.ts +49 -0
- package/templates/monorepo-starter/server/src/modules/public/public.controller.ts +21 -0
- package/templates/monorepo-starter/server/src/modules/public/public.module.ts +9 -0
- package/templates/monorepo-starter/server/src/modules/public/public.service.ts +30 -0
- package/templates/monorepo-starter/server/src/modules/scheduler/cleanup.service.ts +33 -0
- package/templates/monorepo-starter/server/src/modules/scheduler/scheduler.module.ts +7 -0
- package/templates/monorepo-starter/server/src/modules/template/template.module.ts +8 -0
- package/templates/monorepo-starter/server/src/modules/template/template.service.ts +33 -0
- package/templates/monorepo-starter/server/src/modules/token/token.module.ts +11 -0
- package/templates/monorepo-starter/server/src/modules/token/token.service.ts +131 -0
- package/templates/monorepo-starter/server/src/types/express.d.ts +10 -0
- package/templates/monorepo-starter/server/src/types/index.d.ts +13 -0
- package/templates/monorepo-starter/server/templates/notification.templates.ts +243 -0
- package/templates/monorepo-starter/server/test/app.e2e-spec.ts +25 -0
- package/templates/monorepo-starter/server/test/jest-e2e.json +9 -0
- package/templates/monorepo-starter/server/tsconfig.json +23 -0
- package/templates/monorepo-starter/server/tsup.config.ts +14 -0
- package/templates/monorepo-starter/tsconfig.json +3 -0
- package/templates/monorepo-starter/turbo.json +21 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
|
2
|
+
import type { CanActivate, ExecutionContext } from "@nestjs/common";
|
|
3
|
+
import { Reflector } from "@nestjs/core";
|
|
4
|
+
import type { Request, Response } from "express";
|
|
5
|
+
import { IS_PUBLIC_KEY } from "@decorators/public.decorator";
|
|
6
|
+
import { ROLES_KEY } from "@decorators/roles.decorator";
|
|
7
|
+
import type { UserRole } from "@generated/prisma";
|
|
8
|
+
import { TokenService } from "@modules/token/token.service";
|
|
9
|
+
import { LoggerService } from "@modules/logger/logger.service";
|
|
10
|
+
import { InjectLogger } from "@decorators/logger.decorator";
|
|
11
|
+
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class AuthGuard implements CanActivate {
|
|
14
|
+
@InjectLogger()
|
|
15
|
+
private readonly logger!: LoggerService;
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly tokenService: TokenService,
|
|
19
|
+
private readonly reflector: Reflector
|
|
20
|
+
) {}
|
|
21
|
+
|
|
22
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
23
|
+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
|
24
|
+
context.getHandler(),
|
|
25
|
+
context.getClass(),
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
if (isPublic) return true;
|
|
29
|
+
|
|
30
|
+
const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>(
|
|
31
|
+
ROLES_KEY,
|
|
32
|
+
[context.getHandler(), context.getClass()]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const ctx = context.switchToHttp();
|
|
36
|
+
const req = ctx.getRequest<Request>();
|
|
37
|
+
const res = ctx.getResponse<Response>();
|
|
38
|
+
|
|
39
|
+
const accessToken = req.cookies["accessToken"];
|
|
40
|
+
const refreshToken = req.cookies["refreshToken"];
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
if (!accessToken) {
|
|
44
|
+
this.logger.warn(`Access token missing}`, {
|
|
45
|
+
route: req.url,
|
|
46
|
+
});
|
|
47
|
+
throw new Error("Access token is missing or expired");
|
|
48
|
+
}
|
|
49
|
+
const decoded = await this.tokenService.verifyToken(
|
|
50
|
+
accessToken,
|
|
51
|
+
"access"
|
|
52
|
+
);
|
|
53
|
+
if (!decoded) throw new UnauthorizedException("Invalid Access Token");
|
|
54
|
+
this.checkRoles(decoded.roles, requiredRoles);
|
|
55
|
+
this.tokenService.attachDecodedUser(req, decoded);
|
|
56
|
+
this.logger.debug(`✅ User Attached - Auth success`, {
|
|
57
|
+
userId: decoded.sub,
|
|
58
|
+
});
|
|
59
|
+
return true;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (!refreshToken)
|
|
62
|
+
throw new UnauthorizedException("Refresh token is missing");
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const decoded = await this.tokenService.verifyToken(
|
|
66
|
+
refreshToken,
|
|
67
|
+
"refresh"
|
|
68
|
+
);
|
|
69
|
+
if (!decoded) throw new Error("Invalid or expire Refresh Token");
|
|
70
|
+
this.logger.debug(`Refresh token verified`, {
|
|
71
|
+
userId: decoded.sub,
|
|
72
|
+
});
|
|
73
|
+
this.checkRoles(decoded.roles, requiredRoles);
|
|
74
|
+
await this.tokenService.refreshTokens(req, res, decoded);
|
|
75
|
+
|
|
76
|
+
if (!req["user"]) throw new UnauthorizedException("User not found");
|
|
77
|
+
|
|
78
|
+
this.logger.log(`✅ Auth success`, {
|
|
79
|
+
userId: req.user?.id,
|
|
80
|
+
method: req.method,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
this.logger.warn(`Access denied: Missing or invalid token`);
|
|
86
|
+
|
|
87
|
+
throw new UnauthorizedException(
|
|
88
|
+
"Unauthorized: Invalid or expired token."
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private checkRoles(userRoles: UserRole[], requiredRoles?: UserRole[]) {
|
|
95
|
+
if (requiredRoles?.length) {
|
|
96
|
+
const hasRole = userRoles.some((role) => requiredRoles.includes(role));
|
|
97
|
+
if (!hasRole) {
|
|
98
|
+
throw new UnauthorizedException(
|
|
99
|
+
`Forbidden: Requires ${requiredRoles.join(", ")} access.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
type NestInterceptor,
|
|
4
|
+
type ExecutionContext,
|
|
5
|
+
type CallHandler,
|
|
6
|
+
} from "@nestjs/common";
|
|
7
|
+
import { Observable, map } from "rxjs";
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class ResponseInterceptor implements NestInterceptor {
|
|
11
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
12
|
+
const response = context.switchToHttp().getResponse();
|
|
13
|
+
|
|
14
|
+
return next.handle().pipe(
|
|
15
|
+
map((res) => {
|
|
16
|
+
if (!res || typeof res !== "object" || res instanceof Response) {
|
|
17
|
+
return res;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const statusCode = response.statusCode;
|
|
21
|
+
const { data = null, message = "Success", ...meta } = res || {};
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
status: statusCode,
|
|
25
|
+
success: statusCode >= 200 && statusCode < 300,
|
|
26
|
+
message,
|
|
27
|
+
data,
|
|
28
|
+
...(Object.keys(meta).length ? { meta } : {}),
|
|
29
|
+
};
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import ms, { type StringValue } from "ms";
|
|
3
|
+
|
|
4
|
+
const zMsString = z
|
|
5
|
+
.string()
|
|
6
|
+
.transform((val) => val as StringValue)
|
|
7
|
+
.refine((val) => {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = ms(val);
|
|
10
|
+
return typeof parsed === "number" && parsed > 0;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}, "Invalid ms() duration string");
|
|
15
|
+
|
|
16
|
+
export const envSchema = z.object({
|
|
17
|
+
// ==============================
|
|
18
|
+
// App Config
|
|
19
|
+
// ==============================
|
|
20
|
+
NODE_ENV: z
|
|
21
|
+
.enum(["development", "production", "test"])
|
|
22
|
+
.default("development"),
|
|
23
|
+
APP_PORT: z.coerce.number(),
|
|
24
|
+
APP_ENDPOINT: z.string(),
|
|
25
|
+
CLIENT_ENDPOINT: z.string(),
|
|
26
|
+
ADMIN_ENDPOINT: z.string(),
|
|
27
|
+
CORS_ORIGIN: z
|
|
28
|
+
.string()
|
|
29
|
+
.transform((val) => val.split(",").map((origin) => origin.trim())),
|
|
30
|
+
|
|
31
|
+
// ==============================
|
|
32
|
+
// Database
|
|
33
|
+
// ==============================
|
|
34
|
+
DB_URI: z.string(),
|
|
35
|
+
|
|
36
|
+
// ==============================
|
|
37
|
+
// OTP Model
|
|
38
|
+
// ==============================
|
|
39
|
+
OTP_EXP: zMsString,
|
|
40
|
+
|
|
41
|
+
// ==============================
|
|
42
|
+
// Auth
|
|
43
|
+
// ==============================
|
|
44
|
+
JWT_ACCESS_SECRET: z.string(),
|
|
45
|
+
JWT_REFRESH_SECRET: z.string(),
|
|
46
|
+
ACCESS_TOKEN_EXP: zMsString,
|
|
47
|
+
REFRESH_TOKEN_EXP: zMsString,
|
|
48
|
+
|
|
49
|
+
// ==============================
|
|
50
|
+
// OAuth Providers
|
|
51
|
+
// ==============================
|
|
52
|
+
GOOGLE_CLIENT_ID: z.string(),
|
|
53
|
+
GOOGLE_CLIENT_SECRET: z.string(),
|
|
54
|
+
GOOGLE_CALLBACK_URL: z.string(),
|
|
55
|
+
|
|
56
|
+
FACEBOOK_CLIENT_ID: z.string(),
|
|
57
|
+
FACEBOOK_CLIENT_SECRET: z.string(),
|
|
58
|
+
FACEBOOK_CALLBACK_URL: z.string(),
|
|
59
|
+
|
|
60
|
+
// ==============================
|
|
61
|
+
// SMS (Twilio)
|
|
62
|
+
// ==============================
|
|
63
|
+
TWILIO_ACCOUNT_SID: z.string(),
|
|
64
|
+
TWILIO_AUTH_TOKEN: z.string(),
|
|
65
|
+
TWILIO_PHONE_NUMBER: z.string(),
|
|
66
|
+
|
|
67
|
+
// ==============================
|
|
68
|
+
// Email (Resend)
|
|
69
|
+
// ==============================
|
|
70
|
+
RESEND_API_KEY: z.string(),
|
|
71
|
+
|
|
72
|
+
// ==============================
|
|
73
|
+
// API Keys
|
|
74
|
+
// ==============================
|
|
75
|
+
IP_STACK_API_KEY: z.string(),
|
|
76
|
+
|
|
77
|
+
// ==============================
|
|
78
|
+
// Admin
|
|
79
|
+
// ==============================
|
|
80
|
+
ADMIN_EMAIL: z.string(),
|
|
81
|
+
ADMIN_PASSWORD: z.string(),
|
|
82
|
+
ADMIN_NAME: z.string(),
|
|
83
|
+
ADMIN_PHONE: z.string(),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export function validateEnv(config: Record<string, any>) {
|
|
87
|
+
const parsed = envSchema.safeParse(config);
|
|
88
|
+
if (!parsed.success) {
|
|
89
|
+
console.error(
|
|
90
|
+
"❌ Invalid environment variables:",
|
|
91
|
+
parsed.error.flatten().fieldErrors
|
|
92
|
+
);
|
|
93
|
+
throw new Error("Invalid environment variables");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return parsed.data;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type EnvSchema = z.infer<typeof envSchema>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { EnvService } from "@modules/env/env.service";
|
|
3
|
+
import type { Response, CookieOptions } from "express";
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class CookieService {
|
|
7
|
+
constructor(private readonly env: EnvService) {}
|
|
8
|
+
|
|
9
|
+
setCookie = (
|
|
10
|
+
res: Response,
|
|
11
|
+
key: string,
|
|
12
|
+
value: any,
|
|
13
|
+
options?: CookieOptions
|
|
14
|
+
) => {
|
|
15
|
+
res.cookie(key, value, {
|
|
16
|
+
httpOnly: true,
|
|
17
|
+
secure: this.env.get("NODE_ENV") === "production",
|
|
18
|
+
sameSite: "strict",
|
|
19
|
+
path: "/",
|
|
20
|
+
...options,
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import ms, { type StringValue } from "ms";
|
|
2
|
+
|
|
3
|
+
export const slugify = (str: string, slug?: string) => {
|
|
4
|
+
const base = slug && slug.trim().length > 0 ? slug : str;
|
|
5
|
+
|
|
6
|
+
return base
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/\s+/g, "-") // replace spaces with dashes
|
|
9
|
+
.replace(/[^\w\-]+/g, "") // remove invalid chars
|
|
10
|
+
.replace(/\-\-+/g, "-") // collapse multiple dashes
|
|
11
|
+
.replace(/^-+/, "") // trim starting dash
|
|
12
|
+
.replace(/-+$/, ""); // trim ending dash
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const parseExpiry = (exp: string, future = false): number => {
|
|
16
|
+
const val = ms(exp as StringValue);
|
|
17
|
+
if (future) return Date.now() + val;
|
|
18
|
+
return val;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const expiryDate = (exp: string, future = false): Date => {
|
|
22
|
+
const val = parseExpiry(exp, future);
|
|
23
|
+
return new Date(val);
|
|
24
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NestFactory } from "@nestjs/core";
|
|
2
|
+
import { AppModule } from "@/app.module";
|
|
3
|
+
import cookieParser from "cookie-parser";
|
|
4
|
+
import { GlobalValidationPipe } from "@pipes/validation.pipe";
|
|
5
|
+
import { AllExceptionsFilter } from "@filters/exceptions.filter";
|
|
6
|
+
import { ResponseInterceptor } from "@/lib/interceptors/response.interceptor";
|
|
7
|
+
import { EnvService } from "@modules/env/env.service";
|
|
8
|
+
import { LoggerService } from "@modules/logger/logger.service";
|
|
9
|
+
import { WinstonModule } from "nest-winston";
|
|
10
|
+
import { winstonConfig } from "@modules/logger/winston.config";
|
|
11
|
+
|
|
12
|
+
async function bootstrap() {
|
|
13
|
+
const app = await NestFactory.create(AppModule, {
|
|
14
|
+
logger: WinstonModule.createLogger(winstonConfig),
|
|
15
|
+
});
|
|
16
|
+
const env = app.get(EnvService);
|
|
17
|
+
const logger = await app.resolve(LoggerService);
|
|
18
|
+
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
const port = env.get("APP_PORT");
|
|
21
|
+
const endpoint = env.get("APP_ENDPOINT");
|
|
22
|
+
const nodeEnv = env.get("NODE_ENV");
|
|
23
|
+
|
|
24
|
+
app.use(cookieParser());
|
|
25
|
+
app.useGlobalInterceptors(app.get(ResponseInterceptor));
|
|
26
|
+
app.useGlobalFilters(app.get(AllExceptionsFilter));
|
|
27
|
+
app.useGlobalPipes(GlobalValidationPipe);
|
|
28
|
+
await app.listen(port);
|
|
29
|
+
|
|
30
|
+
logger.log("==========================================");
|
|
31
|
+
logger.log(`🚀 Server started successfully!`);
|
|
32
|
+
logger.log(`🌐 Endpoint: ${endpoint}`);
|
|
33
|
+
logger.log(`🔒 Environment: ${nodeEnv}`);
|
|
34
|
+
logger.log(`📦 Listening on port: ${port}`);
|
|
35
|
+
logger.log(`⏱️ Startup time: ${Date.now() - start}ms`);
|
|
36
|
+
logger.log(
|
|
37
|
+
`🪵 Logger initialized with Winston (see logs directory if enabled)`
|
|
38
|
+
);
|
|
39
|
+
logger.log("==========================================");
|
|
40
|
+
}
|
|
41
|
+
bootstrap();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Body, Controller, Get, Post, Query, Req, Res } from "@nestjs/common";
|
|
2
|
+
import { AuthService } from "./auth.service";
|
|
3
|
+
import {
|
|
4
|
+
ChangeIdentifierDto,
|
|
5
|
+
RequestOtpDto,
|
|
6
|
+
ResetPasswordDto,
|
|
7
|
+
SignInDto,
|
|
8
|
+
SignUpDto,
|
|
9
|
+
ValidateOtpDto,
|
|
10
|
+
} from "@dto/auth.dto";
|
|
11
|
+
import type { Request, Response } from "express";
|
|
12
|
+
import { Public } from "@decorators/public.decorator";
|
|
13
|
+
|
|
14
|
+
@Controller("auth")
|
|
15
|
+
export class AuthController {
|
|
16
|
+
constructor(private readonly authService: AuthService) {}
|
|
17
|
+
|
|
18
|
+
@Public()
|
|
19
|
+
@Post("signup")
|
|
20
|
+
async signUp(@Body() dto: SignUpDto) {
|
|
21
|
+
return this.authService.signUp(dto);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Public()
|
|
25
|
+
@Post("signin")
|
|
26
|
+
async signIn(
|
|
27
|
+
@Body() dto: SignInDto,
|
|
28
|
+
@Res({ passthrough: true }) res: Response,
|
|
29
|
+
@Req() req: Request
|
|
30
|
+
) {
|
|
31
|
+
return this.authService.signIn(dto, req, res);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Post("signout")
|
|
35
|
+
async signOut(
|
|
36
|
+
@Res({ passthrough: true }) res: Response,
|
|
37
|
+
@Req() req: Request
|
|
38
|
+
) {
|
|
39
|
+
return this.authService.signOut(req, res);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Public()
|
|
43
|
+
@Post("request-otp")
|
|
44
|
+
async requestOtp(@Body() dto: RequestOtpDto) {
|
|
45
|
+
return this.authService.requestOtp(dto);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Public()
|
|
49
|
+
@Get("validate-otp")
|
|
50
|
+
async validateOtp(
|
|
51
|
+
@Query() dto: ValidateOtpDto,
|
|
52
|
+
@Res({ passthrough: true }) res: Response,
|
|
53
|
+
@Req() req: Request
|
|
54
|
+
) {
|
|
55
|
+
return this.authService.validateOtp(dto, req, res);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Public()
|
|
59
|
+
@Post("reset-password")
|
|
60
|
+
async resetPassword(@Body() dto: ResetPasswordDto) {
|
|
61
|
+
return this.authService.resetPassword(dto);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Post("change-identifier")
|
|
65
|
+
async changeIdentifier(@Body() dto: ChangeIdentifierDto) {
|
|
66
|
+
return this.authService.changeIdentifierReq(dto);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Public()
|
|
70
|
+
@Get("change-identifier")
|
|
71
|
+
async verifyIdentifier(@Query() dto: ChangeIdentifierDto) {
|
|
72
|
+
return this.authService.changeIdentifier(dto);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common";
|
|
2
|
+
import { OtpService } from "./otp.service";
|
|
3
|
+
import { AuthService } from "./auth.service";
|
|
4
|
+
import { OAuthService } from "./oauth.service";
|
|
5
|
+
import { AuthController } from "./auth.controller";
|
|
6
|
+
import { OAuthController } from "./oauth.controller";
|
|
7
|
+
import { PassportModule } from "@nestjs/passport";
|
|
8
|
+
import { TokenModule } from "@modules/token/token.module";
|
|
9
|
+
|
|
10
|
+
@Module({
|
|
11
|
+
imports: [TokenModule, PassportModule],
|
|
12
|
+
controllers: [AuthController, OAuthController],
|
|
13
|
+
providers: [AuthService, OtpService, OAuthService],
|
|
14
|
+
exports: [AuthService],
|
|
15
|
+
})
|
|
16
|
+
export class AuthModule {}
|