@shadimakhoul/ggcoach 1.0.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.
Files changed (134) hide show
  1. package/config/config.ts +7 -0
  2. package/config/index.ts +2 -0
  3. package/config/initExpress.ts +27 -0
  4. package/config/redis.ts +38 -0
  5. package/config/swagger-ui.ts +84 -0
  6. package/config/swagger.ts +4417 -0
  7. package/dist/config/config.d.ts +8 -0
  8. package/dist/config/config.d.ts.map +1 -0
  9. package/dist/config/config.js +11 -0
  10. package/dist/config/config.js.map +1 -0
  11. package/dist/config/index.d.ts +3 -0
  12. package/dist/config/index.d.ts.map +1 -0
  13. package/dist/config/index.js +19 -0
  14. package/dist/config/index.js.map +1 -0
  15. package/dist/config/initExpress.d.ts +2 -0
  16. package/dist/config/initExpress.d.ts.map +1 -0
  17. package/dist/config/initExpress.js +29 -0
  18. package/dist/config/initExpress.js.map +1 -0
  19. package/dist/config/redis.d.ts +6 -0
  20. package/dist/config/redis.d.ts.map +1 -0
  21. package/dist/config/redis.js +34 -0
  22. package/dist/config/redis.js.map +1 -0
  23. package/dist/config/swagger-ui.d.ts +6 -0
  24. package/dist/config/swagger-ui.d.ts.map +1 -0
  25. package/dist/config/swagger-ui.js +88 -0
  26. package/dist/config/swagger-ui.js.map +1 -0
  27. package/dist/config/swagger.d.ts +9 -0
  28. package/dist/config/swagger.d.ts.map +1 -0
  29. package/dist/config/swagger.js +4406 -0
  30. package/dist/config/swagger.js.map +1 -0
  31. package/dist/index.d.ts +5 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +21 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/middleware/admin-auth.d.ts +4 -0
  36. package/dist/middleware/admin-auth.d.ts.map +1 -0
  37. package/dist/middleware/admin-auth.js +55 -0
  38. package/dist/middleware/admin-auth.js.map +1 -0
  39. package/dist/middleware/auth.d.ts +4 -0
  40. package/dist/middleware/auth.d.ts.map +1 -0
  41. package/dist/middleware/auth.js +86 -0
  42. package/dist/middleware/auth.js.map +1 -0
  43. package/dist/middleware/cors.d.ts +16 -0
  44. package/dist/middleware/cors.d.ts.map +1 -0
  45. package/dist/middleware/cors.js +45 -0
  46. package/dist/middleware/cors.js.map +1 -0
  47. package/dist/middleware/errorHandler.d.ts +3 -0
  48. package/dist/middleware/errorHandler.d.ts.map +1 -0
  49. package/dist/middleware/errorHandler.js +58 -0
  50. package/dist/middleware/errorHandler.js.map +1 -0
  51. package/dist/middleware/index.d.ts +9 -0
  52. package/dist/middleware/index.d.ts.map +1 -0
  53. package/dist/middleware/index.js +25 -0
  54. package/dist/middleware/index.js.map +1 -0
  55. package/dist/middleware/internalAuth.d.ts +4 -0
  56. package/dist/middleware/internalAuth.d.ts.map +1 -0
  57. package/dist/middleware/internalAuth.js +23 -0
  58. package/dist/middleware/internalAuth.js.map +1 -0
  59. package/dist/middleware/rateLimiter.d.ts +4 -0
  60. package/dist/middleware/rateLimiter.d.ts.map +1 -0
  61. package/dist/middleware/rateLimiter.js +24 -0
  62. package/dist/middleware/rateLimiter.js.map +1 -0
  63. package/dist/middleware/swagger.d.ts +16 -0
  64. package/dist/middleware/swagger.d.ts.map +1 -0
  65. package/dist/middleware/swagger.js +49 -0
  66. package/dist/middleware/swagger.js.map +1 -0
  67. package/dist/middleware/validation.d.ts +4 -0
  68. package/dist/middleware/validation.d.ts.map +1 -0
  69. package/dist/middleware/validation.js +24 -0
  70. package/dist/middleware/validation.js.map +1 -0
  71. package/dist/types/admin-auth.d.ts +44 -0
  72. package/dist/types/admin-auth.d.ts.map +1 -0
  73. package/dist/types/admin-auth.js +3 -0
  74. package/dist/types/admin-auth.js.map +1 -0
  75. package/dist/types/auth.d.ts +53 -0
  76. package/dist/types/auth.d.ts.map +1 -0
  77. package/dist/types/auth.js +3 -0
  78. package/dist/types/auth.js.map +1 -0
  79. package/dist/types/enums/auth.d.ts +7 -0
  80. package/dist/types/enums/auth.d.ts.map +1 -0
  81. package/dist/types/enums/auth.js +11 -0
  82. package/dist/types/enums/auth.js.map +1 -0
  83. package/dist/types/enums/index.d.ts +2 -0
  84. package/dist/types/enums/index.d.ts.map +1 -0
  85. package/dist/types/enums/index.js +18 -0
  86. package/dist/types/enums/index.js.map +1 -0
  87. package/dist/types/index.d.ts +4 -0
  88. package/dist/types/index.d.ts.map +1 -0
  89. package/dist/types/index.js +20 -0
  90. package/dist/types/index.js.map +1 -0
  91. package/dist/utils/index.d.ts +4 -0
  92. package/dist/utils/index.d.ts.map +1 -0
  93. package/dist/utils/index.js +20 -0
  94. package/dist/utils/index.js.map +1 -0
  95. package/dist/utils/jwt.d.ts +5 -0
  96. package/dist/utils/jwt.d.ts.map +1 -0
  97. package/dist/utils/jwt.js +67 -0
  98. package/dist/utils/jwt.js.map +1 -0
  99. package/dist/utils/logger.d.ts +6 -0
  100. package/dist/utils/logger.d.ts.map +1 -0
  101. package/dist/utils/logger.js +69 -0
  102. package/dist/utils/logger.js.map +1 -0
  103. package/dist/utils/openapi.d.ts +9 -0
  104. package/dist/utils/openapi.d.ts.map +1 -0
  105. package/dist/utils/openapi.js +90 -0
  106. package/dist/utils/openapi.js.map +1 -0
  107. package/dist/utils/validation.d.ts +12 -0
  108. package/dist/utils/validation.d.ts.map +1 -0
  109. package/dist/utils/validation.js +108 -0
  110. package/dist/utils/validation.js.map +1 -0
  111. package/index.ts +4 -0
  112. package/logs/combined.log +85182 -0
  113. package/logs/error.log +48814 -0
  114. package/middleware/admin-auth.ts +93 -0
  115. package/middleware/auth.ts +100 -0
  116. package/middleware/cors.ts +48 -0
  117. package/middleware/errorHandler.ts +62 -0
  118. package/middleware/index.ts +8 -0
  119. package/middleware/internalAuth.ts +25 -0
  120. package/middleware/rateLimiter.ts +18 -0
  121. package/middleware/swagger.ts +69 -0
  122. package/middleware/validation.ts +24 -0
  123. package/package.json +43 -0
  124. package/public/swagger.css +114 -0
  125. package/tsconfig.json +10 -0
  126. package/types/admin-auth.ts +47 -0
  127. package/types/auth.ts +66 -0
  128. package/types/enums/auth.ts +6 -0
  129. package/types/enums/index.ts +1 -0
  130. package/types/index.ts +3 -0
  131. package/utils/index.ts +3 -0
  132. package/utils/jwt.ts +66 -0
  133. package/utils/logger.ts +90 -0
  134. package/utils/openapi.ts +125 -0
