servcraft 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.
Files changed (106) hide show
  1. package/.dockerignore +45 -0
  2. package/.env.example +46 -0
  3. package/.husky/commit-msg +1 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc +11 -0
  7. package/Dockerfile +76 -0
  8. package/Dockerfile.dev +31 -0
  9. package/README.md +232 -0
  10. package/commitlint.config.js +24 -0
  11. package/dist/cli/index.cjs +3968 -0
  12. package/dist/cli/index.cjs.map +1 -0
  13. package/dist/cli/index.d.cts +1 -0
  14. package/dist/cli/index.d.ts +1 -0
  15. package/dist/cli/index.js +3945 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/index.cjs +2458 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +828 -0
  20. package/dist/index.d.ts +828 -0
  21. package/dist/index.js +2332 -0
  22. package/dist/index.js.map +1 -0
  23. package/docker-compose.prod.yml +118 -0
  24. package/docker-compose.yml +147 -0
  25. package/eslint.config.js +27 -0
  26. package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
  27. package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
  28. package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
  29. package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
  30. package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +5 -0
  31. package/npm-cache/_update-notifier-last-checked +0 -0
  32. package/package.json +112 -0
  33. package/prisma/schema.prisma +157 -0
  34. package/src/cli/commands/add-module.ts +422 -0
  35. package/src/cli/commands/db.ts +137 -0
  36. package/src/cli/commands/docs.ts +16 -0
  37. package/src/cli/commands/generate.ts +459 -0
  38. package/src/cli/commands/init.ts +640 -0
  39. package/src/cli/index.ts +32 -0
  40. package/src/cli/templates/controller.ts +67 -0
  41. package/src/cli/templates/dynamic-prisma.ts +89 -0
  42. package/src/cli/templates/dynamic-schemas.ts +232 -0
  43. package/src/cli/templates/dynamic-types.ts +60 -0
  44. package/src/cli/templates/module-index.ts +33 -0
  45. package/src/cli/templates/prisma-model.ts +17 -0
  46. package/src/cli/templates/repository.ts +104 -0
  47. package/src/cli/templates/routes.ts +70 -0
  48. package/src/cli/templates/schemas.ts +26 -0
  49. package/src/cli/templates/service.ts +58 -0
  50. package/src/cli/templates/types.ts +27 -0
  51. package/src/cli/utils/docs-generator.ts +47 -0
  52. package/src/cli/utils/field-parser.ts +315 -0
  53. package/src/cli/utils/helpers.ts +89 -0
  54. package/src/config/env.ts +80 -0
  55. package/src/config/index.ts +97 -0
  56. package/src/core/index.ts +5 -0
  57. package/src/core/logger.ts +43 -0
  58. package/src/core/server.ts +132 -0
  59. package/src/database/index.ts +7 -0
  60. package/src/database/prisma.ts +54 -0
  61. package/src/database/seed.ts +59 -0
  62. package/src/index.ts +63 -0
  63. package/src/middleware/error-handler.ts +73 -0
  64. package/src/middleware/index.ts +3 -0
  65. package/src/middleware/security.ts +116 -0
  66. package/src/modules/audit/audit.service.ts +192 -0
  67. package/src/modules/audit/index.ts +2 -0
  68. package/src/modules/audit/types.ts +37 -0
  69. package/src/modules/auth/auth.controller.ts +182 -0
  70. package/src/modules/auth/auth.middleware.ts +87 -0
  71. package/src/modules/auth/auth.routes.ts +123 -0
  72. package/src/modules/auth/auth.service.ts +142 -0
  73. package/src/modules/auth/index.ts +49 -0
  74. package/src/modules/auth/schemas.ts +52 -0
  75. package/src/modules/auth/types.ts +69 -0
  76. package/src/modules/email/email.service.ts +212 -0
  77. package/src/modules/email/index.ts +10 -0
  78. package/src/modules/email/templates.ts +213 -0
  79. package/src/modules/email/types.ts +57 -0
  80. package/src/modules/swagger/index.ts +3 -0
  81. package/src/modules/swagger/schema-builder.ts +263 -0
  82. package/src/modules/swagger/swagger.service.ts +169 -0
  83. package/src/modules/swagger/types.ts +68 -0
  84. package/src/modules/user/index.ts +30 -0
  85. package/src/modules/user/schemas.ts +49 -0
  86. package/src/modules/user/types.ts +78 -0
  87. package/src/modules/user/user.controller.ts +139 -0
  88. package/src/modules/user/user.repository.ts +156 -0
  89. package/src/modules/user/user.routes.ts +199 -0
  90. package/src/modules/user/user.service.ts +145 -0
  91. package/src/modules/validation/index.ts +18 -0
  92. package/src/modules/validation/validator.ts +104 -0
  93. package/src/types/common.ts +61 -0
  94. package/src/types/index.ts +10 -0
  95. package/src/utils/errors.ts +66 -0
  96. package/src/utils/index.ts +33 -0
  97. package/src/utils/pagination.ts +38 -0
  98. package/src/utils/response.ts +63 -0
  99. package/tests/integration/auth.test.ts +59 -0
  100. package/tests/setup.ts +17 -0
  101. package/tests/unit/modules/validation.test.ts +88 -0
  102. package/tests/unit/utils/errors.test.ts +113 -0
  103. package/tests/unit/utils/pagination.test.ts +82 -0
  104. package/tsconfig.json +33 -0
  105. package/tsup.config.ts +14 -0
  106. package/vitest.config.ts +34 -0
