From 1bba2b518ef63b1f3285bf58276474820f480d4b Mon Sep 17 00:00:00 2001 From: Rayyan Syahbani Hermanto <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:57:18 +0700 Subject: [PATCH] Bot --- dist/adapters/notification/core-api-client.js | 131 +++++++++++++++ .../notification/core-api-client.js.map | 1 + dist/app.module.js | 26 +++ dist/app.module.js.map | 1 + dist/core/auth/guards/jwt-auth.guard.js | 45 ++++++ dist/core/auth/guards/jwt-auth.guard.js.map | 1 + .../authenticated-request.interface.js | 3 + .../authenticated-request.interface.js.map | 1 + dist/core/config/database.config.js | 13 ++ dist/core/config/database.config.js.map | 1 + dist/core/events/event-dispatcher.js | 14 ++ dist/core/events/event-dispatcher.js.map | 1 + dist/core/events/event-handler.js | 3 + dist/core/events/event-handler.js.map | 1 + dist/core/events/event-publisher.js | 14 ++ dist/core/events/event-publisher.js.map | 1 + dist/core/events/event.interface.js | 3 + dist/core/events/event.interface.js.map | 1 + dist/core/exceptions/http-exception.filter.js | 29 ++++ .../exceptions/http-exception.filter.js.map | 1 + dist/core/logger/logger.service.js | 11 ++ dist/core/logger/logger.service.js.map | 1 + dist/core/middleware/request-id.middleware.js | 23 +++ .../middleware/request-id.middleware.js.map | 1 + dist/main.js | 34 ++++ dist/main.js.map | 1 + .../application/authorization.service.js | 2 + .../application/authorization.service.js.map | 1 + .../decorators/permission.decorator.js | 7 + .../decorators/permission.decorator.js.map | 1 + .../presentation/guards/permission.guard.js | 38 +++++ .../guards/permission.guard.js.map | 1 + .../handlers/create-user.handler.js | 79 +++++++++ .../handlers/create-user.handler.js.map | 1 + .../handlers/delete-user.handler.js | 36 +++++ .../handlers/delete-user.handler.js.map | 1 + .../application/handlers/find-user.handler.js | 33 ++++ .../handlers/find-user.handler.js.map | 1 + .../handlers/find-users.handler.js | 29 ++++ .../handlers/find-users.handler.js.map | 1 + .../application/handlers/login.handler.js | 96 +++++++++++ .../application/handlers/login.handler.js.map | 1 + .../application/handlers/token.handler.js | 103 ++++++++++++ .../application/handlers/token.handler.js.map | 1 + .../handlers/update-user.handler.js | 84 ++++++++++ .../handlers/update-user.handler.js.map | 1 + .../domain/entities/service-account.entity.js | 37 +++++ .../entities/service-account.entity.js.map | 1 + .../identity/domain/entities/user.entity.js | 37 +++++ .../domain/entities/user.entity.js.map | 1 + .../domain/events/user-created.event.js | 14 ++ .../domain/events/user-created.event.js.map | 1 + .../domain/events/user-deleted.event.js | 14 ++ .../domain/events/user-deleted.event.js.map | 1 + .../service-account.repository.interface.js | 7 + ...ervice-account.repository.interface.js.map | 1 + .../repositories/user.repository.interface.js | 7 + .../user.repository.interface.js.map | 1 + dist/modules/identity/identity.module.js | 75 +++++++++ dist/modules/identity/identity.module.js.map | 1 + .../mappers/prisma-user.mapper.js | 29 ++++ .../mappers/prisma-user.mapper.js.map | 1 + .../prisma-service-account.repository.js | 109 +++++++++++++ .../prisma-service-account.repository.js.map | 1 + .../repositories/prisma-user.repository.js | 76 +++++++++ .../prisma-user.repository.js.map | 1 + .../controllers/auth.controller.js | 68 ++++++++ .../controllers/auth.controller.js.map | 1 + .../controllers/users.controller.js | 150 ++++++++++++++++++ .../controllers/users.controller.js.map | 1 + .../presentation/dto/create-user.dto.js | 43 +++++ .../presentation/dto/create-user.dto.js.map | 1 + .../identity/presentation/dto/login.dto.js | 31 ++++ .../presentation/dto/login.dto.js.map | 1 + .../presentation/dto/update-user.dto.js | 47 ++++++ .../presentation/dto/update-user.dto.js.map | 1 + dist/shared/prisma.service.js | 24 +++ dist/shared/prisma.service.js.map | 1 + dist/shared/utils/index.js | 5 + dist/shared/utils/index.js.map | 1 + jest.config.cjs | 6 + prisma/schema.prisma | 14 ++ prisma/seed.ts | 45 +++++- src/adapters/notification/core-api-client.ts | 149 +++++++++++++++++ .../handlers/create-user.handler.ts | 6 +- .../application/handlers/login.handler.ts | 57 +++++++ .../application/handlers/token.handler.ts | 63 ++++++++ .../handlers/update-user.handler.ts | 7 + .../domain/entities/service-account.entity.ts | 62 ++++++++ .../identity/domain/entities/user.entity.ts | 4 + .../service-account.repository.interface.ts | 11 ++ .../repositories/user.repository.interface.ts | 2 + src/modules/identity/identity.module.ts | 14 +- .../prisma-service-account.repository.ts | 99 ++++++++++++ .../repositories/prisma-user.repository.ts | 10 +- .../controllers/auth.controller.ts | 40 +++++ .../controllers/users.controller.ts | 20 ++- .../identity/presentation/dto/login.dto.ts | 13 ++ tests/adapters/core-api-client.spec.ts | 87 ++++++++++ .../prisma-service-account-repository.spec.ts | 35 ++++ tests/identity/token.handler.spec.ts | 98 ++++++++++++ 101 files changed, 2503 insertions(+), 8 deletions(-) create mode 100644 dist/adapters/notification/core-api-client.js create mode 100644 dist/adapters/notification/core-api-client.js.map create mode 100644 dist/app.module.js create mode 100644 dist/app.module.js.map create mode 100644 dist/core/auth/guards/jwt-auth.guard.js create mode 100644 dist/core/auth/guards/jwt-auth.guard.js.map create mode 100644 dist/core/auth/interfaces/authenticated-request.interface.js create mode 100644 dist/core/auth/interfaces/authenticated-request.interface.js.map create mode 100644 dist/core/config/database.config.js create mode 100644 dist/core/config/database.config.js.map create mode 100644 dist/core/events/event-dispatcher.js create mode 100644 dist/core/events/event-dispatcher.js.map create mode 100644 dist/core/events/event-handler.js create mode 100644 dist/core/events/event-handler.js.map create mode 100644 dist/core/events/event-publisher.js create mode 100644 dist/core/events/event-publisher.js.map create mode 100644 dist/core/events/event.interface.js create mode 100644 dist/core/events/event.interface.js.map create mode 100644 dist/core/exceptions/http-exception.filter.js create mode 100644 dist/core/exceptions/http-exception.filter.js.map create mode 100644 dist/core/logger/logger.service.js create mode 100644 dist/core/logger/logger.service.js.map create mode 100644 dist/core/middleware/request-id.middleware.js create mode 100644 dist/core/middleware/request-id.middleware.js.map create mode 100644 dist/main.js create mode 100644 dist/main.js.map create mode 100644 dist/modules/authorization/application/authorization.service.js create mode 100644 dist/modules/authorization/application/authorization.service.js.map create mode 100644 dist/modules/authorization/presentation/decorators/permission.decorator.js create mode 100644 dist/modules/authorization/presentation/decorators/permission.decorator.js.map create mode 100644 dist/modules/authorization/presentation/guards/permission.guard.js create mode 100644 dist/modules/authorization/presentation/guards/permission.guard.js.map create mode 100644 dist/modules/identity/application/handlers/create-user.handler.js create mode 100644 dist/modules/identity/application/handlers/create-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/delete-user.handler.js create mode 100644 dist/modules/identity/application/handlers/delete-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/find-user.handler.js create mode 100644 dist/modules/identity/application/handlers/find-user.handler.js.map create mode 100644 dist/modules/identity/application/handlers/find-users.handler.js create mode 100644 dist/modules/identity/application/handlers/find-users.handler.js.map create mode 100644 dist/modules/identity/application/handlers/login.handler.js create mode 100644 dist/modules/identity/application/handlers/login.handler.js.map create mode 100644 dist/modules/identity/application/handlers/token.handler.js create mode 100644 dist/modules/identity/application/handlers/token.handler.js.map create mode 100644 dist/modules/identity/application/handlers/update-user.handler.js create mode 100644 dist/modules/identity/application/handlers/update-user.handler.js.map create mode 100644 dist/modules/identity/domain/entities/service-account.entity.js create mode 100644 dist/modules/identity/domain/entities/service-account.entity.js.map create mode 100644 dist/modules/identity/domain/entities/user.entity.js create mode 100644 dist/modules/identity/domain/entities/user.entity.js.map create mode 100644 dist/modules/identity/domain/events/user-created.event.js create mode 100644 dist/modules/identity/domain/events/user-created.event.js.map create mode 100644 dist/modules/identity/domain/events/user-deleted.event.js create mode 100644 dist/modules/identity/domain/events/user-deleted.event.js.map create mode 100644 dist/modules/identity/domain/repositories/service-account.repository.interface.js create mode 100644 dist/modules/identity/domain/repositories/service-account.repository.interface.js.map create mode 100644 dist/modules/identity/domain/repositories/user.repository.interface.js create mode 100644 dist/modules/identity/domain/repositories/user.repository.interface.js.map create mode 100644 dist/modules/identity/identity.module.js create mode 100644 dist/modules/identity/identity.module.js.map create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js create mode 100644 dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js.map create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-user.repository.js create mode 100644 dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map create mode 100644 dist/modules/identity/presentation/controllers/auth.controller.js create mode 100644 dist/modules/identity/presentation/controllers/auth.controller.js.map create mode 100644 dist/modules/identity/presentation/controllers/users.controller.js create mode 100644 dist/modules/identity/presentation/controllers/users.controller.js.map create mode 100644 dist/modules/identity/presentation/dto/create-user.dto.js create mode 100644 dist/modules/identity/presentation/dto/create-user.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/login.dto.js create mode 100644 dist/modules/identity/presentation/dto/login.dto.js.map create mode 100644 dist/modules/identity/presentation/dto/update-user.dto.js create mode 100644 dist/modules/identity/presentation/dto/update-user.dto.js.map create mode 100644 dist/shared/prisma.service.js create mode 100644 dist/shared/prisma.service.js.map create mode 100644 dist/shared/utils/index.js create mode 100644 dist/shared/utils/index.js.map create mode 100644 jest.config.cjs create mode 100644 src/adapters/notification/core-api-client.ts create mode 100644 src/modules/identity/application/handlers/login.handler.ts create mode 100644 src/modules/identity/application/handlers/token.handler.ts create mode 100644 src/modules/identity/domain/entities/service-account.entity.ts create mode 100644 src/modules/identity/domain/repositories/service-account.repository.interface.ts create mode 100644 src/modules/identity/infrastructure/repositories/prisma-service-account.repository.ts create mode 100644 src/modules/identity/presentation/controllers/auth.controller.ts create mode 100644 src/modules/identity/presentation/dto/login.dto.ts create mode 100644 tests/adapters/core-api-client.spec.ts create mode 100644 tests/identity/prisma-service-account-repository.spec.ts create mode 100644 tests/identity/token.handler.spec.ts diff --git a/dist/adapters/notification/core-api-client.js b/dist/adapters/notification/core-api-client.js new file mode 100644 index 0000000..a9879c8 --- /dev/null +++ b/dist/adapters/notification/core-api-client.js @@ -0,0 +1,131 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CoreApiClient = exports.CoreApiClientError = void 0; +class CoreApiClientError extends Error { + status; + constructor(message, status) { + super(message); + this.name = 'CoreApiClientError'; + this.status = status; + } +} +exports.CoreApiClientError = CoreApiClientError; +class CoreApiClient { + apiUrl; + clientId; + clientSecret; + timeoutMs; + cachedToken = null; + tokenExpiry = 0; // epoch ms + tokenMarginSec = 60; // safety margin in seconds + constructor(options) { + this.apiUrl = options?.apiUrl ?? process.env.RAYLAB_CORE_API_URL ?? 'http://localhost:3000'; + this.clientId = options?.clientId ?? process.env.RAYLAB_BOT_CLIENT_ID ?? ''; + this.clientSecret = options?.clientSecret ?? process.env.RAYLAB_BOT_CLIENT_SECRET ?? ''; + this.timeoutMs = options?.timeoutMs ?? 5000; + if (!this.clientId || !this.clientSecret) { + // keep construction allowed but operations will fail with clear errors + } + } + async fetchWithTimeout(url, init) { + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), this.timeoutMs); + try { + const res = await fetch(url, { ...init, signal: controller.signal }); + return res; + } + finally { + clearTimeout(id); + } + } + isTokenValid() { + return !!this.cachedToken && Date.now() < this.tokenExpiry; + } + async requestAccessToken() { + if (!this.clientId || !this.clientSecret) { + throw new CoreApiClientError('Missing client credentials'); + } + const url = `${this.apiUrl.replace(/\/$/, '')}/api/v1/auth/token`; + const body = JSON.stringify({ client_id: this.clientId, client_secret: this.clientSecret, grant_type: 'client_credentials' }); + let res; + try { + res = await this.fetchWithTimeout(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body, + }); + } + catch (err) { + if (err.name === 'AbortError') { + throw new CoreApiClientError('Request timeout'); + } + throw new CoreApiClientError('Network error'); + } + if (res.status === 401) { + throw new CoreApiClientError('Invalid client credentials', 401); + } + if (res.status >= 400) { + throw new CoreApiClientError(`Token request failed with status ${res.status}`, res.status); + } + const payload = await res.json(); + // Expecting { success: true, data: { access_token, token_type, expires_in }, ... } + const data = payload?.data; + if (!data || !data.access_token) { + throw new CoreApiClientError('Invalid token response'); + } + // Cache token + this.cachedToken = data.access_token; + const expiresIn = typeof data.expires_in === 'number' ? data.expires_in : parseInt(data.expires_in, 10) || 3600; + const effective = Math.max(0, expiresIn - this.tokenMarginSec); + this.tokenExpiry = Date.now() + effective * 1000; + return { access_token: data.access_token, token_type: data.token_type ?? 'Bearer', expires_in: expiresIn }; + } + async getToken() { + if (this.isTokenValid()) { + return this.cachedToken; + } + const tokenResp = await this.requestAccessToken(); + return tokenResp.access_token; + } + async authenticatedRequest(input, init, retry = true) { + const token = await this.getToken(); + const headers = new Headers(init?.headers); + headers.set('Authorization', `Bearer ${token}`); + headers.set('Accept', 'application/json'); + let res; + try { + res = await this.fetchWithTimeout(typeof input === 'string' ? input : input.url, { + ...init, + headers, + }); + } + catch (err) { + if (err.name === 'AbortError') { + throw new CoreApiClientError('Request timeout'); + } + throw new CoreApiClientError('Network error'); + } + if (res.status === 401 && retry) { + // invalidate token and retry once + this.cachedToken = null; + this.tokenExpiry = 0; + try { + const newToken = await this.getToken(); + const headers2 = new Headers(init?.headers); + headers2.set('Authorization', `Bearer ${newToken}`); + headers2.set('Accept', 'application/json'); + const res2 = await this.fetchWithTimeout(typeof input === 'string' ? input : input.url, { + ...init, + headers: headers2, + }); + return res2; + } + catch (e) { + throw e; + } + } + return res; + } +} +exports.CoreApiClient = CoreApiClient; +//# sourceMappingURL=core-api-client.js.map \ No newline at end of file diff --git a/dist/adapters/notification/core-api-client.js.map b/dist/adapters/notification/core-api-client.js.map new file mode 100644 index 0000000..626ab13 --- /dev/null +++ b/dist/adapters/notification/core-api-client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"core-api-client.js","sourceRoot":"","sources":["../../../src/adapters/notification/core-api-client.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/app.module.js b/dist/app.module.js new file mode 100644 index 0000000..758d604 --- /dev/null +++ b/dist/app.module.js @@ -0,0 +1,26 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AppModule = void 0; +const common_1 = require("@nestjs/common"); +const config_1 = require("@nestjs/config"); +const identity_module_1 = require("./modules/identity/identity.module"); +let AppModule = class AppModule { +}; +exports.AppModule = AppModule; +exports.AppModule = AppModule = __decorate([ + (0, common_1.Module)({ + imports: [ + config_1.ConfigModule.forRoot({ + isGlobal: true, + }), + identity_module_1.IdentityModule, + ], + }) +], AppModule); +//# sourceMappingURL=app.module.js.map \ No newline at end of file diff --git a/dist/app.module.js.map b/dist/app.module.js.map new file mode 100644 index 0000000..fe24986 --- /dev/null +++ b/dist/app.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAE9C,wEAAoE;AAW7D,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IATrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;aACf,CAAC;YAEF,gCAAc;SACf;KACF,CAAC;GACW,SAAS,CAAG"} \ No newline at end of file diff --git a/dist/core/auth/guards/jwt-auth.guard.js b/dist/core/auth/guards/jwt-auth.guard.js new file mode 100644 index 0000000..4311bb8 --- /dev/null +++ b/dist/core/auth/guards/jwt-auth.guard.js @@ -0,0 +1,45 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.JwtAuthGuard = void 0; +const common_1 = require("@nestjs/common"); +const jwt_1 = require("@nestjs/jwt"); +let JwtAuthGuard = class JwtAuthGuard { + jwtService; + constructor(jwtService) { + this.jwtService = jwtService; + } + async canActivate(context) { + const request = context.switchToHttp().getRequest(); + const authHeader = request.headers.authorization; + if (!authHeader) { + throw new common_1.UnauthorizedException('Authorization header is missing.'); + } + const [type, token] = authHeader.split(' '); + if (type !== 'Bearer' || !token) { + throw new common_1.UnauthorizedException('Invalid authorization header.'); + } + try { + const payload = await this.jwtService.verifyAsync(token); + request['user'] = payload; + return true; + } + catch { + throw new common_1.UnauthorizedException('Invalid or expired token.'); + } + } +}; +exports.JwtAuthGuard = JwtAuthGuard; +exports.JwtAuthGuard = JwtAuthGuard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [jwt_1.JwtService]) +], JwtAuthGuard); +//# sourceMappingURL=jwt-auth.guard.js.map \ No newline at end of file diff --git a/dist/core/auth/guards/jwt-auth.guard.js.map b/dist/core/auth/guards/jwt-auth.guard.js.map new file mode 100644 index 0000000..7bdb14c --- /dev/null +++ b/dist/core/auth/guards/jwt-auth.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../../src/core/auth/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AACxB,qCAAyC;AAIlC,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEJ;IADnB,YACmB,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IACtC,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAW,CAAC;QAE7D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,8BAAqB,CAAC,kCAAkC,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE5C,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,IAAI,8BAAqB,CAAC,+BAA+B,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAEzD,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;YAE1B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;CACF,CAAA;AA9BY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAGoB,gBAAU;GAF9B,YAAY,CA8BxB"} \ No newline at end of file diff --git a/dist/core/auth/interfaces/authenticated-request.interface.js b/dist/core/auth/interfaces/authenticated-request.interface.js new file mode 100644 index 0000000..c514541 --- /dev/null +++ b/dist/core/auth/interfaces/authenticated-request.interface.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=authenticated-request.interface.js.map \ No newline at end of file diff --git a/dist/core/auth/interfaces/authenticated-request.interface.js.map b/dist/core/auth/interfaces/authenticated-request.interface.js.map new file mode 100644 index 0000000..220894c --- /dev/null +++ b/dist/core/auth/interfaces/authenticated-request.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authenticated-request.interface.js","sourceRoot":"","sources":["../../../../src/core/auth/interfaces/authenticated-request.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/config/database.config.js b/dist/core/config/database.config.js new file mode 100644 index 0000000..c4bf0dd --- /dev/null +++ b/dist/core/config/database.config.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = () => ({ + database: { + url: `postgresql://` + + `${process.env.DB_USER}:` + + `${process.env.DB_PASSWORD}@` + + `${process.env.SERVER_HOST}:` + + `${process.env.DB_PORT}/` + + `${process.env.DB_NAME}?schema=public` + } +}); +//# sourceMappingURL=database.config.js.map \ No newline at end of file diff --git a/dist/core/config/database.config.js.map b/dist/core/config/database.config.js.map new file mode 100644 index 0000000..4f98463 --- /dev/null +++ b/dist/core/config/database.config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"database.config.js","sourceRoot":"","sources":["../../../src/core/config/database.config.ts"],"names":[],"mappings":";;AAAA,kBAAe,GAAG,EAAE,CAAC,CAAC;IAClB,QAAQ,EAAE;QACN,GAAG,EACC,eAAe;YACf,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG;YACzB,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG;YAC7B,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG;YAC7B,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG;YACzB,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,gBAAgB;KAC7C;CACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/core/events/event-dispatcher.js b/dist/core/events/event-dispatcher.js new file mode 100644 index 0000000..ad25771 --- /dev/null +++ b/dist/core/events/event-dispatcher.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventDispatcher = void 0; +class EventDispatcher { + publisher = new (require('./event-publisher').InMemoryEventPublisher)(); + register(handler) { + this.publisher.register(handler); + } + async dispatch(event) { + await this.publisher.publish(event); + } +} +exports.EventDispatcher = EventDispatcher; +//# sourceMappingURL=event-dispatcher.js.map \ No newline at end of file diff --git a/dist/core/events/event-dispatcher.js.map b/dist/core/events/event-dispatcher.js.map new file mode 100644 index 0000000..065bcf8 --- /dev/null +++ b/dist/core/events/event-dispatcher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-dispatcher.js","sourceRoot":"","sources":["../../../src/core/events/event-dispatcher.ts"],"names":[],"mappings":";;;AAEA,MAAa,eAAe;IAClB,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,sBAAsB,CAAC,EAAE,CAAC;IAEhF,QAAQ,CAAC,OAA0C;QACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAkB;QAC/B,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;CACF;AAVD,0CAUC"} \ No newline at end of file diff --git a/dist/core/events/event-handler.js b/dist/core/events/event-handler.js new file mode 100644 index 0000000..6950f78 --- /dev/null +++ b/dist/core/events/event-handler.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=event-handler.js.map \ No newline at end of file diff --git a/dist/core/events/event-handler.js.map b/dist/core/events/event-handler.js.map new file mode 100644 index 0000000..efbfd3f --- /dev/null +++ b/dist/core/events/event-handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-handler.js","sourceRoot":"","sources":["../../../src/core/events/event-handler.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/events/event-publisher.js b/dist/core/events/event-publisher.js new file mode 100644 index 0000000..7429352 --- /dev/null +++ b/dist/core/events/event-publisher.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InMemoryEventPublisher = void 0; +class InMemoryEventPublisher { + handlers = []; + register(handler) { + this.handlers.push(handler); + } + async publish(event) { + await Promise.all(this.handlers.map((h) => h(event))); + } +} +exports.InMemoryEventPublisher = InMemoryEventPublisher; +//# sourceMappingURL=event-publisher.js.map \ No newline at end of file diff --git a/dist/core/events/event-publisher.js.map b/dist/core/events/event-publisher.js.map new file mode 100644 index 0000000..c2c673c --- /dev/null +++ b/dist/core/events/event-publisher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event-publisher.js","sourceRoot":"","sources":["../../../src/core/events/event-publisher.ts"],"names":[],"mappings":";;;AAMA,MAAa,sBAAsB;IACzB,QAAQ,GAA6C,EAAE,CAAC;IAEhE,QAAQ,CAAC,OAA0C;QACjD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAkB;QAC9B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;CACF;AAVD,wDAUC"} \ No newline at end of file diff --git a/dist/core/events/event.interface.js b/dist/core/events/event.interface.js new file mode 100644 index 0000000..051ab18 --- /dev/null +++ b/dist/core/events/event.interface.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=event.interface.js.map \ No newline at end of file diff --git a/dist/core/events/event.interface.js.map b/dist/core/events/event.interface.js.map new file mode 100644 index 0000000..2fa0bd2 --- /dev/null +++ b/dist/core/events/event.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"event.interface.js","sourceRoot":"","sources":["../../../src/core/events/event.interface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/core/exceptions/http-exception.filter.js b/dist/core/exceptions/http-exception.filter.js new file mode 100644 index 0000000..36ced11 --- /dev/null +++ b/dist/core/exceptions/http-exception.filter.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HttpExceptionFilter = void 0; +const common_1 = require("@nestjs/common"); +let HttpExceptionFilter = class HttpExceptionFilter { + catch(exception, host) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const status = exception.getStatus ? exception.getStatus() : 500; + response.status(status).json({ + success: false, + error: { + code: exception.code || 'INTERNAL_ERROR', + message: exception.message || 'Internal server error', + }, + }); + } +}; +exports.HttpExceptionFilter = HttpExceptionFilter; +exports.HttpExceptionFilter = HttpExceptionFilter = __decorate([ + (0, common_1.Catch)() +], HttpExceptionFilter); +//# sourceMappingURL=http-exception.filter.js.map \ No newline at end of file diff --git a/dist/core/exceptions/http-exception.filter.js.map b/dist/core/exceptions/http-exception.filter.js.map new file mode 100644 index 0000000..9774136 --- /dev/null +++ b/dist/core/exceptions/http-exception.filter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"http-exception.filter.js","sourceRoot":"","sources":["../../../src/core/exceptions/http-exception.filter.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAuE;AAGhE,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,KAAK,CAAC,SAAc,EAAE,IAAmB;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAEjE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;YAC3B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,gBAAgB;gBACxC,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,uBAAuB;aACtD;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAdY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,cAAK,GAAE;GACK,mBAAmB,CAc/B"} \ No newline at end of file diff --git a/dist/core/logger/logger.service.js b/dist/core/logger/logger.service.js new file mode 100644 index 0000000..8de8f09 --- /dev/null +++ b/dist/core/logger/logger.service.js @@ -0,0 +1,11 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.logger = void 0; +const pino_1 = __importDefault(require("pino")); +exports.logger = (0, pino_1.default)({ + level: process.env.LOG_LEVEL || 'info', +}); +//# sourceMappingURL=logger.service.js.map \ No newline at end of file diff --git a/dist/core/logger/logger.service.js.map b/dist/core/logger/logger.service.js.map new file mode 100644 index 0000000..459e62c --- /dev/null +++ b/dist/core/logger/logger.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"logger.service.js","sourceRoot":"","sources":["../../../src/core/logger/logger.service.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAEX,QAAA,MAAM,GAAG,IAAA,cAAI,EAAC;IACzB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;CACvC,CAAC,CAAC"} \ No newline at end of file diff --git a/dist/core/middleware/request-id.middleware.js b/dist/core/middleware/request-id.middleware.js new file mode 100644 index 0000000..4fa05fb --- /dev/null +++ b/dist/core/middleware/request-id.middleware.js @@ -0,0 +1,23 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RequestIdMiddleware = void 0; +const common_1 = require("@nestjs/common"); +const uuid_1 = require("uuid"); +let RequestIdMiddleware = class RequestIdMiddleware { + use(req, res, next) { + req.requestId = req.headers['x-request-id'] || (0, uuid_1.v4)(); + res.setHeader('X-Request-ID', req.requestId); + next(); + } +}; +exports.RequestIdMiddleware = RequestIdMiddleware; +exports.RequestIdMiddleware = RequestIdMiddleware = __decorate([ + (0, common_1.Injectable)() +], RequestIdMiddleware); +//# sourceMappingURL=request-id.middleware.js.map \ No newline at end of file diff --git a/dist/core/middleware/request-id.middleware.js.map b/dist/core/middleware/request-id.middleware.js.map new file mode 100644 index 0000000..393bde4 --- /dev/null +++ b/dist/core/middleware/request-id.middleware.js.map @@ -0,0 +1 @@ +{"version":3,"file":"request-id.middleware.js","sourceRoot":"","sources":["../../../src/core/middleware/request-id.middleware.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4D;AAC5D,+BAAoC;AAG7B,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,GAAG,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAgB;QACtC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,IAAA,SAAM,GAAE,CAAC;QACxD,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC;IACT,CAAC;CACF,CAAA;AANY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,mBAAU,GAAE;GACA,mBAAmB,CAM/B"} \ No newline at end of file diff --git a/dist/main.js b/dist/main.js new file mode 100644 index 0000000..283e71b --- /dev/null +++ b/dist/main.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +const config_1 = require("@nestjs/config"); +const swagger_1 = require("@nestjs/swagger"); +const app_module_1 = require("./app.module"); +async function bootstrap() { + const app = await core_1.NestFactory.create(app_module_1.AppModule); + const config = app.get(config_1.ConfigService); + app.setGlobalPrefix('api'); + app.useGlobalPipes(new common_1.ValidationPipe({ + whitelist: true, + transform: true, + forbidNonWhitelisted: true, + })); + app.enableCors(); + const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; + if (swaggerEnabled) { + const swaggerConfig = new swagger_1.DocumentBuilder() + .setTitle('RayLab Core API') + .setDescription('RayLab Core REST API') + .setVersion('1.0.0') + .addBearerAuth() + .build(); + const document = swagger_1.SwaggerModule.createDocument(app, swaggerConfig); + swagger_1.SwaggerModule.setup('docs', app, document); + } + const port = config.get('PORT') || 3000; + await app.listen(port); + console.log(`Server running on http://localhost:${port}`); +} +bootstrap(); +//# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/dist/main.js.map b/dist/main.js.map new file mode 100644 index 0000000..5c78d80 --- /dev/null +++ b/dist/main.js.map @@ -0,0 +1 @@ +{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,2CAAgD;AAChD,uCAA2C;AAC3C,2CAA+C;AAC/C,6CAAiE;AAEjE,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IAEtC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE3B,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,IAAI;QACf,SAAS,EAAE,IAAI;QACf,oBAAoB,EAAE,IAAI;KAC3B,CAAC,CACH,CAAC;IAEF,GAAG,CAAC,UAAU,EAAE,CAAC;IAEjB,MAAM,cAAc,GAClB,MAAM,CAAC,GAAG,CAAS,iBAAiB,CAAC,KAAK,MAAM,CAAC;IAEnD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,aAAa,GAAG,IAAI,yBAAe,EAAE;aACxC,QAAQ,CAAC,iBAAiB,CAAC;aAC3B,cAAc,CAAC,sBAAsB,CAAC;aACtC,UAAU,CAAC,OAAO,CAAC;aACnB,aAAa,EAAE;aACf,KAAK,EAAE,CAAC;QAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAElE,uBAAa,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAS,MAAM,CAAC,IAAI,IAAI,CAAC;IAEhD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/modules/authorization/application/authorization.service.js b/dist/modules/authorization/application/authorization.service.js new file mode 100644 index 0000000..6ce7dcf --- /dev/null +++ b/dist/modules/authorization/application/authorization.service.js @@ -0,0 +1,2 @@ +"use strict"; +//# sourceMappingURL=authorization.service.js.map \ No newline at end of file diff --git a/dist/modules/authorization/application/authorization.service.js.map b/dist/modules/authorization/application/authorization.service.js.map new file mode 100644 index 0000000..dea3510 --- /dev/null +++ b/dist/modules/authorization/application/authorization.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"authorization.service.js","sourceRoot":"","sources":["../../../../src/modules/authorization/application/authorization.service.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/modules/authorization/presentation/decorators/permission.decorator.js b/dist/modules/authorization/presentation/decorators/permission.decorator.js new file mode 100644 index 0000000..2547583 --- /dev/null +++ b/dist/modules/authorization/presentation/decorators/permission.decorator.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Permissions = void 0; +const common_1 = require("@nestjs/common"); +const Permissions = (...permissions) => (0, common_1.SetMetadata)('permissions', permissions); +exports.Permissions = Permissions; +//# sourceMappingURL=permission.decorator.js.map \ No newline at end of file diff --git a/dist/modules/authorization/presentation/decorators/permission.decorator.js.map b/dist/modules/authorization/presentation/decorators/permission.decorator.js.map new file mode 100644 index 0000000..cdad7da --- /dev/null +++ b/dist/modules/authorization/presentation/decorators/permission.decorator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.decorator.js","sourceRoot":"","sources":["../../../../../src/modules/authorization/presentation/decorators/permission.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAEtC,MAAM,WAAW,GAAG,CAAC,GAAG,WAAqB,EAAE,EAAE,CACtD,IAAA,oBAAW,EAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAD7B,QAAA,WAAW,eACkB"} \ No newline at end of file diff --git a/dist/modules/authorization/presentation/guards/permission.guard.js b/dist/modules/authorization/presentation/guards/permission.guard.js new file mode 100644 index 0000000..9073bb8 --- /dev/null +++ b/dist/modules/authorization/presentation/guards/permission.guard.js @@ -0,0 +1,38 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PermissionGuard = void 0; +const common_1 = require("@nestjs/common"); +const core_1 = require("@nestjs/core"); +let PermissionGuard = class PermissionGuard { + reflector; + constructor(reflector) { + this.reflector = reflector; + } + canActivate(context) { + const permissions = this.reflector.getAllAndOverride('permissions', [ + context.getHandler(), + context.getClass(), + ]); + if (!permissions) { + return true; + } + const request = context.switchToHttp().getRequest(); + const user = request.user; + return permissions.every(permission => user.permissions.includes(permission)); + } +}; +exports.PermissionGuard = PermissionGuard; +exports.PermissionGuard = PermissionGuard = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [core_1.Reflector]) +], PermissionGuard); +//# sourceMappingURL=permission.guard.js.map \ No newline at end of file diff --git a/dist/modules/authorization/presentation/guards/permission.guard.js.map b/dist/modules/authorization/presentation/guards/permission.guard.js.map new file mode 100644 index 0000000..c7d4e76 --- /dev/null +++ b/dist/modules/authorization/presentation/guards/permission.guard.js.map @@ -0,0 +1 @@ +{"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../../../../src/modules/authorization/presentation/guards/permission.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAIwB;AACxB,uCAAyC;AAGlC,IAAM,eAAe,GAArB,MAAM,eAAe;IAEP;IADnB,YACmB,SAAoB;QAApB,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,WAAW,CAAC,OAAyB;QACnC,MAAM,WAAW,GACf,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAC9B,aAAa,EACb;YACE,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACnB,CACF,CAAC;QAEJ,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAEpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAE1B,OAAO,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CACpC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CACtC,CAAC;IACJ,CAAC;CACF,CAAA;AA3BY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAGmB,gBAAS;GAF5B,eAAe,CA2B3B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/create-user.handler.js b/dist/modules/identity/application/handlers/create-user.handler.js new file mode 100644 index 0000000..6fbb464 --- /dev/null +++ b/dist/modules/identity/application/handlers/create-user.handler.js @@ -0,0 +1,79 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const bcrypt = __importStar(require("bcrypt")); +const user_entity_1 = require("../../domain/entities/user.entity"); +const user_repository_interface_1 = require("../../domain/repositories/user.repository.interface"); +let CreateUserHandler = class CreateUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(dto) { + const exists = await this.userRepository.existsByEmail(dto.email); + if (exists) { + throw new common_1.ConflictException('Email already exists.'); + } + const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS ?? '10', 10); + const hashedPassword = await bcrypt.hash(dto.password, saltRounds); + const user = user_entity_1.User.create({ + name: dto.name, + email: dto.email, + password: hashedPassword, + metadata: dto.metadata, + }); + await this.userRepository.create(user); + // TODO: + // this.eventDispatcher.publish(new UserCreatedEvent(user)); + return user; + } +}; +exports.CreateUserHandler = CreateUserHandler; +exports.CreateUserHandler = CreateUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_repository_interface_1.UserRepository]) +], CreateUserHandler); +//# sourceMappingURL=create-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/create-user.handler.js.map b/dist/modules/identity/application/handlers/create-user.handler.js.map new file mode 100644 index 0000000..e40acde --- /dev/null +++ b/dist/modules/identity/application/handlers/create-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-user.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/create-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAGwB;AAExB,+CAAiC;AAEjC,mEAAyD;AACzD,mGAAqF;AAG9E,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IADnB,YACmB,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAC9C,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAkB;QAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAElE,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,0BAAiB,CAAC,uBAAuB,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEnE,MAAM,IAAI,GAAG,kBAAI,CAAC,MAAM,CAAC;YACvB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,QAAQ,EAAE,cAAc;YACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;SACvB,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvC,QAAQ;QACR,4DAA4D;QAE5D,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA7BY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,0CAAc;GAFtC,iBAAiB,CA6B7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/delete-user.handler.js b/dist/modules/identity/application/handlers/delete-user.handler.js new file mode 100644 index 0000000..8be4b00 --- /dev/null +++ b/dist/modules/identity/application/handlers/delete-user.handler.js @@ -0,0 +1,36 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DeleteUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_repository_interface_1 = require("../../domain/repositories/user.repository.interface"); +let DeleteUserHandler = class DeleteUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + const user = await this.userRepository.findById(id); + if (!user) { + throw new common_1.NotFoundException('User not found.'); + } + user.delete(); + await this.userRepository.update(user); + // TODO: + // Publish UserDeletedEvent + } +}; +exports.DeleteUserHandler = DeleteUserHandler; +exports.DeleteUserHandler = DeleteUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_repository_interface_1.UserRepository]) +], DeleteUserHandler); +//# sourceMappingURL=delete-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/delete-user.handler.js.map b/dist/modules/identity/application/handlers/delete-user.handler.js.map new file mode 100644 index 0000000..9060645 --- /dev/null +++ b/dist/modules/identity/application/handlers/delete-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"delete-user.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/delete-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAGwB;AAExB,mGAAqF;AAG9E,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IADnB,YACmB,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAC9C,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEpD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;QAEd,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvC,QAAQ;QACR,2BAA2B;IAC7B,CAAC;CACF,CAAA;AAnBY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,0CAAc;GAFtC,iBAAiB,CAmB7B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/find-user.handler.js b/dist/modules/identity/application/handlers/find-user.handler.js new file mode 100644 index 0000000..81c9861 --- /dev/null +++ b/dist/modules/identity/application/handlers/find-user.handler.js @@ -0,0 +1,33 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FindUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_repository_interface_1 = require("../../domain/repositories/user.repository.interface"); +let FindUserHandler = class FindUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id) { + const user = await this.userRepository.findById(id); + if (!user) { + throw new common_1.NotFoundException('User not found.'); + } + return user; + } +}; +exports.FindUserHandler = FindUserHandler; +exports.FindUserHandler = FindUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_repository_interface_1.UserRepository]) +], FindUserHandler); +//# sourceMappingURL=find-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/find-user.handler.js.map b/dist/modules/identity/application/handlers/find-user.handler.js.map new file mode 100644 index 0000000..b5acc94 --- /dev/null +++ b/dist/modules/identity/application/handlers/find-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"find-user.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/find-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAGwB;AAExB,mGAAqF;AAI9E,IAAM,eAAe,GAArB,MAAM,eAAe;IAEP;IADnB,YACmB,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAC9C,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEpD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAdY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;qCAGwB,0CAAc;GAFtC,eAAe,CAc3B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/find-users.handler.js b/dist/modules/identity/application/handlers/find-users.handler.js new file mode 100644 index 0000000..fa8e6ea --- /dev/null +++ b/dist/modules/identity/application/handlers/find-users.handler.js @@ -0,0 +1,29 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FindUsersHandler = void 0; +const common_1 = require("@nestjs/common"); +const user_repository_interface_1 = require("../../domain/repositories/user.repository.interface"); +let FindUsersHandler = class FindUsersHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute() { + return await this.userRepository.findAll(); + } +}; +exports.FindUsersHandler = FindUsersHandler; +exports.FindUsersHandler = FindUsersHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_repository_interface_1.UserRepository]) +], FindUsersHandler); +//# sourceMappingURL=find-users.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/find-users.handler.js.map b/dist/modules/identity/application/handlers/find-users.handler.js.map new file mode 100644 index 0000000..6d0b33d --- /dev/null +++ b/dist/modules/identity/application/handlers/find-users.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"find-users.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/find-users.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAE5C,mGAAqF;AAI9E,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAER;IADnB,YACmB,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAC9C,CAAC;IAEJ,KAAK,CAAC,OAAO;QACX,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;IAC7C,CAAC;CACF,CAAA;AARY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;qCAGwB,0CAAc;GAFtC,gBAAgB,CAQ5B"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/login.handler.js b/dist/modules/identity/application/handlers/login.handler.js new file mode 100644 index 0000000..bc356e1 --- /dev/null +++ b/dist/modules/identity/application/handlers/login.handler.js @@ -0,0 +1,96 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoginHandler = void 0; +const common_1 = require("@nestjs/common"); +const bcrypt = __importStar(require("bcrypt")); +const jwt_1 = require("@nestjs/jwt"); +const user_repository_interface_1 = require("../../domain/repositories/user.repository.interface"); +let LoginHandler = class LoginHandler { + userRepository; + jwtService; + constructor(userRepository, jwtService) { + this.userRepository = userRepository; + this.jwtService = jwtService; + } + async execute(dto) { + const user = await this.userRepository.findByEmail(dto.email); + // Generic error message for security + const unauthorizedMsg = 'Invalid credentials.'; + if (!user) { + throw new common_1.UnauthorizedException(unauthorizedMsg); + } + const isMatch = await bcrypt.compare(dto.password, user.password); + if (!isMatch) { + throw new common_1.UnauthorizedException(unauthorizedMsg); + } + // Build payload according to existing JwtPayload interface + const metadata = user.metadata ?? {}; + const role = typeof metadata.role === 'string' ? metadata.role : 'USER'; + const permissions = Array.isArray(metadata.permissions) ? metadata.permissions : []; + const payload = { + sub: user.id, + email: user.email, + role, + permissions, + }; + const accessToken = await this.jwtService.signAsync(payload); + return { + access_token: accessToken, + expires_in: '1d', + user: { + id: user.id, + name: user.name, + email: user.email, + }, + }; + } +}; +exports.LoginHandler = LoginHandler; +exports.LoginHandler = LoginHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_repository_interface_1.UserRepository, + jwt_1.JwtService]) +], LoginHandler); +//# sourceMappingURL=login.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/login.handler.js.map b/dist/modules/identity/application/handlers/login.handler.js.map new file mode 100644 index 0000000..3154c0e --- /dev/null +++ b/dist/modules/identity/application/handlers/login.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"login.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/login.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAmE;AACnE,+CAAiC;AACjC,qCAAyC;AAGzC,mGAAqF;AAG9E,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEJ;IACA;IAFnB,YACmB,cAA8B,EAC9B,UAAsB;QADtB,mBAAc,GAAd,cAAc,CAAgB;QAC9B,eAAU,GAAV,UAAU,CAAY;IACtC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,GAAa;QACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE9D,qCAAqC;QACrC,MAAM,eAAe,GAAG,sBAAsB,CAAC;QAE/C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAElE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;QAED,2DAA2D;QAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QAErC,MAAM,IAAI,GAAG,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;QAExE,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAEpF,MAAM,OAAO,GAAG;YACd,GAAG,EAAE,IAAI,CAAC,EAAE;YACZ,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI;YACJ,WAAW;SACZ,CAAC;QAEF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAE7D,OAAO;YACL,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE;gBACJ,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AAhDY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAGwB,0CAAc;QAClB,gBAAU;GAH9B,YAAY,CAgDxB"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/token.handler.js b/dist/modules/identity/application/handlers/token.handler.js new file mode 100644 index 0000000..c80a3e9 --- /dev/null +++ b/dist/modules/identity/application/handlers/token.handler.js @@ -0,0 +1,103 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TokenHandler = void 0; +const common_1 = require("@nestjs/common"); +const bcrypt = __importStar(require("bcrypt")); +const jwt_1 = require("@nestjs/jwt"); +const service_account_repository_interface_1 = require("../../domain/repositories/service-account.repository.interface"); +let TokenHandler = class TokenHandler { + serviceAccountRepository; + jwtService; + constructor(serviceAccountRepository, jwtService) { + this.serviceAccountRepository = serviceAccountRepository; + this.jwtService = jwtService; + } + async execute(body) { + const { client_id, client_secret, grant_type } = body; + if (grant_type !== 'client_credentials') { + throw new common_1.BadRequestException('Invalid grant_type.'); + } + const unauthorizedMsg = 'Invalid client credentials.'; + if (!client_id || !client_secret) { + throw new common_1.UnauthorizedException(unauthorizedMsg); + } + const serviceAccount = await this.serviceAccountRepository.findByClientId(client_id); + if (!serviceAccount) { + // generic error + throw new common_1.UnauthorizedException(unauthorizedMsg); + } + // check status + if (serviceAccount.status !== 'ACTIVE') { + throw new common_1.UnauthorizedException(unauthorizedMsg); + } + const match = await bcrypt.compare(client_secret, serviceAccount.clientSecretHash); + if (!match) { + throw new common_1.UnauthorizedException(unauthorizedMsg); + } + const payload = { + sub: serviceAccount.id, + client_id: serviceAccount.clientId, + // existing JwtPayload expects email; provide empty string to remain compatible + email: '', + role: serviceAccount.role, + permissions: serviceAccount.permissions ?? [], + }; + const accessToken = await this.jwtService.signAsync(payload, { + expiresIn: '1h', + }); + return { + access_token: accessToken, + token_type: 'Bearer', + expires_in: 3600, + }; + } +}; +exports.TokenHandler = TokenHandler; +exports.TokenHandler = TokenHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [service_account_repository_interface_1.ServiceAccountRepository, + jwt_1.JwtService]) +], TokenHandler); +//# sourceMappingURL=token.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/token.handler.js.map b/dist/modules/identity/application/handlers/token.handler.js.map new file mode 100644 index 0000000..880460f --- /dev/null +++ b/dist/modules/identity/application/handlers/token.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"token.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/token.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAwF;AACxF,+CAAiC;AACjC,qCAAyC;AACzC,yHAA0G;AAGnG,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEJ;IACA;IAFnB,YACmB,wBAAkD,EAClD,UAAsB;QADtB,6BAAwB,GAAxB,wBAAwB,CAA0B;QAClD,eAAU,GAAV,UAAU,CAAY;IACtC,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,IAAyE;QACrF,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;QAEtD,IAAI,UAAU,KAAK,oBAAoB,EAAE,CAAC;YACxC,MAAM,IAAI,4BAAmB,CAAC,qBAAqB,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,eAAe,GAAG,6BAA6B,CAAC;QAEtD,IAAI,CAAC,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;YACjC,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAErF,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,gBAAgB;YAChB,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;QAED,eAAe;QACf,IAAI,cAAc,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACvC,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,cAAc,CAAC,gBAAgB,CAAC,CAAC;QAEnF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,8BAAqB,CAAC,eAAe,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,OAAO,GAAQ;YACnB,GAAG,EAAE,cAAc,CAAC,EAAE;YACtB,SAAS,EAAE,cAAc,CAAC,QAAQ;YAClC,+EAA+E;YAC/E,KAAK,EAAE,EAAE;YACT,IAAI,EAAE,cAAc,CAAC,IAAI;YACzB,WAAW,EAAE,cAAc,CAAC,WAAW,IAAI,EAAE;SAC9C,CAAC;QAEF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE;YAC3D,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QAEH,OAAO;YACL,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,QAAQ;YACpB,UAAU,EAAE,IAAI;SACjB,CAAC;IACJ,CAAC;CACF,CAAA;AAxDY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAGkC,+DAAwB;QACtC,gBAAU;GAH9B,YAAY,CAwDxB"} \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/update-user.handler.js b/dist/modules/identity/application/handlers/update-user.handler.js new file mode 100644 index 0000000..573e0d3 --- /dev/null +++ b/dist/modules/identity/application/handlers/update-user.handler.js @@ -0,0 +1,84 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateUserHandler = void 0; +const common_1 = require("@nestjs/common"); +const bcrypt = __importStar(require("bcrypt")); +const user_repository_interface_1 = require("../../domain/repositories/user.repository.interface"); +let UpdateUserHandler = class UpdateUserHandler { + userRepository; + constructor(userRepository) { + this.userRepository = userRepository; + } + async execute(id, dto) { + const user = await this.userRepository.findById(id); + if (!user) { + throw new common_1.NotFoundException('User not found.'); + } + if (dto.email && + dto.email !== user.email) { + const exists = await this.userRepository.existsByEmail(dto.email); + if (exists) { + throw new common_1.ConflictException('Email already exists.'); + } + user.changeEmail(dto.email); + } + if (dto.name) { + user.changeName(dto.name); + } + if (dto.password) { + const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS ?? '10', 10); + const hashedPassword = await bcrypt.hash(dto.password, saltRounds); + user.changePassword(hashedPassword); + } + await this.userRepository.update(user); + return user; + } +}; +exports.UpdateUserHandler = UpdateUserHandler; +exports.UpdateUserHandler = UpdateUserHandler = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [user_repository_interface_1.UserRepository]) +], UpdateUserHandler); +//# sourceMappingURL=update-user.handler.js.map \ No newline at end of file diff --git a/dist/modules/identity/application/handlers/update-user.handler.js.map b/dist/modules/identity/application/handlers/update-user.handler.js.map new file mode 100644 index 0000000..a82601e --- /dev/null +++ b/dist/modules/identity/application/handlers/update-user.handler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-user.handler.js","sourceRoot":"","sources":["../../../../../src/modules/identity/application/handlers/update-user.handler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAIwB;AAExB,+CAAiC;AAEjC,mGAAqF;AAI9E,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAET;IADnB,YACmB,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAC9C,CAAC;IAEJ,KAAK,CAAC,OAAO,CACX,EAAU,EACV,GAAkB;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEpD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,0BAAiB,CAAC,iBAAiB,CAAC,CAAC;QACjD,CAAC;QAED,IACE,GAAG,CAAC,KAAK;YACT,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EACxB,CAAC;YACD,MAAM,MAAM,GACV,MAAM,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAErD,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,0BAAiB,CACzB,uBAAuB,CACxB,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YACb,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACjB,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;YACxE,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YACnE,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;QACtC,CAAC;QAED,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvC,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA7CY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAGwB,0CAAc;GAFtC,iBAAiB,CA6C7B"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/service-account.entity.js b/dist/modules/identity/domain/entities/service-account.entity.js new file mode 100644 index 0000000..6919532 --- /dev/null +++ b/dist/modules/identity/domain/entities/service-account.entity.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ServiceAccount = void 0; +class ServiceAccount { + id; + clientId; + clientSecretHash; + name; + role; + permissions; + status; + metadata; + constructor(id, clientId, clientSecretHash, name, role, permissions, status, metadata) { + this.id = id; + this.clientId = clientId; + this.clientSecretHash = clientSecretHash; + this.name = name; + this.role = role; + this.permissions = permissions; + this.status = status; + this.metadata = metadata; + } + static create(data) { + return new ServiceAccount(crypto.randomUUID(), data.clientId, data.clientSecretHash, data.name, data.role, data.permissions, 'ACTIVE', data.metadata); + } + static restore(data) { + return new ServiceAccount(data.id, data.clientId, data.clientSecretHash, data.name, data.role, data.permissions, data.status, data.metadata); + } + disable() { + this.status = 'DISABLED'; + } + enable() { + this.status = 'ACTIVE'; + } +} +exports.ServiceAccount = ServiceAccount; +//# sourceMappingURL=service-account.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/service-account.entity.js.map b/dist/modules/identity/domain/entities/service-account.entity.js.map new file mode 100644 index 0000000..2ccae0e --- /dev/null +++ b/dist/modules/identity/domain/entities/service-account.entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"service-account.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/service-account.entity.ts"],"names":[],"mappings":";;;AAAA,MAAa,cAAc;IAEP;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IART,YACkB,EAAU,EACnB,QAAgB,EAChB,gBAAwB,EACxB,IAAY,EACZ,IAAY,EACZ,WAAqB,EACrB,MAAc,EACd,QAA8B;QAPrB,OAAE,GAAF,EAAE,CAAQ;QACnB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,qBAAgB,GAAhB,gBAAgB,CAAQ;QACxB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,gBAAW,GAAX,WAAW,CAAU;QACrB,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAsB;IACpC,CAAC;IAEJ,MAAM,CAAC,MAAM,CAAC,IAOb;QACC,OAAO,IAAI,cAAc,CACvB,MAAM,CAAC,UAAU,EAAE,EACnB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,WAAW,EAChB,QAAQ,EACR,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IASd;QACC,OAAO,IAAI,cAAc,CACvB,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;IAC3B,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;IACzB,CAAC;CACF;AA7DD,wCA6DC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/user.entity.js b/dist/modules/identity/domain/entities/user.entity.js new file mode 100644 index 0000000..babebff --- /dev/null +++ b/dist/modules/identity/domain/entities/user.entity.js @@ -0,0 +1,37 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.User = void 0; +class User { + id; + name; + email; + password; + metadata; + constructor(id, name, email, password, metadata) { + this.id = id; + this.name = name; + this.email = email; + this.password = password; + this.metadata = metadata; + } + static create(data) { + return new User(crypto.randomUUID(), data.name, data.email, data.password, data.metadata); + } + delete() { + // Business Rule + } + changeEmail(email) { + this.email = email; + } + changeName(name) { + this.name = name; + } + changePassword(newPassword) { + this.password = newPassword; + } + static restore(data) { + return new User(data.id, data.name, data.email, data.password, data.metadata); + } +} +exports.User = User; +//# sourceMappingURL=user.entity.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/entities/user.entity.js.map b/dist/modules/identity/domain/entities/user.entity.js.map new file mode 100644 index 0000000..13c863e --- /dev/null +++ b/dist/modules/identity/domain/entities/user.entity.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user.entity.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/entities/user.entity.ts"],"names":[],"mappings":";;;AAAA,MAAa,IAAI;IAEG;IACT;IACA;IACA;IACA;IALT,YACkB,EAAU,EACnB,IAAY,EACZ,KAAa,EACb,QAAgB,EAChB,QAA8B;QAJrB,OAAE,GAAF,EAAE,CAAQ;QACnB,SAAI,GAAJ,IAAI,CAAQ;QACZ,UAAK,GAAL,KAAK,CAAQ;QACb,aAAQ,GAAR,QAAQ,CAAQ;QAChB,aAAQ,GAAR,QAAQ,CAAsB;IACpC,CAAC;IAEJ,MAAM,CAAC,MAAM,CAAC,IAKb;QACC,OAAO,IAAI,IAAI,CACb,MAAM,CAAC,UAAU,EAAE,EACnB,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;IAED,MAAM;QACJ,gBAAgB;IAClB,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,cAAc,CAAC,WAAmB;QAChC,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IAMd;QACC,OAAO,IAAI,IAAI,CACb,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;CACF;AAvDD,oBAuDC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-created.event.js b/dist/modules/identity/domain/events/user-created.event.js new file mode 100644 index 0000000..854e0bc --- /dev/null +++ b/dist/modules/identity/domain/events/user-created.event.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserCreatedEvent = void 0; +class UserCreatedEvent { + payload; + occurredAt; + name = 'UserCreated'; + constructor(payload, occurredAt = new Date()) { + this.payload = payload; + this.occurredAt = occurredAt; + } +} +exports.UserCreatedEvent = UserCreatedEvent; +//# sourceMappingURL=user-created.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-created.event.js.map b/dist/modules/identity/domain/events/user-created.event.js.map new file mode 100644 index 0000000..cc198b4 --- /dev/null +++ b/dist/modules/identity/domain/events/user-created.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-created.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-created.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAEC;IAA8B;IADjD,IAAI,GAAG,aAAa,CAAC;IAC9B,YAA4B,OAAY,EAAkB,aAAmB,IAAI,IAAI,EAAE;QAA3D,YAAO,GAAP,OAAO,CAAK;QAAkB,eAAU,GAAV,UAAU,CAAmB;IAAG,CAAC;CAC5F;AAHD,4CAGC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-deleted.event.js b/dist/modules/identity/domain/events/user-deleted.event.js new file mode 100644 index 0000000..c7d247b --- /dev/null +++ b/dist/modules/identity/domain/events/user-deleted.event.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserDeletedEvent = void 0; +class UserDeletedEvent { + payload; + occurredAt; + name = 'UserDeleted'; + constructor(payload, occurredAt = new Date()) { + this.payload = payload; + this.occurredAt = occurredAt; + } +} +exports.UserDeletedEvent = UserDeletedEvent; +//# sourceMappingURL=user-deleted.event.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/events/user-deleted.event.js.map b/dist/modules/identity/domain/events/user-deleted.event.js.map new file mode 100644 index 0000000..f4f552e --- /dev/null +++ b/dist/modules/identity/domain/events/user-deleted.event.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user-deleted.event.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/events/user-deleted.event.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IAEC;IAA8B;IADjD,IAAI,GAAG,aAAa,CAAC;IAC9B,YAA4B,OAAY,EAAkB,aAAmB,IAAI,IAAI,EAAE;QAA3D,YAAO,GAAP,OAAO,CAAK;QAAkB,eAAU,GAAV,UAAU,CAAmB;IAAG,CAAC;CAC5F;AAHD,4CAGC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/service-account.repository.interface.js b/dist/modules/identity/domain/repositories/service-account.repository.interface.js new file mode 100644 index 0000000..cefafed --- /dev/null +++ b/dist/modules/identity/domain/repositories/service-account.repository.interface.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ServiceAccountRepository = void 0; +class ServiceAccountRepository { +} +exports.ServiceAccountRepository = ServiceAccountRepository; +//# sourceMappingURL=service-account.repository.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/service-account.repository.interface.js.map b/dist/modules/identity/domain/repositories/service-account.repository.interface.js.map new file mode 100644 index 0000000..8ae9f53 --- /dev/null +++ b/dist/modules/identity/domain/repositories/service-account.repository.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"service-account.repository.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/service-account.repository.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,wBAAwB;CAQ7C;AARD,4DAQC"} \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/user.repository.interface.js b/dist/modules/identity/domain/repositories/user.repository.interface.js new file mode 100644 index 0000000..81a6ca5 --- /dev/null +++ b/dist/modules/identity/domain/repositories/user.repository.interface.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserRepository = void 0; +class UserRepository { +} +exports.UserRepository = UserRepository; +//# sourceMappingURL=user.repository.interface.js.map \ No newline at end of file diff --git a/dist/modules/identity/domain/repositories/user.repository.interface.js.map b/dist/modules/identity/domain/repositories/user.repository.interface.js.map new file mode 100644 index 0000000..8e52335 --- /dev/null +++ b/dist/modules/identity/domain/repositories/user.repository.interface.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user.repository.interface.js","sourceRoot":"","sources":["../../../../../src/modules/identity/domain/repositories/user.repository.interface.ts"],"names":[],"mappings":";;;AAEA,MAAsB,cAAc;CAgBnC;AAhBD,wCAgBC"} \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js b/dist/modules/identity/identity.module.js new file mode 100644 index 0000000..20e24fd --- /dev/null +++ b/dist/modules/identity/identity.module.js @@ -0,0 +1,75 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IdentityModule = void 0; +const common_1 = require("@nestjs/common"); +const users_controller_1 = require("./presentation/controllers/users.controller"); +const prisma_service_1 = require("../../shared/prisma.service"); +const prisma_user_repository_1 = require("./infrastructure/repositories/prisma-user.repository"); +const user_repository_interface_1 = require("./domain/repositories/user.repository.interface"); +const event_dispatcher_1 = require("../../core/events/event-dispatcher"); +const jwt_auth_guard_1 = require("../../core/auth/guards/jwt-auth.guard"); +const jwt_1 = require("@nestjs/jwt"); +const create_user_handler_1 = require("./application/handlers/create-user.handler"); +const find_user_handler_1 = require("./application/handlers/find-user.handler"); +const delete_user_handler_1 = require("./application/handlers/delete-user.handler"); +const find_users_handler_1 = require("./application/handlers/find-users.handler"); +const update_user_handler_1 = require("./application/handlers/update-user.handler"); +const permission_guard_1 = require("../authorization/presentation/guards/permission.guard"); +const core_1 = require("@nestjs/core"); +const login_handler_1 = require("./application/handlers/login.handler"); +const auth_controller_1 = require("./presentation/controllers/auth.controller"); +const token_handler_1 = require("./application/handlers/token.handler"); +const prisma_service_account_repository_1 = require("./infrastructure/repositories/prisma-service-account.repository"); +const service_account_repository_interface_1 = require("./domain/repositories/service-account.repository.interface"); +let IdentityModule = class IdentityModule { +}; +exports.IdentityModule = IdentityModule; +exports.IdentityModule = IdentityModule = __decorate([ + (0, common_1.Module)({ + imports: [ + jwt_1.JwtModule.register({ + secret: process.env.JWT_SECRET, + signOptions: { + expiresIn: '1d', + }, + }), + ], + controllers: [users_controller_1.UsersController, auth_controller_1.AuthController], + providers: [ + //#region User + create_user_handler_1.CreateUserHandler, + find_user_handler_1.FindUserHandler, + find_users_handler_1.FindUsersHandler, + update_user_handler_1.UpdateUserHandler, + delete_user_handler_1.DeleteUserHandler, + login_handler_1.LoginHandler, + token_handler_1.TokenHandler, + //#endregion + jwt_auth_guard_1.JwtAuthGuard, + permission_guard_1.PermissionGuard, + core_1.Reflector, + prisma_service_1.PrismaService, + prisma_user_repository_1.PrismaUserRepository, + prisma_service_account_repository_1.PrismaServiceAccountRepository, + { + provide: user_repository_interface_1.UserRepository, + useClass: prisma_user_repository_1.PrismaUserRepository, + }, + { + provide: service_account_repository_interface_1.ServiceAccountRepository, + useClass: prisma_service_account_repository_1.PrismaServiceAccountRepository, + }, + { + provide: 'EVENT_DISPATCHER', + useValue: new event_dispatcher_1.EventDispatcher(), + }, + ], + }) +], IdentityModule); +//# sourceMappingURL=identity.module.js.map \ No newline at end of file diff --git a/dist/modules/identity/identity.module.js.map b/dist/modules/identity/identity.module.js.map new file mode 100644 index 0000000..24cc48a --- /dev/null +++ b/dist/modules/identity/identity.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"identity.module.js","sourceRoot":"","sources":["../../../src/modules/identity/identity.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,kFAA8E;AAC9E,gEAA4D;AAC5D,iGAA4F;AAC5F,+FAAiF;AACjF,yEAAqE;AACrE,0EAAqE;AACrE,qCAAwC;AACxC,oFAA+E;AAC/E,gFAA2E;AAC3E,oFAA+E;AAC/E,kFAA6E;AAC7E,oFAA+E;AAC/E,4FAAwF;AACxF,uCAAyC;AACzC,wEAAoE;AACpE,gFAA4E;AAC5E,wEAAoE;AACpE,uHAAiH;AACjH,qHAAsG;AA4C/F,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAzC1B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,eAAS,CAAC,QAAQ,CAAC;gBACjB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;gBAC9B,WAAW,EAAE;oBACX,SAAS,EAAE,IAAI;iBAChB;aACF,CAAC;SACH;QACD,WAAW,EAAE,CAAC,kCAAe,EAAE,gCAAc,CAAC;QAC9C,SAAS,EAAE;YACT,eAAe;YACf,uCAAiB;YACjB,mCAAe;YACf,qCAAgB;YAChB,uCAAiB;YACjB,uCAAiB;YACjB,4BAAY;YACZ,4BAAY;YACZ,YAAY;YAEZ,6BAAY;YACZ,kCAAe;YACf,gBAAS;YACT,8BAAa;YACb,6CAAoB;YACpB,kEAA8B;YAC9B;gBACE,OAAO,EAAE,0CAAc;gBACvB,QAAQ,EAAE,6CAAoB;aAC/B;YACD;gBACE,OAAO,EAAE,+DAAwB;gBACjC,QAAQ,EAAE,kEAA8B;aACzC;YACD;gBACE,OAAO,EAAE,kBAAkB;gBAC3B,QAAQ,EAAE,IAAI,kCAAe,EAAE;aAChC;SACF;KACF,CAAC;GACW,cAAc,CAAG"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js new file mode 100644 index 0000000..3fa0451 --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js @@ -0,0 +1,29 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaUserMapper = void 0; +const user_entity_1 = require("../../domain/entities/user.entity"); +class PrismaUserMapper { + static toDomain(model) { + if (!model) { + return null; + } + return user_entity_1.User.restore({ + id: model.id, + name: model.name, + email: model.email, + password: model.password, + metadata: model.metadata, + }); + } + static toPersistence(user) { + return { + id: user.id, + name: user.name, + email: user.email, + password: user.password, + metadata: user.metadata ?? {}, + }; + } +} +exports.PrismaUserMapper = PrismaUserMapper; +//# sourceMappingURL=prisma-user.mapper.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map new file mode 100644 index 0000000..40c94a8 --- /dev/null +++ b/dist/modules/identity/infrastructure/mappers/prisma-user.mapper.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-user.mapper.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/mappers/prisma-user.mapper.ts"],"names":[],"mappings":";;;AAAA,mEAAyD;AAEzD,MAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,KAAU;QACxB,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,kBAAI,CAAC,OAAO,CAAC;YAClB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,IAAU;QAC7B,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;SAC9B,CAAC;IACJ,CAAC;CACF;AAxBD,4CAwBC"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js new file mode 100644 index 0000000..98ec54c --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js @@ -0,0 +1,109 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaServiceAccountRepository = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const service_account_entity_1 = require("../../domain/entities/service-account.entity"); +let PrismaServiceAccountRepository = class PrismaServiceAccountRepository { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async create(serviceAccount) { + const created = await this.prisma.serviceAccount.create({ + data: { + id: serviceAccount.id, + client_id: serviceAccount.clientId, + client_secret_hash: serviceAccount.clientSecretHash, + name: serviceAccount.name, + role: serviceAccount.role, + permissions: serviceAccount.permissions, + status: serviceAccount.status, + metadata: serviceAccount.metadata ?? {}, + }, + }); + return service_account_entity_1.ServiceAccount.restore({ + id: created.id, + clientId: created.client_id, + clientSecretHash: created.client_secret_hash, + name: created.name, + role: created.role, + permissions: created.permissions, + status: created.status, + metadata: (created.metadata ?? {}), + }); + } + async update(serviceAccount) { + const updated = await this.prisma.serviceAccount.update({ + where: { id: serviceAccount.id }, + data: { + client_id: serviceAccount.clientId, + client_secret_hash: serviceAccount.clientSecretHash, + name: serviceAccount.name, + role: serviceAccount.role, + permissions: serviceAccount.permissions, + status: serviceAccount.status, + metadata: serviceAccount.metadata ?? {}, + }, + }); + return service_account_entity_1.ServiceAccount.restore({ + id: updated.id, + clientId: updated.client_id, + clientSecretHash: updated.client_secret_hash, + name: updated.name, + role: updated.role, + permissions: updated.permissions, + status: updated.status, + metadata: (updated.metadata ?? {}), + }); + } + async findById(id) { + const model = await this.prisma.serviceAccount.findUnique({ + where: { id }, + }); + if (!model) + return null; + return service_account_entity_1.ServiceAccount.restore({ + id: model.id, + clientId: model.client_id, + clientSecretHash: model.client_secret_hash, + name: model.name, + role: model.role, + permissions: model.permissions, + status: model.status, + metadata: (model.metadata ?? {}), + }); + } + async findByClientId(clientId) { + const model = await this.prisma.serviceAccount.findUnique({ + where: { client_id: clientId }, + }); + if (!model) + return null; + return service_account_entity_1.ServiceAccount.restore({ + id: model.id, + clientId: model.client_id, + clientSecretHash: model.client_secret_hash, + name: model.name, + role: model.role, + permissions: model.permissions, + status: model.status, + metadata: (model.metadata ?? {}), + }); + } +}; +exports.PrismaServiceAccountRepository = PrismaServiceAccountRepository; +exports.PrismaServiceAccountRepository = PrismaServiceAccountRepository = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PrismaServiceAccountRepository); +//# sourceMappingURL=prisma-service-account.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js.map new file mode 100644 index 0000000..ea83532 --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-service-account.repository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-service-account.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-service-account.repository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js new file mode 100644 index 0000000..0e94d6e --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js @@ -0,0 +1,76 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaUserRepository = void 0; +const common_1 = require("@nestjs/common"); +const prisma_service_1 = require("../../../../shared/prisma.service"); +const prisma_user_mapper_1 = require("../mappers/prisma-user.mapper"); +let PrismaUserRepository = class PrismaUserRepository { + prisma; + constructor(prisma) { + this.prisma = prisma; + } + async findById(id) { + const user = await this.prisma.user.findUnique({ + where: { id }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(user); + } + async findByEmail(email) { + const user = await this.prisma.user.findUnique({ + where: { email }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(user); + } + async findAll() { + const users = await this.prisma.user.findMany({ + where: { deleted_at: null }, + orderBy: { created_at: 'desc' }, + }); + return users.map(u => prisma_user_mapper_1.PrismaUserMapper.toDomain(u)); + } + async create(user) { + const created = await this.prisma.user.create({ + data: prisma_user_mapper_1.PrismaUserMapper.toPersistence(user), + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(created); + } + async update(user) { + const updated = await this.prisma.user.update({ + where: { + id: user.id, + }, + data: prisma_user_mapper_1.PrismaUserMapper.toPersistence(user), + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(updated); + } + async delete(id) { + const user = await this.prisma.user.update({ + where: { id }, + data: { deleted_at: new Date() }, + }); + return prisma_user_mapper_1.PrismaUserMapper.toDomain(user); + } + async existsByEmail(email, excludeId) { + const where = { email }; + if (excludeId) { + where.id = { not: excludeId }; + } + const user = await this.prisma.user.findFirst({ where }); + return !!user; + } +}; +exports.PrismaUserRepository = PrismaUserRepository; +exports.PrismaUserRepository = PrismaUserRepository = __decorate([ + (0, common_1.Injectable)(), + __metadata("design:paramtypes", [prisma_service_1.PrismaService]) +], PrismaUserRepository); +//# sourceMappingURL=prisma-user.repository.js.map \ No newline at end of file diff --git a/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map new file mode 100644 index 0000000..acbe756 --- /dev/null +++ b/dist/modules/identity/infrastructure/repositories/prisma-user.repository.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma-user.repository.js","sourceRoot":"","sources":["../../../../../src/modules/identity/infrastructure/repositories/prisma-user.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,sEAAkE;AAGlE,sEAAiE;AAG1D,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACF;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAEtD,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE;SACd,CAAC,CAAC;QAEH,OAAO,qCAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa;QAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAC7C,KAAK,EAAE,EAAE,KAAK,EAAE;SACjB,CAAC,CAAC;QAEH,OAAO,qCAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC5C,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;YAC3B,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;SAChC,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,qCAAgB,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAU;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5C,IAAI,EAAE,qCAAgB,CAAC,aAAa,CAAC,IAAI,CAAC;SAC3C,CAAC,CAAC;QAEH,OAAO,qCAAgB,CAAC,QAAQ,CAAC,OAAO,CAAE,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAU;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5C,KAAK,EAAE;gBACL,EAAE,EAAE,IAAI,CAAC,EAAE;aACZ;YACD,IAAI,EAAE,qCAAgB,CAAC,aAAa,CAAC,IAAI,CAAC;SAC3C,CAAC,CAAC;QAEH,OAAO,qCAAgB,CAAC,QAAQ,CAAC,OAAO,CAAE,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YACzC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE;SACjC,CAAC,CAAC;QACH,OAAO,qCAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,SAAyB;QAC1D,MAAM,KAAK,GAAQ,EAAE,KAAK,EAAE,CAAC;QAC7B,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QAChC,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,CAAC,IAAI,CAAC;IAChB,CAAC;CACF,CAAA;AAhEY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAE0B,8BAAa;GADvC,oBAAoB,CAgEhC"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/auth.controller.js b/dist/modules/identity/presentation/controllers/auth.controller.js new file mode 100644 index 0000000..3cd519e --- /dev/null +++ b/dist/modules/identity/presentation/controllers/auth.controller.js @@ -0,0 +1,68 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuthController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const login_dto_1 = require("../dto/login.dto"); +const login_handler_1 = require("../../application/handlers/login.handler"); +const token_handler_1 = require("../../application/handlers/token.handler"); +let AuthController = class AuthController { + loginHandler; + tokenHandler; + constructor(loginHandler, tokenHandler) { + this.loginHandler = loginHandler; + this.tokenHandler = tokenHandler; + } + async login(dto) { + const data = await this.loginHandler.execute(dto); + return { + success: true, + data, + meta: {}, + }; + } + async token(body) { + const data = await this.tokenHandler.execute(body); + return { + success: true, + data, + meta: {}, + }; + } +}; +exports.AuthController = AuthController; +__decorate([ + (0, common_1.Post)('login'), + (0, swagger_1.ApiOperation)({ summary: 'Login and obtain access token' }), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [login_dto_1.LoginDto]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "login", null); +__decorate([ + (0, common_1.Post)('token'), + (0, swagger_1.ApiOperation)({ summary: 'Obtain access token using client credentials' }), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], AuthController.prototype, "token", null); +exports.AuthController = AuthController = __decorate([ + (0, swagger_1.ApiTags)('Auth'), + (0, common_1.Controller)('api/v1/auth'), + __metadata("design:paramtypes", [login_handler_1.LoginHandler, + token_handler_1.TokenHandler]) +], AuthController); +//# sourceMappingURL=auth.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/auth.controller.js.map b/dist/modules/identity/presentation/controllers/auth.controller.js.map new file mode 100644 index 0000000..27c7664 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/auth.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/auth.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAwD;AACxD,6CAAwD;AAExD,gDAA4C;AAC5C,4EAAwE;AACxE,4EAAwE;AAIjE,IAAM,cAAc,GAApB,MAAM,cAAc;IAEN;IACA;IAFnB,YACmB,YAA0B,EAC1B,YAA0B;QAD1B,iBAAY,GAAZ,YAAY,CAAc;QAC1B,iBAAY,GAAZ,YAAY,CAAc;IAC1C,CAAC;IAIE,AAAN,KAAK,CAAC,KAAK,CAAS,GAAa;QAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAElD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI;YACJ,IAAI,EAAE,EAAE;SACT,CAAC;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,KAAK,CAAS,IAAS;QAC3B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAEnD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI;YACJ,IAAI,EAAE,EAAE;SACT,CAAC;IACJ,CAAC;CACF,CAAA;AA7BY,wCAAc;AAQnB;IAFL,IAAA,aAAI,EAAC,OAAO,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC;IAC9C,WAAA,IAAA,aAAI,GAAE,CAAA;;qCAAM,oBAAQ;;2CAQhC;AAIK;IAFL,IAAA,aAAI,EAAC,OAAO,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,8CAA8C,EAAE,CAAC;IAC7D,WAAA,IAAA,aAAI,GAAE,CAAA;;;;2CAQlB;yBA5BU,cAAc;IAF1B,IAAA,iBAAO,EAAC,MAAM,CAAC;IACf,IAAA,mBAAU,EAAC,aAAa,CAAC;qCAGS,4BAAY;QACZ,4BAAY;GAHlC,cAAc,CA6B1B"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/users.controller.js b/dist/modules/identity/presentation/controllers/users.controller.js new file mode 100644 index 0000000..9ce27b3 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/users.controller.js @@ -0,0 +1,150 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UsersController = void 0; +const common_1 = require("@nestjs/common"); +const swagger_1 = require("@nestjs/swagger"); +const jwt_auth_guard_1 = require("../../../../core/auth/guards/jwt-auth.guard"); +const permission_guard_1 = require("../../../authorization/presentation/guards/permission.guard"); +const permission_decorator_1 = require("../../../authorization/presentation/decorators/permission.decorator"); +const find_user_handler_1 = require("../../application/handlers/find-user.handler"); +const find_users_handler_1 = require("../../application/handlers/find-users.handler"); +const create_user_handler_1 = require("../../application/handlers/create-user.handler"); +const update_user_handler_1 = require("../../application/handlers/update-user.handler"); +const delete_user_handler_1 = require("../../application/handlers/delete-user.handler"); +const create_user_dto_1 = require("../dto/create-user.dto"); +const update_user_dto_1 = require("../dto/update-user.dto"); +let UsersController = class UsersController { + createUserHandler; + findUserHandler; + findUsersHandler; + updateUserHandler; + deleteUserHandler; + constructor(createUserHandler, findUserHandler, findUsersHandler, updateUserHandler, deleteUserHandler) { + this.createUserHandler = createUserHandler; + this.findUserHandler = findUserHandler; + this.findUsersHandler = findUsersHandler; + this.updateUserHandler = updateUserHandler; + this.deleteUserHandler = deleteUserHandler; + } + sanitizeUser(user) { + if (!user) + return user; + if (Array.isArray(user)) { + return user.map(u => { + const { password, ...rest } = u; + return rest; + }); + } + const { password, ...rest } = user; + return rest; + } + async create(dto) { + const data = await this.createUserHandler.execute(dto); + return { + success: true, + data: this.sanitizeUser(data), + meta: {}, + }; + } + async getById(id) { + const data = await this.findUserHandler.execute(id); + return { + success: true, + data: this.sanitizeUser(data), + meta: {}, + }; + } + async getAll() { + const data = await this.findUsersHandler.execute(); + return { + success: true, + data: this.sanitizeUser(data), + meta: {}, + }; + } + async update(id, dto) { + const data = await this.updateUserHandler.execute(id, dto); + return { + success: true, + data: this.sanitizeUser(data), + meta: {}, + }; + } + async delete(id) { + await this.deleteUserHandler.execute(id); + return { + success: true, + data: null, + meta: {}, + }; + } +}; +exports.UsersController = UsersController; +__decorate([ + (0, common_1.Post)(), + (0, permission_decorator_1.Permissions)('USER_CREATE'), + (0, swagger_1.ApiOperation)({ summary: 'Create user' }), + __param(0, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [create_user_dto_1.CreateUserDto]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "create", null); +__decorate([ + (0, common_1.Get)(':id'), + (0, permission_decorator_1.Permissions)('USER_READ'), + (0, swagger_1.ApiOperation)({ summary: 'Get user by id' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "getById", null); +__decorate([ + (0, common_1.Get)(), + (0, permission_decorator_1.Permissions)('USER_READ_ADMIN'), + (0, swagger_1.ApiOperation)({ summary: 'Get All User' }), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", Promise) +], UsersController.prototype, "getAll", null); +__decorate([ + (0, common_1.Patch)(':id'), + (0, permission_decorator_1.Permissions)('USER_UPDATE'), + (0, swagger_1.ApiOperation)({ summary: 'Update User' }), + __param(0, (0, common_1.Param)('id')), + __param(1, (0, common_1.Body)()), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String, update_user_dto_1.UpdateUserDto]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "update", null); +__decorate([ + (0, common_1.Delete)(':id'), + (0, permission_decorator_1.Permissions)('USER_DELETE'), + (0, swagger_1.ApiOperation)({ summary: 'Delete user by id' }), + __param(0, (0, common_1.Param)('id')), + __metadata("design:type", Function), + __metadata("design:paramtypes", [String]), + __metadata("design:returntype", Promise) +], UsersController.prototype, "delete", null); +exports.UsersController = UsersController = __decorate([ + (0, swagger_1.ApiTags)('Users'), + (0, common_1.Controller)('api/v1/users'), + (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, permission_guard_1.PermissionGuard), + __metadata("design:paramtypes", [create_user_handler_1.CreateUserHandler, + find_user_handler_1.FindUserHandler, + find_users_handler_1.FindUsersHandler, + update_user_handler_1.UpdateUserHandler, + delete_user_handler_1.DeleteUserHandler]) +], UsersController); +//# sourceMappingURL=users.controller.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/controllers/users.controller.js.map b/dist/modules/identity/presentation/controllers/users.controller.js.map new file mode 100644 index 0000000..2a2a606 --- /dev/null +++ b/dist/modules/identity/presentation/controllers/users.controller.js.map @@ -0,0 +1 @@ +{"version":3,"file":"users.controller.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/controllers/users.controller.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-user.dto.js b/dist/modules/identity/presentation/dto/create-user.dto.js new file mode 100644 index 0000000..0c3486e --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-user.dto.js @@ -0,0 +1,43 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CreateUserDto = void 0; +const class_validator_1 = require("class-validator"); +const swagger_1 = require("@nestjs/swagger"); +class CreateUserDto { + name; + email; + password; + metadata; +} +exports.CreateUserDto = CreateUserDto; +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'John Doe' }), + (0, class_validator_1.IsString)(), + (0, class_validator_1.IsNotEmpty)(), + __metadata("design:type", String) +], CreateUserDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'user@example.com' }), + (0, class_validator_1.IsEmail)(), + __metadata("design:type", String) +], CreateUserDto.prototype, "email", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'strongpassword' }), + (0, class_validator_1.IsString)(), + (0, class_validator_1.Length)(8, 128), + __metadata("design:type", String) +], CreateUserDto.prototype, "password", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ required: false }), + __metadata("design:type", Object) +], CreateUserDto.prototype, "metadata", void 0); +//# sourceMappingURL=create-user.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/create-user.dto.js.map b/dist/modules/identity/presentation/dto/create-user.dto.js.map new file mode 100644 index 0000000..b6f5707 --- /dev/null +++ b/dist/modules/identity/presentation/dto/create-user.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"create-user.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/create-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAwE;AACxE,6CAA8C;AAE9C,MAAa,aAAa;IAIxB,IAAI,CAAU;IAId,KAAK,CAAU;IAKf,QAAQ,CAAU;IAGlB,QAAQ,CAAuB;CAChC;AAjBD,sCAiBC;AAbC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACpC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,GAAE;;2CACC;AAId;IAFC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,GAAE;;4CACK;AAKf;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;+CACG;AAGlB;IADC,IAAA,qBAAW,EAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;;+CACF"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/login.dto.js b/dist/modules/identity/presentation/dto/login.dto.js new file mode 100644 index 0000000..34aa65f --- /dev/null +++ b/dist/modules/identity/presentation/dto/login.dto.js @@ -0,0 +1,31 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoginDto = void 0; +const class_validator_1 = require("class-validator"); +const swagger_1 = require("@nestjs/swagger"); +class LoginDto { + email; + password; +} +exports.LoginDto = LoginDto; +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'user@example.com' }), + (0, class_validator_1.IsEmail)(), + __metadata("design:type", String) +], LoginDto.prototype, "email", void 0); +__decorate([ + (0, swagger_1.ApiProperty)({ example: 'strongpassword' }), + (0, class_validator_1.IsString)(), + (0, class_validator_1.Length)(8, 128), + __metadata("design:type", String) +], LoginDto.prototype, "password", void 0); +//# sourceMappingURL=login.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/login.dto.js.map b/dist/modules/identity/presentation/dto/login.dto.js.map new file mode 100644 index 0000000..414a842 --- /dev/null +++ b/dist/modules/identity/presentation/dto/login.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"login.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/login.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAA4D;AAC5D,6CAA8C;AAE9C,MAAa,QAAQ;IAGnB,KAAK,CAAU;IAKf,QAAQ,CAAU;CACnB;AATD,4BASC;AANC;IAFC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,GAAE;;uCACK;AAKf;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;0CACG"} \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-user.dto.js b/dist/modules/identity/presentation/dto/update-user.dto.js new file mode 100644 index 0000000..1a12e5c --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-user.dto.js @@ -0,0 +1,47 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UpdateUserDto = void 0; +const mapped_types_1 = require("@nestjs/mapped-types"); +const create_user_dto_1 = require("./create-user.dto"); +const swagger_1 = require("@nestjs/swagger"); +const class_validator_1 = require("class-validator"); +class UpdateUserDto extends (0, mapped_types_1.PartialType)(create_user_dto_1.CreateUserDto) { + name; + email; + password; + metadata; +} +exports.UpdateUserDto = UpdateUserDto; +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsString)(), + __metadata("design:type", String) +], UpdateUserDto.prototype, "name", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.IsEmail)(), + __metadata("design:type", String) +], UpdateUserDto.prototype, "email", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + (0, class_validator_1.Length)(8, 128), + __metadata("design:type", String) +], UpdateUserDto.prototype, "password", void 0); +__decorate([ + (0, swagger_1.ApiPropertyOptional)(), + (0, class_validator_1.IsOptional)(), + __metadata("design:type", Object) +], UpdateUserDto.prototype, "metadata", void 0); +//# sourceMappingURL=update-user.dto.js.map \ No newline at end of file diff --git a/dist/modules/identity/presentation/dto/update-user.dto.js.map b/dist/modules/identity/presentation/dto/update-user.dto.js.map new file mode 100644 index 0000000..1d3d581 --- /dev/null +++ b/dist/modules/identity/presentation/dto/update-user.dto.js.map @@ -0,0 +1 @@ +{"version":3,"file":"update-user.dto.js","sourceRoot":"","sources":["../../../../../src/modules/identity/presentation/dto/update-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uDAAmD;AACnD,uDAAkD;AAClD,6CAAsD;AACtD,qDAAwE;AAExE,MAAa,aAAc,SAAQ,IAAA,0BAAW,EAAC,+BAAa,CAAC;IAI3D,IAAI,CAAU;IAKd,KAAK,CAAU;IAKf,QAAQ,CAAU;IAIlB,QAAQ,CAAuB;CAChC;AAnBD,sCAmBC;AAfC;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2CACG;AAKd;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;;4CACK;AAKf;IAHC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,CAAC,EAAE,GAAG,CAAC;;+CACG;AAIlB;IAFC,IAAA,6BAAmB,GAAE;IACrB,IAAA,4BAAU,GAAE;;+CACkB"} \ No newline at end of file diff --git a/dist/shared/prisma.service.js b/dist/shared/prisma.service.js new file mode 100644 index 0000000..a5f7bec --- /dev/null +++ b/dist/shared/prisma.service.js @@ -0,0 +1,24 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PrismaService = void 0; +const common_1 = require("@nestjs/common"); +const client_1 = require("@prisma/client"); +let PrismaService = class PrismaService extends client_1.PrismaClient { + async onModuleInit() { + await this.$connect(); + } + async onModuleDestroy() { + await this.$disconnect(); + } +}; +exports.PrismaService = PrismaService; +exports.PrismaService = PrismaService = __decorate([ + (0, common_1.Injectable)() +], PrismaService); +//# sourceMappingURL=prisma.service.js.map \ No newline at end of file diff --git a/dist/shared/prisma.service.js.map b/dist/shared/prisma.service.js.map new file mode 100644 index 0000000..0776229 --- /dev/null +++ b/dist/shared/prisma.service.js.map @@ -0,0 +1 @@ +{"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../../src/shared/prisma.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA2E;AAC3E,2CAA8C;AAGvC,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAY;IAC7C,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AARY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAQzB"} \ No newline at end of file diff --git a/dist/shared/utils/index.js b/dist/shared/utils/index.js new file mode 100644 index 0000000..0dc1a78 --- /dev/null +++ b/dist/shared/utils/index.js @@ -0,0 +1,5 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.noop = noop; +function noop() { } +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/shared/utils/index.js.map b/dist/shared/utils/index.js.map new file mode 100644 index 0000000..a39412e --- /dev/null +++ b/dist/shared/utils/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/utils/index.ts"],"names":[],"mappings":";;AAAA,oBAAyB;AAAzB,SAAgB,IAAI,KAAI,CAAC"} \ No newline at end of file diff --git a/jest.config.cjs b/jest.config.cjs new file mode 100644 index 0000000..5cdaf29 --- /dev/null +++ b/jest.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'], + moduleFileExtensions: ['ts', 'js', 'json', 'node'], +}; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 19f49c7..8483a28 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -21,3 +21,17 @@ model User { updated_at DateTime @updatedAt deleted_at DateTime? } + +model ServiceAccount { + id String @id @default(uuid()) + client_id String @unique + client_secret_hash String + name String + role String + permissions Json + status String @default("ACTIVE") + metadata Json? + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + deleted_at DateTime? +} diff --git a/prisma/seed.ts b/prisma/seed.ts index 6107de9..7ee0f78 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1 +1,44 @@ -// prisma seed placeholder +import { PrismaClient } from '@prisma/client'; +import * as bcrypt from 'bcrypt'; +import crypto from 'crypto'; + +const prisma = new PrismaClient(); + +async function main() { + const clientId = 'raylab-telegram-bot'; + const clientSecret = crypto.randomBytes(32).toString('hex'); + const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS ?? '10', 10); + const clientSecretHash = await bcrypt.hash(clientSecret, saltRounds); + + // Create service account only if not exists + const existing = await prisma.serviceAccount.findUnique({ where: { client_id: clientId } }); + + if (!existing) { + const sa = await prisma.serviceAccount.create({ + data: { + client_id: clientId, + client_secret_hash: clientSecretHash, + name: 'Telegram Bot', + role: 'BOT', + permissions: ['DEBT_CREATE', 'DEBT_READ', 'DEBT_SETTLE'], + status: 'ACTIVE', + }, + }); + + console.log('Service account created for development/testing.'); + console.log('Client ID:', clientId); + console.log('Client Secret (one-time):', clientSecret); + console.log('Please store the client secret securely.'); + } else { + console.log('Service account already exists.'); + } +} + +main() + .catch(e => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/src/adapters/notification/core-api-client.ts b/src/adapters/notification/core-api-client.ts new file mode 100644 index 0000000..7c854c2 --- /dev/null +++ b/src/adapters/notification/core-api-client.ts @@ -0,0 +1,149 @@ +export type TokenResponse = { + access_token: string; + token_type: string; + expires_in: number; +}; + +export class CoreApiClientError extends Error { + public status?: number; + constructor(message: string, status?: number) { + super(message); + this.name = 'CoreApiClientError'; + this.status = status; + } +} + +export class CoreApiClient { + private apiUrl: string; + private clientId: string; + private clientSecret: string; + private timeoutMs: number; + + private cachedToken: string | null = null; + private tokenExpiry: number = 0; // epoch ms + private tokenMarginSec = 60; // safety margin in seconds + + constructor(options?: { apiUrl?: string; clientId?: string; clientSecret?: string; timeoutMs?: number }) { + this.apiUrl = options?.apiUrl ?? process.env.RAYLAB_CORE_API_URL ?? 'http://localhost:3000'; + this.clientId = options?.clientId ?? process.env.RAYLAB_BOT_CLIENT_ID ?? ''; + this.clientSecret = options?.clientSecret ?? process.env.RAYLAB_BOT_CLIENT_SECRET ?? ''; + this.timeoutMs = options?.timeoutMs ?? 5000; + + if (!this.clientId || !this.clientSecret) { + // keep construction allowed but operations will fail with clear errors + } + } + + private async fetchWithTimeout(url: string, init: RequestInit) { + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), this.timeoutMs); + try { + const res = await fetch(url, { ...init, signal: controller.signal }); + return res; + } finally { + clearTimeout(id); + } + } + + private isTokenValid() { + return !!this.cachedToken && Date.now() < this.tokenExpiry; + } + + public async requestAccessToken(): Promise { + if (!this.clientId || !this.clientSecret) { + throw new CoreApiClientError('Missing client credentials'); + } + + const url = `${this.apiUrl.replace(/\/$/, '')}/api/v1/auth/token`; + + const body = JSON.stringify({ client_id: this.clientId, client_secret: this.clientSecret, grant_type: 'client_credentials' }); + + let res: Response; + try { + res = await this.fetchWithTimeout(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body, + }); + } catch (err: any) { + if (err.name === 'AbortError') { + throw new CoreApiClientError('Request timeout'); + } + throw new CoreApiClientError('Network error'); + } + + if (res.status === 401) { + throw new CoreApiClientError('Invalid client credentials', 401); + } + + if (res.status >= 400) { + throw new CoreApiClientError(`Token request failed with status ${res.status}`, res.status); + } + + const payload = await res.json(); + // Expecting { success: true, data: { access_token, token_type, expires_in }, ... } + const data = payload?.data; + if (!data || !data.access_token) { + throw new CoreApiClientError('Invalid token response'); + } + + // Cache token + this.cachedToken = data.access_token; + const expiresIn = typeof data.expires_in === 'number' ? data.expires_in : parseInt(data.expires_in, 10) || 3600; + const effective = Math.max(0, expiresIn - this.tokenMarginSec); + this.tokenExpiry = Date.now() + effective * 1000; + + return { access_token: data.access_token, token_type: data.token_type ?? 'Bearer', expires_in: expiresIn }; + } + + public async getToken(): Promise { + if (this.isTokenValid()) { + return this.cachedToken as string; + } + + const tokenResp = await this.requestAccessToken(); + return tokenResp.access_token; + } + + public async authenticatedRequest(input: RequestInfo, init?: RequestInit, retry = true): Promise { + const token = await this.getToken(); + + const headers = new Headers(init?.headers as HeadersInit); + headers.set('Authorization', `Bearer ${token}`); + headers.set('Accept', 'application/json'); + + let res: Response; + try { + res = await this.fetchWithTimeout(typeof input === 'string' ? input : (input as Request).url, { + ...init, + headers, + }); + } catch (err: any) { + if (err.name === 'AbortError') { + throw new CoreApiClientError('Request timeout'); + } + throw new CoreApiClientError('Network error'); + } + + if (res.status === 401 && retry) { + // invalidate token and retry once + this.cachedToken = null; + this.tokenExpiry = 0; + try { + const newToken = await this.getToken(); + const headers2 = new Headers(init?.headers as HeadersInit); + headers2.set('Authorization', `Bearer ${newToken}`); + headers2.set('Accept', 'application/json'); + const res2 = await this.fetchWithTimeout(typeof input === 'string' ? input : (input as Request).url, { + ...init, + headers: headers2, + }); + return res2; + } catch (e) { + throw e; + } + } + + return res; + } +} diff --git a/src/modules/identity/application/handlers/create-user.handler.ts b/src/modules/identity/application/handlers/create-user.handler.ts index 58a314b..b2eb623 100644 --- a/src/modules/identity/application/handlers/create-user.handler.ts +++ b/src/modules/identity/application/handlers/create-user.handler.ts @@ -3,6 +3,7 @@ import { ConflictException, } from '@nestjs/common'; +import * as bcrypt from 'bcrypt'; import { CreateUserDto } from '../../presentation/dto/create-user.dto'; import { User } from '../../domain/entities/user.entity'; import { UserRepository } from '../../domain/repositories/user.repository.interface'; @@ -20,10 +21,13 @@ export class CreateUserHandler { throw new ConflictException('Email already exists.'); } + const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS ?? '10', 10); + const hashedPassword = await bcrypt.hash(dto.password, saltRounds); + const user = User.create({ name: dto.name, email: dto.email, - password: dto.password, + password: hashedPassword, metadata: dto.metadata, }); diff --git a/src/modules/identity/application/handlers/login.handler.ts b/src/modules/identity/application/handlers/login.handler.ts new file mode 100644 index 0000000..e97b4f2 --- /dev/null +++ b/src/modules/identity/application/handlers/login.handler.ts @@ -0,0 +1,57 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import * as bcrypt from 'bcrypt'; +import { JwtService } from '@nestjs/jwt'; + +import { LoginDto } from '../../presentation/dto/login.dto'; +import { UserRepository } from '../../domain/repositories/user.repository.interface'; + +@Injectable() +export class LoginHandler { + constructor( + private readonly userRepository: UserRepository, + private readonly jwtService: JwtService, + ) {} + + async execute(dto: LoginDto) { + const user = await this.userRepository.findByEmail(dto.email); + + // Generic error message for security + const unauthorizedMsg = 'Invalid credentials.'; + + if (!user) { + throw new UnauthorizedException(unauthorizedMsg); + } + + const isMatch = await bcrypt.compare(dto.password, user.password); + + if (!isMatch) { + throw new UnauthorizedException(unauthorizedMsg); + } + + // Build payload according to existing JwtPayload interface + const metadata = user.metadata ?? {}; + + const role = typeof metadata.role === 'string' ? metadata.role : 'USER'; + + const permissions = Array.isArray(metadata.permissions) ? metadata.permissions : []; + + const payload = { + sub: user.id, + email: user.email, + role, + permissions, + }; + + const accessToken = await this.jwtService.signAsync(payload); + + return { + access_token: accessToken, + expires_in: '1d', + user: { + id: user.id, + name: user.name, + email: user.email, + }, + }; + } +} diff --git a/src/modules/identity/application/handlers/token.handler.ts b/src/modules/identity/application/handlers/token.handler.ts new file mode 100644 index 0000000..1655d36 --- /dev/null +++ b/src/modules/identity/application/handlers/token.handler.ts @@ -0,0 +1,63 @@ +import { Injectable, BadRequestException, UnauthorizedException } from '@nestjs/common'; +import * as bcrypt from 'bcrypt'; +import { JwtService } from '@nestjs/jwt'; +import { ServiceAccountRepository } from '../../domain/repositories/service-account.repository.interface'; + +@Injectable() +export class TokenHandler { + constructor( + private readonly serviceAccountRepository: ServiceAccountRepository, + private readonly jwtService: JwtService, + ) {} + + async execute(body: { client_id?: string; client_secret?: string; grant_type?: string }) { + const { client_id, client_secret, grant_type } = body; + + if (grant_type !== 'client_credentials') { + throw new BadRequestException('Invalid grant_type.'); + } + + const unauthorizedMsg = 'Invalid client credentials.'; + + if (!client_id || !client_secret) { + throw new UnauthorizedException(unauthorizedMsg); + } + + const serviceAccount = await this.serviceAccountRepository.findByClientId(client_id); + + if (!serviceAccount) { + // generic error + throw new UnauthorizedException(unauthorizedMsg); + } + + // check status + if (serviceAccount.status !== 'ACTIVE') { + throw new UnauthorizedException(unauthorizedMsg); + } + + const match = await bcrypt.compare(client_secret, serviceAccount.clientSecretHash); + + if (!match) { + throw new UnauthorizedException(unauthorizedMsg); + } + + const payload: any = { + sub: serviceAccount.id, + client_id: serviceAccount.clientId, + // existing JwtPayload expects email; provide empty string to remain compatible + email: '', + role: serviceAccount.role, + permissions: serviceAccount.permissions ?? [], + }; + + const accessToken = await this.jwtService.signAsync(payload, { + expiresIn: '1h', + }); + + return { + access_token: accessToken, + token_type: 'Bearer', + expires_in: 3600, + }; + } +} diff --git a/src/modules/identity/application/handlers/update-user.handler.ts b/src/modules/identity/application/handlers/update-user.handler.ts index d4e04d6..e29afb7 100644 --- a/src/modules/identity/application/handlers/update-user.handler.ts +++ b/src/modules/identity/application/handlers/update-user.handler.ts @@ -4,6 +4,7 @@ import { NotFoundException, } from '@nestjs/common'; +import * as bcrypt from 'bcrypt'; import { UpdateUserDto } from '../../presentation/dto/update-user.dto'; import { UserRepository } from '../../domain/repositories/user.repository.interface'; import { User } from '../../domain/entities/user.entity'; @@ -44,6 +45,12 @@ export class UpdateUserHandler { user.changeName(dto.name); } + if (dto.password) { + const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS ?? '10', 10); + const hashedPassword = await bcrypt.hash(dto.password, saltRounds); + user.changePassword(hashedPassword); + } + await this.userRepository.update(user); return user; diff --git a/src/modules/identity/domain/entities/service-account.entity.ts b/src/modules/identity/domain/entities/service-account.entity.ts new file mode 100644 index 0000000..5b3d410 --- /dev/null +++ b/src/modules/identity/domain/entities/service-account.entity.ts @@ -0,0 +1,62 @@ +export class ServiceAccount { + private constructor( + public readonly id: string, + public clientId: string, + public clientSecretHash: string, + public name: string, + public role: string, + public permissions: string[], + public status: string, + public metadata?: Record, + ) {} + + static create(data: { + clientId: string; + clientSecretHash: string; + name: string; + role: string; + permissions: string[]; + metadata?: Record; + }): ServiceAccount { + return new ServiceAccount( + crypto.randomUUID(), + data.clientId, + data.clientSecretHash, + data.name, + data.role, + data.permissions, + 'ACTIVE', + data.metadata, + ); + } + + static restore(data: { + id: string; + clientId: string; + clientSecretHash: string; + name: string; + role: string; + permissions: string[]; + status: string; + metadata?: Record; + }): ServiceAccount { + return new ServiceAccount( + data.id, + data.clientId, + data.clientSecretHash, + data.name, + data.role, + data.permissions, + data.status, + data.metadata, + ); + } + + disable() { + this.status = 'DISABLED'; + } + + enable() { + this.status = 'ACTIVE'; + } +} diff --git a/src/modules/identity/domain/entities/user.entity.ts b/src/modules/identity/domain/entities/user.entity.ts index e1a4538..4ee0de5 100644 --- a/src/modules/identity/domain/entities/user.entity.ts +++ b/src/modules/identity/domain/entities/user.entity.ts @@ -34,6 +34,10 @@ export class User { this.name = name; } + changePassword(newPassword: string) { + this.password = newPassword; + } + static restore(data: { id: string; name: string; diff --git a/src/modules/identity/domain/repositories/service-account.repository.interface.ts b/src/modules/identity/domain/repositories/service-account.repository.interface.ts new file mode 100644 index 0000000..2f24c73 --- /dev/null +++ b/src/modules/identity/domain/repositories/service-account.repository.interface.ts @@ -0,0 +1,11 @@ +import { ServiceAccount } from '../entities/service-account.entity'; + +export abstract class ServiceAccountRepository { + abstract create(serviceAccount: ServiceAccount): Promise; + + abstract update(serviceAccount: ServiceAccount): Promise; + + abstract findById(id: string): Promise; + + abstract findByClientId(clientId: string): Promise; +} diff --git a/src/modules/identity/domain/repositories/user.repository.interface.ts b/src/modules/identity/domain/repositories/user.repository.interface.ts index f76c4a8..197cb75 100644 --- a/src/modules/identity/domain/repositories/user.repository.interface.ts +++ b/src/modules/identity/domain/repositories/user.repository.interface.ts @@ -9,6 +9,8 @@ export abstract class UserRepository { id: string, ): Promise; + abstract findByEmail(email: string): Promise; + abstract findAll(): Promise; abstract existsByEmail( diff --git a/src/modules/identity/identity.module.ts b/src/modules/identity/identity.module.ts index 9074380..d00633e 100644 --- a/src/modules/identity/identity.module.ts +++ b/src/modules/identity/identity.module.ts @@ -13,6 +13,11 @@ import { FindUsersHandler } from './application/handlers/find-users.handler'; import { UpdateUserHandler } from './application/handlers/update-user.handler'; import { PermissionGuard } from '../authorization/presentation/guards/permission.guard'; import { Reflector } from '@nestjs/core'; +import { LoginHandler } from './application/handlers/login.handler'; +import { AuthController } from './presentation/controllers/auth.controller'; +import { TokenHandler } from './application/handlers/token.handler'; +import { PrismaServiceAccountRepository } from './infrastructure/repositories/prisma-service-account.repository'; +import { ServiceAccountRepository } from './domain/repositories/service-account.repository.interface'; @Module({ @@ -24,7 +29,7 @@ import { Reflector } from '@nestjs/core'; }, }), ], - controllers: [UsersController], + controllers: [UsersController, AuthController], providers: [ //#region User CreateUserHandler, @@ -32,6 +37,8 @@ import { Reflector } from '@nestjs/core'; FindUsersHandler, UpdateUserHandler, DeleteUserHandler, + LoginHandler, + TokenHandler, //#endregion JwtAuthGuard, @@ -39,10 +46,15 @@ import { Reflector } from '@nestjs/core'; Reflector, PrismaService, PrismaUserRepository, + PrismaServiceAccountRepository, { provide: UserRepository, useClass: PrismaUserRepository, }, + { + provide: ServiceAccountRepository, + useClass: PrismaServiceAccountRepository, + }, { provide: 'EVENT_DISPATCHER', useValue: new EventDispatcher(), diff --git a/src/modules/identity/infrastructure/repositories/prisma-service-account.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-service-account.repository.ts new file mode 100644 index 0000000..ec026f5 --- /dev/null +++ b/src/modules/identity/infrastructure/repositories/prisma-service-account.repository.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../../../shared/prisma.service'; +import { ServiceAccountRepository } from '../../domain/repositories/service-account.repository.interface'; +import { ServiceAccount } from '../../domain/entities/service-account.entity'; + +@Injectable() +export class PrismaServiceAccountRepository implements ServiceAccountRepository { + constructor(private readonly prisma: PrismaService) {} + + async create(serviceAccount: ServiceAccount): Promise { + const created = await this.prisma.serviceAccount.create({ + data: { + id: serviceAccount.id, + client_id: serviceAccount.clientId, + client_secret_hash: serviceAccount.clientSecretHash, + name: serviceAccount.name, + role: serviceAccount.role, + permissions: serviceAccount.permissions, + status: serviceAccount.status, + metadata: serviceAccount.metadata ?? {}, + }, + }); + + return ServiceAccount.restore({ + id: created.id, + clientId: created.client_id, + clientSecretHash: created.client_secret_hash, + name: created.name, + role: created.role, + permissions: created.permissions as string[], + status: created.status, + metadata: (created.metadata ?? {}) as any, + }); + } + + async update(serviceAccount: ServiceAccount): Promise { + const updated = await this.prisma.serviceAccount.update({ + where: { id: serviceAccount.id }, + data: { + client_id: serviceAccount.clientId, + client_secret_hash: serviceAccount.clientSecretHash, + name: serviceAccount.name, + role: serviceAccount.role, + permissions: serviceAccount.permissions, + status: serviceAccount.status, + metadata: serviceAccount.metadata ?? {}, + }, + }); + + return ServiceAccount.restore({ + id: updated.id, + clientId: updated.client_id, + clientSecretHash: updated.client_secret_hash, + name: updated.name, + role: updated.role, + permissions: updated.permissions as string[], + status: updated.status, + metadata: (updated.metadata ?? {}) as any, + }); + } + + async findById(id: string): Promise { + const model = await this.prisma.serviceAccount.findUnique({ + where: { id }, + }); + + if (!model) return null; + + return ServiceAccount.restore({ + id: model.id, + clientId: model.client_id, + clientSecretHash: model.client_secret_hash, + name: model.name, + role: model.role, + permissions: model.permissions as string[], + status: model.status, + metadata: (model.metadata ?? {}) as any, + }); + } + + async findByClientId(clientId: string): Promise { + const model = await this.prisma.serviceAccount.findUnique({ + where: { client_id: clientId }, + }); + + if (!model) return null; + + return ServiceAccount.restore({ + id: model.id, + clientId: model.client_id, + clientSecretHash: model.client_secret_hash, + name: model.name, + role: model.role, + permissions: model.permissions as string[], + status: model.status, + metadata: (model.metadata ?? {}) as any, + }); + } +} diff --git a/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts b/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts index 74bc603..d3e8a73 100644 --- a/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts +++ b/src/modules/identity/infrastructure/repositories/prisma-user.repository.ts @@ -16,13 +16,21 @@ export class PrismaUserRepository implements UserRepository { return PrismaUserMapper.toDomain(user); } + async findByEmail(email: string): Promise { + const user = await this.prisma.user.findUnique({ + where: { email }, + }); + + return PrismaUserMapper.toDomain(user); + } + async findAll(): Promise { const users = await this.prisma.user.findMany({ where: { deleted_at: null }, orderBy: { created_at: 'desc' }, }); - return users.map(PrismaUserMapper.toDomain); + return users.map(u => PrismaUserMapper.toDomain(u)!); } async create(user: User): Promise { diff --git a/src/modules/identity/presentation/controllers/auth.controller.ts b/src/modules/identity/presentation/controllers/auth.controller.ts new file mode 100644 index 0000000..9ac2eb6 --- /dev/null +++ b/src/modules/identity/presentation/controllers/auth.controller.ts @@ -0,0 +1,40 @@ +import { Controller, Post, Body } from '@nestjs/common'; +import { ApiTags, ApiOperation } from '@nestjs/swagger'; + +import { LoginDto } from '../dto/login.dto'; +import { LoginHandler } from '../../application/handlers/login.handler'; +import { TokenHandler } from '../../application/handlers/token.handler'; + +@ApiTags('Auth') +@Controller('api/v1/auth') +export class AuthController { + constructor( + private readonly loginHandler: LoginHandler, + private readonly tokenHandler: TokenHandler, + ) {} + + @Post('login') + @ApiOperation({ summary: 'Login and obtain access token' }) + async login(@Body() dto: LoginDto) { + const data = await this.loginHandler.execute(dto); + + return { + success: true, + data, + meta: {}, + }; + } + + @Post('token') + @ApiOperation({ summary: 'Obtain access token using client credentials' }) + async token(@Body() body: any) { + const data = await this.tokenHandler.execute(body); + + return { + success: true, + data, + meta: {}, + }; + } +} + diff --git a/src/modules/identity/presentation/controllers/users.controller.ts b/src/modules/identity/presentation/controllers/users.controller.ts index edd1057..dd6d95c 100644 --- a/src/modules/identity/presentation/controllers/users.controller.ts +++ b/src/modules/identity/presentation/controllers/users.controller.ts @@ -34,6 +34,18 @@ export class UsersController { private readonly deleteUserHandler: DeleteUserHandler, ) {} + private sanitizeUser(user: any) { + if (!user) return user; + if (Array.isArray(user)) { + return user.map(u => { + const { password, ...rest } = u as any; + return rest; + }); + } + const { password, ...rest } = user as any; + return rest; + } + @Post() @Permissions('USER_CREATE') @ApiOperation({ summary: 'Create user' }) @@ -42,7 +54,7 @@ export class UsersController { return { success: true, - data, + data: this.sanitizeUser(data), meta: {}, } } @@ -55,7 +67,7 @@ export class UsersController { return { success: true, - data, + data: this.sanitizeUser(data), meta: {}, }; } @@ -68,7 +80,7 @@ export class UsersController { return { success: true, - data, + data: this.sanitizeUser(data), meta: {}, }; } @@ -81,7 +93,7 @@ export class UsersController { return { success: true, - data, + data: this.sanitizeUser(data), meta: {}, } } diff --git a/src/modules/identity/presentation/dto/login.dto.ts b/src/modules/identity/presentation/dto/login.dto.ts new file mode 100644 index 0000000..39b3b21 --- /dev/null +++ b/src/modules/identity/presentation/dto/login.dto.ts @@ -0,0 +1,13 @@ +import { IsEmail, IsString, Length } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class LoginDto { + @ApiProperty({ example: 'user@example.com' }) + @IsEmail() + email!: string; + + @ApiProperty({ example: 'strongpassword' }) + @IsString() + @Length(8, 128) + password!: string; +} diff --git a/tests/adapters/core-api-client.spec.ts b/tests/adapters/core-api-client.spec.ts new file mode 100644 index 0000000..79acced --- /dev/null +++ b/tests/adapters/core-api-client.spec.ts @@ -0,0 +1,87 @@ +import { CoreApiClient, CoreApiClientError } from '../../src/adapters/notification/core-api-client'; + +describe('CoreApiClient', () => { + const API_URL = 'http://core.local'; + const CLIENT_ID = 'cid'; + const CLIENT_SECRET = 'csec'; + + beforeEach(() => { + (global as any).fetch = jest.fn(); + jest.clearAllMocks(); + }); + + it('requests token successfully and caches it', async () => { + // token endpoint response + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ success: true, data: { access_token: 't1', token_type: 'Bearer', expires_in: 3600 } }) }); + + const client = new CoreApiClient({ apiUrl: API_URL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }); + + const token = await client.getToken(); + expect(token).toBe('t1'); + + // second call should not call fetch again for token + const token2 = await client.getToken(); + expect(token2).toBe('t1'); + expect(global.fetch).toHaveBeenCalledTimes(1); + }); + + it('throws on invalid credentials (401)', async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 401, json: async () => ({}) }); + const client = new CoreApiClient({ apiUrl: API_URL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }); + await expect(client.getToken()).rejects.toThrow(CoreApiClientError); + }); + + it('retries once on 401 during authenticatedRequest', async () => { + // initial token fetch + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ success: true, data: { access_token: 't1', token_type: 'Bearer', expires_in: 3600 } }) }); + + // first API call returns 401 + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 401, json: async () => ({}) }); + + // token refresh fetch returns new token + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ success: true, data: { access_token: 't2', token_type: 'Bearer', expires_in: 3600 } }) }); + + // retried API call returns 200 + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ ok: true }) }); + + const client = new CoreApiClient({ apiUrl: API_URL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }); + + const res = await client.authenticatedRequest(`${API_URL}/api/v1/users`, { method: 'GET' }); + expect(res.status).toBe(200); + expect(global.fetch).toHaveBeenCalledTimes(4); + }); + + it('does not retry on 403', async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ success: true, data: { access_token: 't1', token_type: 'Bearer', expires_in: 3600 } }) }); + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 403, json: async () => ({}) }); + + const client = new CoreApiClient({ apiUrl: API_URL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }); + + const res = await client.authenticatedRequest(`${API_URL}/api/v1/forbidden`, { method: 'GET' }); + expect(res.status).toBe(403); + // only token fetch + one API call + expect(global.fetch).toHaveBeenCalledTimes(2); + }); + + it('handles token refresh before expiry', async () => { + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ success: true, data: { access_token: 't1', token_type: 'Bearer', expires_in: 2 } }) }); + const client = new CoreApiClient({ apiUrl: API_URL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }); + + const token1 = await client.getToken(); + expect(token1).toBe('t1'); + + // force expiry by manipulating internal state + (client as any).tokenExpiry = Date.now() - 1000; + + (global.fetch as jest.Mock).mockResolvedValueOnce({ status: 200, json: async () => ({ success: true, data: { access_token: 't2', token_type: 'Bearer', expires_in: 3600 } }) }); + + const token2 = await client.getToken(); + expect(token2).toBe('t2'); + }); + + it('handles network timeout', async () => { + (global.fetch as jest.Mock).mockRejectedValueOnce(Object.assign(new Error('AbortError'), { name: 'AbortError' })); + const client = new CoreApiClient({ apiUrl: API_URL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, timeoutMs: 1 }); + await expect(client.getToken()).rejects.toThrow(CoreApiClientError); + }); +}); diff --git a/tests/identity/prisma-service-account-repository.spec.ts b/tests/identity/prisma-service-account-repository.spec.ts new file mode 100644 index 0000000..a2747b8 --- /dev/null +++ b/tests/identity/prisma-service-account-repository.spec.ts @@ -0,0 +1,35 @@ +import { PrismaService } from '../../src/shared/prisma.service'; +import { PrismaServiceAccountRepository } from '../../src/modules/identity/infrastructure/repositories/prisma-service-account.repository'; +import { ServiceAccount } from '../../src/modules/identity/domain/entities/service-account.entity'; + +describe('PrismaServiceAccountRepository', () => { + it('maps prisma model to domain entity for findByClientId', async () => { + const fakeModel = { + id: 'id-1', + client_id: 'cid', + client_secret_hash: 'hash', + name: 'Bot', + role: 'BOT', + permissions: ['DEBT_READ'], + status: 'ACTIVE', + metadata: { hello: 'world' }, + }; + + const mockPrisma: Partial = { + serviceAccount: { + findUnique: jest.fn().mockResolvedValue(fakeModel), + } as any, + }; + + const repo = new PrismaServiceAccountRepository(mockPrisma as any); + + const sa = await repo.findByClientId('cid'); + + expect(sa).not.toBeNull(); + expect(sa!.clientId).toBe('cid'); + expect(sa!.name).toBe('Bot'); + expect(sa!.role).toBe('BOT'); + expect(sa!.permissions).toEqual(fakeModel.permissions); + expect(sa!.metadata).toEqual(fakeModel.metadata); + }); +}); diff --git a/tests/identity/token.handler.spec.ts b/tests/identity/token.handler.spec.ts new file mode 100644 index 0000000..0a72487 --- /dev/null +++ b/tests/identity/token.handler.spec.ts @@ -0,0 +1,98 @@ +import { BadRequestException, UnauthorizedException } from '@nestjs/common'; +import * as bcrypt from 'bcrypt'; + +import { TokenHandler } from '../../src/modules/identity/application/handlers/token.handler'; +import { ServiceAccount } from '../../src/modules/identity/domain/entities/service-account.entity'; +import { ServiceAccountRepository } from '../../src/modules/identity/domain/repositories/service-account.repository.interface'; + +describe('TokenHandler', () => { + let mockRepo: Partial; + let mockJwtService: any; + let handler: TokenHandler; + + beforeEach(() => { + mockRepo = { + findByClientId: jest.fn(), + }; + + mockJwtService = { + signAsync: jest.fn().mockResolvedValue('signed-token'), + }; + + handler = new TokenHandler(mockRepo as any, mockJwtService as any); + }); + + it('should throw BadRequestException for invalid grant_type', async () => { + await expect(handler.execute({ client_id: 'a', client_secret: 'b', grant_type: 'foo' })).rejects.toBeInstanceOf(BadRequestException); + }); + + it('should throw UnauthorizedException when client_id not found', async () => { + (mockRepo.findByClientId as jest.Mock).mockResolvedValue(null); + + await expect(handler.execute({ client_id: 'notfound', client_secret: 'x', grant_type: 'client_credentials' })).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should throw UnauthorizedException when service account disabled', async () => { + const sa = ServiceAccount.restore({ + id: 'sa-id', + clientId: 'cid', + clientSecretHash: await bcrypt.hash('secret', 1), + name: 'Bot', + role: 'BOT', + permissions: ['DEBT_READ'], + status: 'DISABLED', + metadata: {}, + }); + + (mockRepo.findByClientId as jest.Mock).mockResolvedValue(sa); + + await expect(handler.execute({ client_id: 'cid', client_secret: 'secret', grant_type: 'client_credentials' })).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should throw UnauthorizedException when secret mismatch', async () => { + const sa = ServiceAccount.restore({ + id: 'sa-id', + clientId: 'cid', + clientSecretHash: await bcrypt.hash('secret', 1), + name: 'Bot', + role: 'BOT', + permissions: ['DEBT_READ'], + status: 'ACTIVE', + metadata: {}, + }); + + (mockRepo.findByClientId as jest.Mock).mockResolvedValue(sa); + + await expect(handler.execute({ client_id: 'cid', client_secret: 'wrong', grant_type: 'client_credentials' })).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should sign JWT and return token for valid credentials', async () => { + const hash = await bcrypt.hash('secret', 1); + + const sa = ServiceAccount.restore({ + id: 'sa-id', + clientId: 'cid', + clientSecretHash: hash, + name: 'Bot', + role: 'BOT', + permissions: ['DEBT_READ', 'DEBT_CREATE'], + status: 'ACTIVE', + metadata: {}, + }); + + (mockRepo.findByClientId as jest.Mock).mockResolvedValue(sa); + + const result = await handler.execute({ client_id: 'cid', client_secret: 'secret', grant_type: 'client_credentials' }); + + expect(mockJwtService.signAsync).toHaveBeenCalledTimes(1); + + const signCallArgs = (mockJwtService.signAsync as jest.Mock).mock.calls[0]; + const payload = signCallArgs[0]; + const options = signCallArgs[1]; + + expect(payload).toMatchObject({ sub: 'sa-id', client_id: 'cid', role: 'BOT', permissions: expect.any(Array) }); + expect(options).toMatchObject({ expiresIn: '1h' }); + + expect(result).toMatchObject({ access_token: 'signed-token', token_type: 'Bearer', expires_in: 3600 }); + }); +});