@@ -0,0 +1,93 @@
1
+ import { AccountType, IApiResponse } from "../types";
2
+ import { Request, Response, NextFunction } from "express";
3
+
4
+ export const requireAdmin = (
5
+ req: Request,
6
+ res: Response,
7
+ next: NextFunction,
8
+ ) => {
9
+ try {
10
+ const user = req.user;
11
+
12
+ if (!user) {
13
+ return res.status(401).json({
14
+ success: false,
15
+ error: "Unauthorized: User not authenticated",
16
+ } as IApiResponse);
17
+ }
18
+
19
+ if (user.accountType !== AccountType.Admin && user.accountType !== AccountType.SuperAdmin) {
20
+ return res.status(403).json({
21
+ success: false,
22
+ error: "Forbidden: Admin access required",
23
+ } as IApiResponse);
24
+ }
25
+
26
+ return next();
27
+ } catch (error: any) {
28
+ return res.status(500).json({
29
+ success: false,
30
+ error: "Authorization check failed",
31
+ } as IApiResponse);
32
+ }
33
+ };
34
+
35
+ export const requireSuperAdmin = (
36
+ req: Request,
37
+ res: Response,
38
+ next: NextFunction,
39
+ ) => {
40
+ try {
41
+ const user = req.user;
42
+
43
+ if (!user) {
44
+ return res.status(401).json({
45
+ success: false,
46
+ error: "Unauthorized: User not authenticated",
47
+ } as IApiResponse);
48
+ }
49
+
50
+ if (user.accountType !== AccountType.SuperAdmin) {
51
+ return res.status(403).json({
52
+ success: false,
53
+ error: "Forbidden: Super admin access required",
54
+ } as IApiResponse);
55
+ }
56
+
57
+ return next();
58
+ } catch (error: any) {
59
+ return res.status(500).json({
60
+ success: false,
61
+ error: "Authorization check failed",
62
+ } as IApiResponse);
63
+ }
64
+ };
65
+
66
+ // export const requirePermission = (permission: string) => {
67
+ // return (req: Request, res: Response, next: NextFunction) => {
68
+ // try {
69
+ // const user = req.user;
70
+
71
+ // if (!user) {
72
+ // return res.status(401).json({
73
+ // success: false,
74
+ // error: "Unauthorized: User not authenticated",
75
+ // } as IApiResponse);
76
+ // }
77
+
78
+ // if (!user.permissions || !user.permissions.includes(permission)) {
79
+ // return res.status(403).json({
80
+ // success: false,
81
+ // error: `Forbidden: Permission '${permission}' required`,
82
+ // } as IApiResponse);
83
+ // }
84
+
85
+ // return next();
86
+ // } catch (error: any) {
87
+ // return res.status(500).json({
88
+ // success: false,
89
+ // error: "Permission check failed",
90
+ // } as IApiResponse);
91
+ // }
92
+ // };
93
+ // };
@@ -0,0 +1,100 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import jwt from "jsonwebtoken";
3
+
4
+ import { logger } from "../utils";
5
+ import { getRedisClient } from "../config";
6
+
7
+ import { JWTPayload } from "../types";
8
+
9
+ const JWT_SECRET =
10
+ process.env.JWT_SECRET || "your-super-secret-jwt-key-change-in-production";
11
+ const TOKEN_VERSION_PREFIX = "auth:token-version:";
12
+
13
+ export const authenticateToken = async (
14
+ req: Request,
15
+ res: Response,
16
+ next: NextFunction,
17
+ ): Promise<void> => {
18
+ const authHeader = req.headers["authorization"];
19
+ const token = authHeader && authHeader.split(" ")[1]; // Bearer TOKEN
20
+
21
+ if (!token) {
22
+ res.status(401).json({
23
+ success: false,
24
+ message: "Access token required",
25
+ });
26
+ return;
27
+ }
28
+
29
+ try {
30
+ const decoded = jwt.verify(token, JWT_SECRET, {
31
+ issuer: "ggcoach-auth-service",
32
+ audience: "ggcoach-app",
33
+ }) as JWTPayload;
34
+
35
+ const isValid = await validateTokenWithRedis(decoded);
36
+ if (!isValid) {
37
+ res.status(403).json({
38
+ success: false,
39
+ message: "Invalid or expired token",
40
+ });
41
+ return;
42
+ }
43
+
44
+ req.user = decoded;
45
+ next();
46
+ } catch (error) {
47
+ res.status(403).json({
48
+ success: false,
49
+ message: "Invalid or expired token",
50
+ });
51
+ }
52
+ };
53
+
54
+ const validateTokenWithRedis = async (decoded: JWTPayload): Promise<boolean> => {
55
+ try {
56
+ const client = getRedisClient();
57
+ const cacheKey = `${TOKEN_VERSION_PREFIX}${decoded.id}`;
58
+ const cached = await client.get(cacheKey);
59
+
60
+ if (cached === null) {
61
+ return false;
62
+ }
63
+
64
+ const cachedVersion = Number(cached);
65
+ const tokenVersion = decoded.tokenVersion ?? 0;
66
+ return Number.isFinite(cachedVersion) && cachedVersion === tokenVersion;
67
+ } catch (error) {
68
+ const err = error instanceof Error ? error : new Error(String(error));
69
+ logger.warn(`Token version cache read failed: ${err.message}`);
70
+ return false;
71
+ }
72
+ };
73
+
74
+ export const optionalAuth = async (
75
+ req: Request,
76
+ _res: Response,
77
+ next: NextFunction,
78
+ ): Promise<void> => {
79
+ const authHeader = req.headers["authorization"];
80
+ const token = authHeader && authHeader.split(" ")[1];
81
+
82
+ if (token) {
83
+ try {
84
+ const decoded = jwt.verify(token, JWT_SECRET, {
85
+ issuer: "ggcoach-auth-service",
86
+ audience: "ggcoach-app",
87
+ }) as JWTPayload;
88
+ const isValid = await validateTokenWithRedis(decoded);
89
+ if (isValid) {
90
+ req.user = decoded;
91
+ }
92
+ } catch (error) {
93
+ // Token is invalid, but we continue without user info
94
+ const err = error instanceof Error ? error : new Error(String(error));
95
+ logger.warn(`Invalid token in optional auth: ${err.message}`);
96
+ }
97
+ }
98
+
99
+ next();
100
+ };
@@ -0,0 +1,48 @@
1
+ import cors from "cors";
2
+
3
+ export const corsOptions = {
4
+ origin: (
5
+ origin: string | undefined,
6
+ callback: (err: Error | null, allow?: boolean) => void
7
+ ) => {
8
+ const defaultOrigins = [
9
+ "http://localhost:4000",
10
+ "http://localhost:4001",
11
+ "http://localhost:4002",
12
+ "http://localhost:4003",
13
+ "http://localhost:4004",
14
+ "http://localhost:4005",
15
+ "http://localhost:8080",
16
+ "http://localhost:3000",
17
+ "http://localhost:3001",
18
+ "https://gg.staging.brainexy.com",
19
+ "https://gg.register.brainexy.com",
20
+ ];
21
+ const envOrigins = process.env.ALLOWED_ORIGINS?.split(",").map(o => o.trim()) || [];
22
+ const allowedOrigins = Array.from(
23
+ new Set([...defaultOrigins, ...envOrigins].map(o => o.toLowerCase()))
24
+ );
25
+
26
+ // Allow no-origin requests (Postman, mobile apps)
27
+ if (!origin) return callback(null, true);
28
+ if (allowedOrigins.includes(origin.toLowerCase())) {
29
+ return callback(null, true);
30
+ }
31
+
32
+ return callback(new Error("Not allowed by CORS"));
33
+ },
34
+
35
+ credentials: true,
36
+ methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
37
+ allowedHeaders: [
38
+ "Content-Type",
39
+ "Authorization",
40
+ "X-Requested-With",
41
+ "X-Request-ID",
42
+ ],
43
+ exposedHeaders: ["X-Request-ID"], // optional but useful
44
+ preflightContinue: false,
45
+ optionsSuccessStatus: 200,
46
+ };
47
+
48
+ export const corsMiddleware = cors(corsOptions);
@@ -0,0 +1,62 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { logger } from '../utils';
3
+
4
+ export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction): void => {
5
+ logger.error(`Error: ${err}`);
6
+
7
+ // Handle specific error types
8
+ if (err.code === 'ECONNREFUSED') {
9
+ res.status(503).json({
10
+ success: false,
11
+ message: 'Service temporarily unavailable',
12
+ code: 'SERVICE_UNAVAILABLE'
13
+ });
14
+ return;
15
+ }
16
+
17
+ if (err.code === 'ETIMEDOUT') {
18
+ res.status(504).json({
19
+ success: false,
20
+ message: 'Gateway timeout',
21
+ code: 'GATEWAY_TIMEOUT'
22
+ });
23
+ return;
24
+ }
25
+
26
+ if (err.name === 'ValidationError') {
27
+ res.status(400).json({
28
+ success: false,
29
+ message: 'Validation error',
30
+ errors: err.details || [err.message]
31
+ });
32
+ return;
33
+ }
34
+
35
+ if (err.name === 'SequelizeValidationError') {
36
+ res.status(400).json({
37
+ success: false,
38
+ message: 'Validation error',
39
+ errors: err.errors.map((e: any) => e.message)
40
+ });
41
+ return;
42
+ }
43
+
44
+ if (err.name === 'SequelizeUniqueConstraintError') {
45
+ res.status(409).json({
46
+ success: false,
47
+ message: 'Resource already exists',
48
+ field: err.errors[0]?.path || 'unknown'
49
+ });
50
+ return;
51
+ }
52
+
53
+ res.status(err.status || 500).json({
54
+ success: false,
55
+ message: err.message || 'Internal server error',
56
+ code: err.code || 'INTERNAL_ERROR',
57
+ ...(process.env.NODE_ENV === 'development' && {
58
+ stack: err.stack,
59
+ requestId: req.headers['x-request-id']
60
+ })
61
+ });
62
+ };
@@ -0,0 +1,8 @@
1
+ export * from './auth';
2
+ export * from './validation';
3
+ export * from './errorHandler';
4
+ export * from './cors';
5
+ export * from './rateLimiter';
6
+ export * from './swagger';
7
+ export * from './internalAuth';
8
+ export * from './admin-auth';
@@ -0,0 +1,25 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ export const requireInternalToken = (req: Request, res: Response, next: NextFunction): void => {
4
+ if (process.env.ALLOW_DIRECT_ACCESS === 'true') {
5
+ next();
6
+ return;
7
+ }
8
+
9
+ const token = process.env.INTERNAL_SERVICE_TOKEN;
10
+ if (!token) {
11
+ // If token isn't set, reject to be safe
12
+ res.status(403).json({ success: false, message: 'Internal access token not configured' });
13
+ return;
14
+ }
15
+
16
+ const incoming = (req.headers['x-internal-token'] as string) || '';
17
+ if (incoming !== token) {
18
+ res.status(403).json({ success: false, message: 'Forbidden' });
19
+ return;
20
+ }
21
+
22
+ next();
23
+ };
24
+
25
+ export default requireInternalToken;
@@ -0,0 +1,18 @@
1
+ import rateLimit from 'express-rate-limit';
2
+
3
+ export const createRateLimiter = (windowMs: number = 15 * 60 * 1000, max: number = 1000) => {
4
+ return rateLimit({
5
+ windowMs,
6
+ max,
7
+ message: {
8
+ success: false,
9
+ message: 'Too many requests from this IP, please try again later.',
10
+ retryAfter: `${Math.ceil(windowMs / 60000)} minutes`
11
+ },
12
+ standardHeaders: true,
13
+ legacyHeaders: false,
14
+ });
15
+ };
16
+
17
+ export const authRateLimiter = createRateLimiter(15 * 60 * 1000, 5); // 5 attempts per 15 minutes for auth
18
+ export const generalRateLimiter = createRateLimiter(15 * 60 * 1000, 1000); // 1000 requests per 15 minutes
@@ -0,0 +1,69 @@
1
+ import { Express } from 'express';
2
+ import swaggerUi from 'swagger-ui-express';
3
+ import { swaggerUiOptions } from '../config/swagger-ui';
4
+ import { logger } from '../utils';
5
+
6
+ type OpenApiSpec = Record<string, unknown>;
7
+
8
+ type SwaggerUiUrl = {
9
+ name: string;
10
+ url: string;
11
+ };
12
+
13
+ type SwaggerSetupOptions = {
14
+ basePath?: string;
15
+ spec?: OpenApiSpec;
16
+ getSpec?: () => Promise<OpenApiSpec> | OpenApiSpec;
17
+ swaggerUiUrls?: SwaggerUiUrl[];
18
+ swaggerUiPrimaryName?: string;
19
+ };
20
+
21
+ const resolveSpec = async (options: SwaggerSetupOptions): Promise<OpenApiSpec> => {
22
+ if (options.getSpec) {
23
+ return await options.getSpec();
24
+ }
25
+
26
+ if (options.spec) {
27
+ return options.spec;
28
+ }
29
+
30
+ return {};
31
+ };
32
+
33
+ export const setupSwagger = (app: Express, options: SwaggerSetupOptions = {}) => {
34
+ const basePath = options.basePath ?? '/api-docs';
35
+
36
+ app.get(`${basePath}/swagger.json`, async (_req, res) => {
37
+ try {
38
+ const spec = await resolveSpec(options);
39
+ res.setHeader('Content-Type', 'application/json');
40
+ res.send(spec);
41
+ } catch (error) {
42
+ const err = error instanceof Error ? error : new Error(String(error));
43
+ logger.error(`Failed to build swagger spec: ${err.message}`);
44
+ res.status(500).json({ success: false, message: 'Failed to build swagger spec' });
45
+ }
46
+ });
47
+
48
+ app.use(
49
+ basePath,
50
+ swaggerUi.serve,
51
+ swaggerUi.setup(undefined, {
52
+ ...swaggerUiOptions,
53
+ swaggerOptions: {
54
+ ...(options.swaggerUiUrls && options.swaggerUiUrls.length > 0
55
+ ? {
56
+ urls: options.swaggerUiUrls,
57
+ ...(options.swaggerUiPrimaryName
58
+ ? { urlsPrimaryName: options.swaggerUiPrimaryName }
59
+ : {}),
60
+ }
61
+ : { url: `${basePath}/swagger.json` }),
62
+ },
63
+ })
64
+ );
65
+
66
+ logger.info(
67
+ `📚 Swagger documentation available at: http://localhost:${process.env.PORT || 4000}${basePath}`
68
+ );
69
+ };
@@ -0,0 +1,24 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import Joi from 'joi';
3
+
4
+ export const validateRequest = (schema: Joi.ObjectSchema) => {
5
+ return (req: Request, res: Response, next: NextFunction): void => {
6
+ const { error, value } = schema.validate(req.body, {
7
+ abortEarly: false,
8
+ stripUnknown: true
9
+ });
10
+
11
+ if (error) {
12
+ const errorMessages = error.details.map(detail => detail.message);
13
+ res.status(400).json({
14
+ success: false,
15
+ message: 'Validation error',
16
+ errors: errorMessages
17
+ });
18
+ return;
19
+ }
20
+
21
+ req.body = value;
22
+ next();
23
+ };
24
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@shadimakhoul/ggcoach",
3
+ "version": "1.0.0",
4
+ "description": "Shared utilities and types for GGCoach microservices",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "type": "commonjs",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "dev": "tsc --watch",
11
+ "test": "jest",
12
+ "lint": "eslint src/**/*.ts",
13
+ "lint:fix": "eslint src/**/*.ts --fix"
14
+ },
15
+ "dependencies": {
16
+ "express-list-endpoints": "^6.0.0",
17
+ "redis": "^4.6.10",
18
+ "helmet": "^7.1.0",
19
+ "express-rate-limit": "^7.1.5",
20
+ "winston": "^3.11.0",
21
+ "dotenv": "^16.3.1",
22
+ "cors": "^2.8.5",
23
+ "express": "^4.18.2",
24
+ "joi": "^17.11.0",
25
+ "jsonwebtoken": "^9.0.2",
26
+ "morgan": "^1.10.0",
27
+ "mysql2": "^3.15.3",
28
+ "swagger-jsdoc": "^6.2.8",
29
+ "swagger-ui-express": "^5.0.0",
30
+ "tsconfig-paths": "^4.2.0"
31
+ },
32
+ "keywords": [
33
+ "shared",
34
+ "utilities",
35
+ "types",
36
+ "microservices"
37
+ ],
38
+ "author": "Shadi Makhoul",
39
+ "license": "ISC",
40
+ "devDependencies": {
41
+ "@types/express-list-endpoints": "^6.0.0"
42
+ }
43
+ }
@@ -0,0 +1,114 @@
1
+ /* Hide top bar */
2
+ .swagger-ui .topbar {
3
+ display: none;
4
+ }
5
+
6
+ /* Global */
7
+ body,
8
+ .swagger-ui {
9
+ background-color: #020617;
10
+ color: #f1f5f9;
11
+ }
12
+
13
+ /* Main titles */
14
+ .swagger-ui .info .title {
15
+ color: #f8fafc;
16
+ font-weight: 700;
17
+ }
18
+
19
+ .swagger-ui h1,
20
+ .swagger-ui h2,
21
+ .swagger-ui h3,
22
+ .swagger-ui h4 {
23
+ color: #e5e7eb;
24
+ }
25
+
26
+ /* Description text */
27
+ .swagger-ui .info p,
28
+ .swagger-ui .info li,
29
+ .swagger-ui .markdown p,
30
+ .swagger-ui .markdown li {
31
+ color: #d1d5db;
32
+ }
33
+
34
+ /* Labels & small text */
35
+ .swagger-ui label,
36
+ .swagger-ui .parameter__name,
37
+ .swagger-ui .parameter__type,
38
+ .swagger-ui .parameter__deprecated,
39
+ .swagger-ui .response-col_status {
40
+ color: #e5e7eb;
41
+ }
42
+
43
+ /* Scheme / auth container */
44
+ .swagger-ui .scheme-container {
45
+ background: #020617;
46
+ padding: 10px;
47
+ border-radius: 6px;
48
+ border: 1px solid #1e293b;
49
+ }
50
+
51
+ /* Operation blocks */
52
+ .swagger-ui .opblock,
53
+ .swagger-ui .opblock-summary {
54
+ background: #020617;
55
+ border-color: #1e293b;
56
+ }
57
+
58
+ /* Tables */
59
+ .swagger-ui table {
60
+ color: #e5e7eb;
61
+ }
62
+
63
+ .swagger-ui table thead th {
64
+ color: #f8fafc;
65
+ border-bottom: 1px solid #334155;
66
+ }
67
+
68
+ .swagger-ui table tbody td {
69
+ color: #d1d5db;
70
+ border-bottom: 1px solid #1e293b;
71
+ }
72
+
73
+ /* Inputs */
74
+ .swagger-ui input,
75
+ .swagger-ui select,
76
+ .swagger-ui textarea {
77
+ background: #020617;
78
+ color: #f8fafc;
79
+ border: 1px solid #334155;
80
+ }
81
+
82
+ /* Placeholders */
83
+ .swagger-ui input::placeholder,
84
+ .swagger-ui textarea::placeholder {
85
+ color: #94a3b8;
86
+ }
87
+
88
+ /* Buttons */
89
+ .swagger-ui .btn {
90
+ background: #1e293b;
91
+ color: #f8fafc;
92
+ border: 1px solid #334155;
93
+ }
94
+
95
+ .swagger-ui .btn:hover {
96
+ background: #334155;
97
+ }
98
+
99
+ /* Code blocks */
100
+ .swagger-ui pre,
101
+ .swagger-ui code {
102
+ background: #020617;
103
+ color: #f8fafc;
104
+ border: 1px solid #1e293b;
105
+ }
106
+
107
+ /* Links */
108
+ .swagger-ui a {
109
+ color: #38bdf8;
110
+ }
111
+
112
+ .swagger-ui a:hover {
113
+ color: #7dd3fc;
114
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./",
6
+ "composite": false
7
+ },
8
+ "include": ["**/*.ts"],
9
+ "exclude": ["node_modules", "dist"]
10
+ }
@@ -0,0 +1,47 @@
1
+ export interface IAdminUser {
2
+ id: string;
3
+ email: string;
4
+ name: string;
5
+ role: string;
6
+ isActive: boolean;
7
+ permissions: string[];
8
+ }
9
+
10
+ export interface IPermission {
11
+ id: string;
12
+ name: string;
13
+ description: string;
14
+ module: string;
15
+ action: string;
16
+ }
17
+
18
+ export interface IAppRole {
19
+ id: string;
20
+ name: string;
21
+ description: string;
22
+ permissions: string[];
23
+ isActive: boolean;
24
+ usersCount?: number;
25
+ createdAt: Date;
26
+ updatedAt: Date;
27
+ }
28
+
29
+ export interface IPaginationQuery {
30
+ page: number;
31
+ limit: number;
32
+ sortBy?: string;
33
+ sortOrder?: "ASC" | "DESC";
34
+ search?: string;
35
+ }
36
+
37
+ export interface IApiResponse<T = any> {
38
+ success: boolean;
39
+ data?: T;
40
+ message?: string;
41
+ error?: string;
42
+ meta?: {
43
+ page?: number;
44
+ limit?: number;
45
+ total?: number;
46
+ };
47
+ }