@@ -0,0 +1,68 @@
1
+ export interface SwaggerConfig {
2
+ enabled?: boolean;
3
+ route?: string;
4
+ title: string;
5
+ description: string;
6
+ version: string;
7
+ basePath?: string;
8
+ tags?: SwaggerTag[];
9
+ servers?: SwaggerServer[];
10
+ contact?: {
11
+ name?: string;
12
+ email?: string;
13
+ url?: string;
14
+ };
15
+ license?: {
16
+ name: string;
17
+ url?: string;
18
+ };
19
+ }
20
+
21
+ export interface SwaggerTag {
22
+ name: string;
23
+ description?: string;
24
+ }
25
+
26
+ export interface SwaggerServer {
27
+ url: string;
28
+ description?: string;
29
+ }
30
+
31
+ export interface RouteSchema {
32
+ summary?: string;
33
+ description?: string;
34
+ tags?: string[];
35
+ security?: Array<Record<string, string[]>>;
36
+ params?: Record<string, unknown>;
37
+ querystring?: Record<string, unknown>;
38
+ body?: Record<string, unknown>;
39
+ response?: Record<number, unknown>;
40
+ }
41
+
42
+ export interface EndpointDoc {
43
+ method: string;
44
+ path: string;
45
+ summary: string;
46
+ description?: string;
47
+ tags: string[];
48
+ auth: boolean;
49
+ roles?: string[];
50
+ params?: FieldDoc[];
51
+ query?: FieldDoc[];
52
+ body?: FieldDoc[];
53
+ responses: ResponseDoc[];
54
+ }
55
+
56
+ export interface FieldDoc {
57
+ name: string;
58
+ type: string;
59
+ required: boolean;
60
+ description?: string;
61
+ example?: unknown;
62
+ }
63
+
64
+ export interface ResponseDoc {
65
+ status: number;
66
+ description: string;
67
+ schema?: Record<string, unknown>;
68
+ }
@@ -0,0 +1,30 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import { logger } from '../../core/logger.js';
3
+ import { UserService, createUserService } from './user.service.js';
4
+ import { UserController, createUserController } from './user.controller.js';
5
+ import { UserRepository, createUserRepository } from './user.repository.js';
6
+ import { registerUserRoutes } from './user.routes.js';
7
+ import type { AuthService } from '../auth/auth.service.js';
8
+
9
+ export async function registerUserModule(
10
+ app: FastifyInstance,
11
+ authService: AuthService
12
+ ): Promise<void> {
13
+ // Create repository and service
14
+ const repository = createUserRepository();
15
+ const userService = createUserService(repository);
16
+
17
+ // Create controller
18
+ const userController = createUserController(userService);
19
+
20
+ // Register routes
21
+ registerUserRoutes(app, userController, authService);
22
+
23
+ logger.info('User module registered');
24
+ }
25
+
26
+ export { UserService, createUserService } from './user.service.js';
27
+ export { UserController, createUserController } from './user.controller.js';
28
+ export { UserRepository, createUserRepository } from './user.repository.js';
29
+ export * from './types.js';
30
+ export * from './schemas.js';
@@ -0,0 +1,49 @@
1
+ import { z } from 'zod';
2
+
3
+ export const userStatusEnum = z.enum(['active', 'inactive', 'suspended', 'banned']);
4
+ export const userRoleEnum = z.enum(['user', 'admin', 'moderator', 'super_admin']);
5
+
6
+ export const createUserSchema = z.object({
7
+ email: z.string().email('Invalid email address'),
8
+ password: z
9
+ .string()
10
+ .min(8, 'Password must be at least 8 characters')
11
+ .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
12
+ .regex(/[a-z]/, 'Password must contain at least one lowercase letter')
13
+ .regex(/[0-9]/, 'Password must contain at least one number'),
14
+ name: z.string().min(2, 'Name must be at least 2 characters').optional(),
15
+ role: userRoleEnum.optional().default('user'),
16
+ });
17
+
18
+ export const updateUserSchema = z.object({
19
+ email: z.string().email('Invalid email address').optional(),
20
+ name: z.string().min(2, 'Name must be at least 2 characters').optional(),
21
+ role: userRoleEnum.optional(),
22
+ status: userStatusEnum.optional(),
23
+ emailVerified: z.boolean().optional(),
24
+ metadata: z.record(z.unknown()).optional(),
25
+ });
26
+
27
+ export const updateProfileSchema = z.object({
28
+ name: z.string().min(2, 'Name must be at least 2 characters').optional(),
29
+ metadata: z.record(z.unknown()).optional(),
30
+ });
31
+
32
+ export const userQuerySchema = z.object({
33
+ page: z.string().transform(Number).optional(),
34
+ limit: z.string().transform(Number).optional(),
35
+ sortBy: z.string().optional(),
36
+ sortOrder: z.enum(['asc', 'desc']).optional(),
37
+ status: userStatusEnum.optional(),
38
+ role: userRoleEnum.optional(),
39
+ search: z.string().optional(),
40
+ emailVerified: z
41
+ .string()
42
+ .transform((val) => val === 'true')
43
+ .optional(),
44
+ });
45
+
46
+ export type CreateUserInput = z.infer<typeof createUserSchema>;
47
+ export type UpdateUserInput = z.infer<typeof updateUserSchema>;
48
+ export type UpdateProfileInput = z.infer<typeof updateProfileSchema>;
49
+ export type UserQueryInput = z.infer<typeof userQuerySchema>;
@@ -0,0 +1,78 @@
1
+ import type { BaseEntity } from '../../types/index.js';
2
+
3
+ export type UserStatus = 'active' | 'inactive' | 'suspended' | 'banned';
4
+
5
+ export type UserRole = 'user' | 'admin' | 'moderator' | 'super_admin';
6
+
7
+ export interface User extends BaseEntity {
8
+ email: string;
9
+ password: string;
10
+ name?: string;
11
+ role: UserRole;
12
+ status: UserStatus;
13
+ emailVerified: boolean;
14
+ lastLoginAt?: Date;
15
+ metadata?: Record<string, unknown>;
16
+ }
17
+
18
+ export interface CreateUserData {
19
+ email: string;
20
+ password: string;
21
+ name?: string;
22
+ role?: UserRole;
23
+ }
24
+
25
+ export interface UpdateUserData {
26
+ email?: string;
27
+ name?: string;
28
+ role?: UserRole;
29
+ status?: UserStatus;
30
+ emailVerified?: boolean;
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+
34
+ export interface UserFilters {
35
+ status?: UserStatus;
36
+ role?: UserRole;
37
+ search?: string;
38
+ emailVerified?: boolean;
39
+ }
40
+
41
+ // RBAC Types
42
+ export interface Permission {
43
+ id: string;
44
+ name: string;
45
+ description?: string;
46
+ resource: string;
47
+ action: 'create' | 'read' | 'update' | 'delete' | 'manage';
48
+ }
49
+
50
+ export interface Role {
51
+ id: string;
52
+ name: UserRole;
53
+ description?: string;
54
+ permissions: Permission[];
55
+ }
56
+
57
+ // Default permissions mapping
58
+ export const DEFAULT_ROLE_PERMISSIONS: Record<UserRole, string[]> = {
59
+ user: ['profile:read', 'profile:update'],
60
+ moderator: [
61
+ 'profile:read',
62
+ 'profile:update',
63
+ 'users:read',
64
+ 'content:read',
65
+ 'content:update',
66
+ 'content:delete',
67
+ ],
68
+ admin: [
69
+ 'profile:read',
70
+ 'profile:update',
71
+ 'users:read',
72
+ 'users:update',
73
+ 'users:delete',
74
+ 'content:manage',
75
+ 'settings:read',
76
+ ],
77
+ super_admin: ['*:manage'], // All permissions
78
+ };
@@ -0,0 +1,139 @@
1
+ import type { FastifyRequest, FastifyReply } from 'fastify';
2
+ import type { UserService } from './user.service.js';
3
+ import { updateUserSchema, updateProfileSchema, userQuerySchema } from './schemas.js';
4
+ import { success, noContent } from '../../utils/response.js';
5
+ import { parsePaginationParams } from '../../utils/pagination.js';
6
+ import { validateBody, validateQuery } from '../validation/validator.js';
7
+ import type { AuthenticatedRequest } from '../auth/types.js';
8
+ import { ForbiddenError } from '../../utils/errors.js';
9
+
10
+ export class UserController {
11
+ constructor(private userService: UserService) {}
12
+
13
+ async list(request: FastifyRequest, reply: FastifyReply): Promise<void> {
14
+ const query = validateQuery(userQuerySchema, request.query);
15
+ const pagination = parsePaginationParams(query);
16
+
17
+ const filters = {
18
+ status: query.status,
19
+ role: query.role,
20
+ search: query.search,
21
+ emailVerified: query.emailVerified,
22
+ };
23
+
24
+ const result = await this.userService.findMany(pagination, filters);
25
+ success(reply, result);
26
+ }
27
+
28
+ async getById(
29
+ request: FastifyRequest<{ Params: { id: string } }>,
30
+ reply: FastifyReply
31
+ ): Promise<void> {
32
+ const user = await this.userService.findById(request.params.id);
33
+
34
+ if (!user) {
35
+ return reply.status(404).send({
36
+ success: false,
37
+ message: 'User not found',
38
+ });
39
+ }
40
+
41
+ // Remove password from response
42
+ const { password, ...userData } = user;
43
+ success(reply, userData);
44
+ }
45
+
46
+ async update(
47
+ request: FastifyRequest<{ Params: { id: string } }>,
48
+ reply: FastifyReply
49
+ ): Promise<void> {
50
+ const data = validateBody(updateUserSchema, request.body);
51
+ const user = await this.userService.update(request.params.id, data);
52
+
53
+ const { password, ...userData } = user;
54
+ success(reply, userData);
55
+ }
56
+
57
+ async delete(
58
+ request: FastifyRequest<{ Params: { id: string } }>,
59
+ reply: FastifyReply
60
+ ): Promise<void> {
61
+ const authRequest = request as AuthenticatedRequest;
62
+
63
+ // Prevent self-deletion
64
+ if (authRequest.user.id === request.params.id) {
65
+ throw new ForbiddenError('Cannot delete your own account');
66
+ }
67
+
68
+ await this.userService.delete(request.params.id);
69
+ noContent(reply);
70
+ }
71
+
72
+ async suspend(
73
+ request: FastifyRequest<{ Params: { id: string } }>,
74
+ reply: FastifyReply
75
+ ): Promise<void> {
76
+ const authRequest = request as AuthenticatedRequest;
77
+
78
+ if (authRequest.user.id === request.params.id) {
79
+ throw new ForbiddenError('Cannot suspend your own account');
80
+ }
81
+
82
+ const user = await this.userService.suspend(request.params.id);
83
+ const { password, ...userData } = user;
84
+ success(reply, userData);
85
+ }
86
+
87
+ async ban(
88
+ request: FastifyRequest<{ Params: { id: string } }>,
89
+ reply: FastifyReply
90
+ ): Promise<void> {
91
+ const authRequest = request as AuthenticatedRequest;
92
+
93
+ if (authRequest.user.id === request.params.id) {
94
+ throw new ForbiddenError('Cannot ban your own account');
95
+ }
96
+
97
+ const user = await this.userService.ban(request.params.id);
98
+ const { password, ...userData } = user;
99
+ success(reply, userData);
100
+ }
101
+
102
+ async activate(
103
+ request: FastifyRequest<{ Params: { id: string } }>,
104
+ reply: FastifyReply
105
+ ): Promise<void> {
106
+ const user = await this.userService.activate(request.params.id);
107
+ const { password, ...userData } = user;
108
+ success(reply, userData);
109
+ }
110
+
111
+ // Profile routes (for authenticated user)
112
+ async getProfile(request: FastifyRequest, reply: FastifyReply): Promise<void> {
113
+ const authRequest = request as AuthenticatedRequest;
114
+ const user = await this.userService.findById(authRequest.user.id);
115
+
116
+ if (!user) {
117
+ return reply.status(404).send({
118
+ success: false,
119
+ message: 'User not found',
120
+ });
121
+ }
122
+
123
+ const { password, ...userData } = user;
124
+ success(reply, userData);
125
+ }
126
+
127
+ async updateProfile(request: FastifyRequest, reply: FastifyReply): Promise<void> {
128
+ const authRequest = request as AuthenticatedRequest;
129
+ const data = validateBody(updateProfileSchema, request.body);
130
+
131
+ const user = await this.userService.update(authRequest.user.id, data);
132
+ const { password, ...userData } = user;
133
+ success(reply, userData);
134
+ }
135
+ }
136
+
137
+ export function createUserController(userService: UserService): UserController {
138
+ return new UserController(userService);
139
+ }
@@ -0,0 +1,156 @@
1
+ import { randomUUID } from 'crypto';
2
+ import type { PaginatedResult, PaginationParams } from '../../types/index.js';
3
+ import { createPaginatedResult, getSkip } from '../../utils/pagination.js';
4
+ import type { User, CreateUserData, UpdateUserData, UserFilters } from './types.js';
5
+
6
+ // In-memory storage for development (will be replaced by Prisma)
7
+ const users = new Map<string, User>();
8
+
9
+ export class UserRepository {
10
+ async findById(id: string): Promise<User | null> {
11
+ return users.get(id) || null;
12
+ }
13
+
14
+ async findByEmail(email: string): Promise<User | null> {
15
+ for (const user of users.values()) {
16
+ if (user.email.toLowerCase() === email.toLowerCase()) {
17
+ return user;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ async findMany(
24
+ params: PaginationParams,
25
+ filters?: UserFilters
26
+ ): Promise<PaginatedResult<User>> {
27
+ let filteredUsers = Array.from(users.values());
28
+
29
+ // Apply filters
30
+ if (filters) {
31
+ if (filters.status) {
32
+ filteredUsers = filteredUsers.filter((u) => u.status === filters.status);
33
+ }
34
+ if (filters.role) {
35
+ filteredUsers = filteredUsers.filter((u) => u.role === filters.role);
36
+ }
37
+ if (filters.emailVerified !== undefined) {
38
+ filteredUsers = filteredUsers.filter((u) => u.emailVerified === filters.emailVerified);
39
+ }
40
+ if (filters.search) {
41
+ const search = filters.search.toLowerCase();
42
+ filteredUsers = filteredUsers.filter(
43
+ (u) =>
44
+ u.email.toLowerCase().includes(search) ||
45
+ u.name?.toLowerCase().includes(search)
46
+ );
47
+ }
48
+ }
49
+
50
+ // Sort
51
+ if (params.sortBy) {
52
+ const sortKey = params.sortBy as keyof User;
53
+ filteredUsers.sort((a, b) => {
54
+ const aVal = a[sortKey];
55
+ const bVal = b[sortKey];
56
+ if (aVal === undefined || bVal === undefined) return 0;
57
+ if (aVal < bVal) return params.sortOrder === 'desc' ? 1 : -1;
58
+ if (aVal > bVal) return params.sortOrder === 'desc' ? -1 : 1;
59
+ return 0;
60
+ });
61
+ }
62
+
63
+ const total = filteredUsers.length;
64
+ const skip = getSkip(params);
65
+ const data = filteredUsers.slice(skip, skip + params.limit);
66
+
67
+ return createPaginatedResult(data, total, params);
68
+ }
69
+
70
+ async create(data: CreateUserData): Promise<User> {
71
+ const now = new Date();
72
+ const user: User = {
73
+ id: randomUUID(),
74
+ email: data.email,
75
+ password: data.password,
76
+ name: data.name,
77
+ role: data.role || 'user',
78
+ status: 'active',
79
+ emailVerified: false,
80
+ createdAt: now,
81
+ updatedAt: now,
82
+ };
83
+
84
+ users.set(user.id, user);
85
+ return user;
86
+ }
87
+
88
+ async update(id: string, data: UpdateUserData): Promise<User | null> {
89
+ const user = users.get(id);
90
+ if (!user) return null;
91
+
92
+ const updatedUser: User = {
93
+ ...user,
94
+ ...data,
95
+ updatedAt: new Date(),
96
+ };
97
+
98
+ users.set(id, updatedUser);
99
+ return updatedUser;
100
+ }
101
+
102
+ async updatePassword(id: string, password: string): Promise<User | null> {
103
+ const user = users.get(id);
104
+ if (!user) return null;
105
+
106
+ const updatedUser: User = {
107
+ ...user,
108
+ password,
109
+ updatedAt: new Date(),
110
+ };
111
+
112
+ users.set(id, updatedUser);
113
+ return updatedUser;
114
+ }
115
+
116
+ async updateLastLogin(id: string): Promise<User | null> {
117
+ const user = users.get(id);
118
+ if (!user) return null;
119
+
120
+ const updatedUser: User = {
121
+ ...user,
122
+ lastLoginAt: new Date(),
123
+ updatedAt: new Date(),
124
+ };
125
+
126
+ users.set(id, updatedUser);
127
+ return updatedUser;
128
+ }
129
+
130
+ async delete(id: string): Promise<boolean> {
131
+ return users.delete(id);
132
+ }
133
+
134
+ async count(filters?: UserFilters): Promise<number> {
135
+ let count = 0;
136
+ for (const user of users.values()) {
137
+ if (filters) {
138
+ if (filters.status && user.status !== filters.status) continue;
139
+ if (filters.role && user.role !== filters.role) continue;
140
+ if (filters.emailVerified !== undefined && user.emailVerified !== filters.emailVerified)
141
+ continue;
142
+ }
143
+ count++;
144
+ }
145
+ return count;
146
+ }
147
+
148
+ // Helper to clear all users (for testing)
149
+ async clear(): Promise<void> {
150
+ users.clear();
151
+ }
152
+ }
153
+
154
+ export function createUserRepository(): UserRepository {
155
+ return new UserRepository();
156
+ }