feat: Initial commit - template-saas

Template base para proyectos SaaS multi-tenant.

Estructura inicial:
- apps/backend (NestJS API)
- apps/frontend (React/Vite)
- apps/database (PostgreSQL DDL)
- docs/ (Documentación)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2026-01-07 04:41:24 -06:00
commit 26f0e52ca7
21215 changed files with 2190550 additions and 0 deletions

115
README.md Normal file
View File

@ -0,0 +1,115 @@
# Template SaaS Multi-Tenant
**Version:** 0.1.0
**Estado:** Fase 0 - Preparacion
**Tipo:** STANDALONE
**Sistema:** SIMCO + NEXUS v3.4
---
## Descripcion
Template base para desarrollo de plataformas SaaS multi-tenant con arquitectura moderna, siguiendo los estandares definidos en el workspace NEXUS.
Este proyecto sirve como punto de partida para cualquier aplicacion SaaS que requiera:
- Multi-tenancy con aislamiento de datos (RLS)
- Sistema de suscripciones y pagos (Stripe)
- Tres portales: Usuario Final, Admin de Tenant, Superadmin
- Integracion con LLMs (agnóstico al proveedor)
- RBAC (Role-Based Access Control)
---
## Stack Tecnologico
| Capa | Tecnologia |
|------|------------|
| Backend | Node.js 20+ / Express.js / TypeScript 5.3+ |
| Frontend | React 18+ / Vite 5+ / TypeScript / Tailwind CSS 4 |
| Database | PostgreSQL 16+ con RLS |
| State | Zustand |
| Pagos | Stripe |
| IA | Claude / OpenAI / Gemini (wrapper agnóstico) |
---
## Modulos Core
1. **SAAS-001-auth** - Autenticacion JWT, OAuth, MFA
2. **SAAS-002-tenants** - Gestion de organizaciones
3. **SAAS-003-users** - Usuarios con RBAC
4. **SAAS-004-billing** - Suscripciones Stripe
5. **SAAS-005-plans** - Planes y limites
6. **SAAS-006-onboarding** - Flujo de registro
7. **SAAS-007-notifications** - Email, push, in-app
8. **SAAS-008-feature-flags** - Toggles por plan/tenant
9. **SAAS-009-audit** - Auditoria de acciones
10. **SAAS-010-portal-user** - Portal usuario final
11. **SAAS-011-portal-admin** - Portal admin de tenant
12. **SAAS-012-portal-superadmin** - Portal superadmin
---
## Estructura del Proyecto
```
template-saas/
├── apps/
│ ├── database/
│ │ ├── ddl/schemas/
│ │ ├── seeds/
│ │ └── scripts/
│ ├── backend/src/
│ │ ├── modules/
│ │ └── shared/
│ └── frontend/src/
│ ├── portals/
│ ├── shared/
│ └── stores/
├── docs/
│ ├── 00-vision-general/
│ ├── 01-modulos/
│ ├── 02-integraciones/
│ └── 97-adr/
└── orchestration/
├── 00-guidelines/
├── inventarios/
└── trazas/
```
---
## Inicio Rapido
```bash
# Database
cd apps/database && ./scripts/drop-and-recreate-database.sh
# Backend
cd apps/backend && npm install && npm run start:dev
# Frontend
cd apps/frontend && npm install && npm run dev
```
---
## Herencia SIMCO
Este proyecto hereda directivas de:
- `core/orchestration/directivas/simco/`
- `shared/catalog/` (funcionalidades reutilizables)
---
## Referencias
- Vision: `docs/00-vision-general/VISION-TEMPLATE-SAAS.md`
- Arquitectura: `docs/00-vision-general/ARQUITECTURA-MULTI-TENANT.md`
- Contexto: `orchestration/00-guidelines/CONTEXTO-PROYECTO.md`
- Estado: `orchestration/PROJECT-STATUS.md`
---
**Creado:** 2026-01-07
**Sistema:** NEXUS v3.4 | SIMCO

26
apps/backend/.env.example Normal file
View File

@ -0,0 +1,26 @@
# Template SaaS Backend Configuration
# Copy this file to .env and adjust values
# Server
NODE_ENV=development
PORT=3001
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=template_saas_dev
DB_USER=gamilit_user
DB_PASSWORD=GO0jAOgw8Yzankwt
# JWT
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=http://localhost:3000
# Stripe Integration (optional - leave empty to disable)
STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret

2
apps/backend/dist/app.module.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export declare class AppModule {
}

61
apps/backend/dist/app.module.js vendored Normal file
View File

@ -0,0 +1,61 @@
"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 typeorm_1 = require("@nestjs/typeorm");
const throttler_1 = require("@nestjs/throttler");
const terminus_1 = require("@nestjs/terminus");
const env_config_1 = require("@config/env.config");
const database_config_1 = require("@config/database.config");
const auth_module_1 = require("@modules/auth/auth.module");
const tenants_module_1 = require("@modules/tenants/tenants.module");
const users_module_1 = require("@modules/users/users.module");
const rbac_module_1 = require("@modules/rbac/rbac.module");
const notifications_module_1 = require("@modules/notifications/notifications.module");
const billing_module_1 = require("@modules/billing/billing.module");
const audit_module_1 = require("@modules/audit/audit.module");
const feature_flags_module_1 = require("@modules/feature-flags/feature-flags.module");
const health_module_1 = require("@modules/health/health.module");
let AppModule = class AppModule {
};
exports.AppModule = AppModule;
exports.AppModule = AppModule = __decorate([
(0, common_1.Module)({
imports: [
config_1.ConfigModule.forRoot({
isGlobal: true,
load: [env_config_1.envConfig],
validationSchema: env_config_1.validationSchema,
}),
typeorm_1.TypeOrmModule.forRootAsync({
imports: [config_1.ConfigModule],
useFactory: (configService) => (0, database_config_1.databaseConfig)(configService),
inject: [config_1.ConfigService],
}),
throttler_1.ThrottlerModule.forRoot([
{
ttl: 60000,
limit: 100,
},
]),
terminus_1.TerminusModule,
auth_module_1.AuthModule,
tenants_module_1.TenantsModule,
users_module_1.UsersModule,
rbac_module_1.RbacModule,
notifications_module_1.NotificationsModule,
billing_module_1.BillingModule,
audit_module_1.AuditModule,
feature_flags_module_1.FeatureFlagsModule,
health_module_1.HealthModule,
],
})
], AppModule);
//# sourceMappingURL=app.module.js.map

1
apps/backend/dist/app.module.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA6D;AAC7D,6CAAgD;AAChD,iDAAoD;AACpD,+CAAkD;AAGlD,mDAAiE;AACjE,6DAAyD;AAGzD,2DAAuD;AACvD,oEAAgE;AAChE,8DAA0D;AAC1D,2DAAuD;AACvD,sFAAkF;AAClF,oEAAgE;AAChE,8DAA0D;AAC1D,sFAAiF;AACjF,iEAA6D;AAyCtD,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAvCrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YAEP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,CAAC,sBAAS,CAAC;gBACjB,gBAAgB,EAAhB,6BAAgB;aACjB,CAAC;YAGF,uBAAa,CAAC,YAAY,CAAC;gBACzB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,UAAU,EAAE,CAAC,aAA4B,EAAE,EAAE,CAAC,IAAA,gCAAc,EAAC,aAAa,CAAC;gBAC3E,MAAM,EAAE,CAAC,sBAAa,CAAC;aACxB,CAAC;YAGF,2BAAe,CAAC,OAAO,CAAC;gBACtB;oBACE,GAAG,EAAE,KAAK;oBACV,KAAK,EAAE,GAAG;iBACX;aACF,CAAC;YAGF,yBAAc;YAGd,wBAAU;YACV,8BAAa;YACb,0BAAW;YACX,wBAAU;YACV,0CAAmB;YACnB,8BAAa;YACb,0BAAW;YACX,yCAAkB;YAClB,4BAAY;SACb;KACF,CAAC;GACW,SAAS,CAAG"}

View File

@ -0,0 +1,3 @@
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export declare const databaseConfig: (configService: ConfigService) => TypeOrmModuleOptions;

View File

@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.databaseConfig = void 0;
const databaseConfig = (configService) => ({
type: 'postgres',
host: configService.get('database.host'),
port: configService.get('database.port'),
database: configService.get('database.name'),
username: configService.get('database.user'),
password: configService.get('database.password'),
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: false,
logging: configService.get('nodeEnv') === 'development',
ssl: configService.get('nodeEnv') === 'production'
? { rejectUnauthorized: false }
: false,
});
exports.databaseConfig = databaseConfig;
//# sourceMappingURL=database.config.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"database.config.js","sourceRoot":"","sources":["../../src/config/database.config.ts"],"names":[],"mappings":";;;AAGO,MAAM,cAAc,GAAG,CAAC,aAA4B,EAAwB,EAAE,CAAC,CAAC;IACrF,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,aAAa,CAAC,GAAG,CAAS,eAAe,CAAC;IAChD,IAAI,EAAE,aAAa,CAAC,GAAG,CAAS,eAAe,CAAC;IAChD,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAS,eAAe,CAAC;IACpD,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAS,eAAe,CAAC;IACpD,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAS,mBAAmB,CAAC;IACxD,QAAQ,EAAE,CAAC,SAAS,GAAG,0BAA0B,CAAC;IAClD,WAAW,EAAE,KAAK;IAClB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAS,SAAS,CAAC,KAAK,aAAa;IAC/D,GAAG,EAAE,aAAa,CAAC,GAAG,CAAS,SAAS,CAAC,KAAK,YAAY;QACxD,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE;QAC/B,CAAC,CAAC,KAAK;CACV,CAAC,CAAC;AAbU,QAAA,cAAc,kBAaxB"}

View File

@ -0,0 +1,21 @@
import * as Joi from 'joi';
export declare const envConfig: () => {
nodeEnv: string;
port: number;
database: {
host: string;
port: number;
name: string;
user: string;
password: string;
};
jwt: {
secret: string;
expiresIn: string;
refreshExpiresIn: string;
};
cors: {
origin: string;
};
};
export declare const validationSchema: Joi.ObjectSchema<any>;

73
apps/backend/dist/config/env.config.js vendored Normal file
View File

@ -0,0 +1,73 @@
"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 __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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.validationSchema = exports.envConfig = void 0;
const Joi = __importStar(require("joi"));
const envConfig = () => ({
nodeEnv: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT || '3001', 10),
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
name: process.env.DB_NAME || 'template_saas_dev',
user: process.env.DB_USER || 'template_saas_user',
password: process.env.DB_PASSWORD || 'template_saas_dev_2026',
},
jwt: {
secret: process.env.JWT_SECRET || 'dev-jwt-secret-change-in-production',
expiresIn: process.env.JWT_EXPIRES_IN || '15m',
refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d',
},
cors: {
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
},
});
exports.envConfig = envConfig;
exports.validationSchema = Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test')
.default('development'),
PORT: Joi.number().default(3001),
DB_HOST: Joi.string().default('localhost'),
DB_PORT: Joi.number().default(5432),
DB_NAME: Joi.string().default('template_saas_dev'),
DB_USER: Joi.string().default('template_saas_user'),
DB_PASSWORD: Joi.string().default('template_saas_dev_2026'),
JWT_SECRET: Joi.string().default('dev-jwt-secret-change-in-production'),
JWT_EXPIRES_IN: Joi.string().default('15m'),
JWT_REFRESH_EXPIRES_IN: Joi.string().default('7d'),
CORS_ORIGIN: Joi.string().default('http://localhost:3000'),
});
//# sourceMappingURL=env.config.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"env.config.js","sourceRoot":"","sources":["../../src/config/env.config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAA2B;AAEpB,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;IAC9C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,CAAC;IAE9C,QAAQ,EAAE;QACR,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,WAAW;QACxC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,MAAM,EAAE,EAAE,CAAC;QACjD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,mBAAmB;QAChD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,oBAAoB;QACjD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,wBAAwB;KAC9D;IAED,GAAG,EAAE;QACH,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,qCAAqC;QACvE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,KAAK;QAC9C,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,IAAI;KAC7D;IAED,IAAI,EAAE;QACJ,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,uBAAuB;KAC3D;CACF,CAAC,CAAC;AArBU,QAAA,SAAS,aAqBnB;AAEU,QAAA,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;SACnB,KAAK,CAAC,aAAa,EAAE,YAAY,EAAE,MAAM,CAAC;SAC1C,OAAO,CAAC,aAAa,CAAC;IACzB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAEhC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC;IAC1C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC;IAClD,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC;IACnD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,wBAAwB,CAAC;IAE3D,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC;IACvE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC3C,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAElD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,uBAAuB,CAAC;CAC3D,CAAC,CAAC"}

2
apps/backend/dist/config/index.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export * from './env.config';
export * from './database.config';

19
apps/backend/dist/config/index.js vendored Normal file
View File

@ -0,0 +1,19 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./env.config"), exports);
__exportStar(require("./database.config"), exports);
//# sourceMappingURL=index.js.map

1
apps/backend/dist/config/index.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,oDAAkC"}

1
apps/backend/dist/main.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {};

91
apps/backend/dist/main.js vendored Normal file
View File

@ -0,0 +1,91 @@
"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 __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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@nestjs/core");
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const config_1 = require("@nestjs/config");
const helmet_1 = __importDefault(require("helmet"));
const compression = __importStar(require("compression"));
const app_module_1 = require("./app.module");
async function bootstrap() {
const app = await core_1.NestFactory.create(app_module_1.AppModule);
const configService = app.get(config_1.ConfigService);
app.use((0, helmet_1.default)());
app.use(compression());
app.enableCors({
origin: configService.get('CORS_ORIGIN') || 'http://localhost:3000',
credentials: true,
});
app.setGlobalPrefix('api/v1');
app.useGlobalPipes(new common_1.ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: {
enableImplicitConversion: true,
},
}));
if (configService.get('NODE_ENV') !== 'production') {
const config = new swagger_1.DocumentBuilder()
.setTitle('Template SaaS API')
.setDescription('Multi-tenant SaaS Platform API')
.setVersion('1.0')
.addBearerAuth()
.addTag('auth', 'Authentication endpoints')
.addTag('tenants', 'Tenant management')
.addTag('users', 'User management')
.addTag('billing', 'Billing and subscriptions')
.build();
const document = swagger_1.SwaggerModule.createDocument(app, config);
swagger_1.SwaggerModule.setup('api/docs', app, document);
}
const port = configService.get('PORT') || 3001;
await app.listen(port);
console.log(`
Template SaaS Backend
Server running on: http://localhost:${port} ║
API Docs: http://localhost:${port}/api/docs ║
Environment: ${configService.get('NODE_ENV') || 'development'}
`);
}
bootstrap();
//# sourceMappingURL=main.js.map

1
apps/backend/dist/main.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAA2C;AAC3C,2CAAgD;AAChD,6CAAiE;AACjE,2CAA+C;AAC/C,oDAA4B;AAC5B,yDAA2C;AAC3C,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IAG7C,GAAG,CAAC,GAAG,CAAC,IAAA,gBAAM,GAAE,CAAC,CAAC;IAClB,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAGvB,GAAG,CAAC,UAAU,CAAC;QACb,MAAM,EAAE,aAAa,CAAC,GAAG,CAAS,aAAa,CAAC,IAAI,uBAAuB;QAC3E,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAGH,GAAG,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;IAG9B,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,IAAI;QACf,oBAAoB,EAAE,IAAI;QAC1B,SAAS,EAAE,IAAI;QACf,gBAAgB,EAAE;YAChB,wBAAwB,EAAE,IAAI;SAC/B;KACF,CAAC,CACH,CAAC;IAGF,IAAI,aAAa,CAAC,GAAG,CAAS,UAAU,CAAC,KAAK,YAAY,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,IAAI,yBAAe,EAAE;aACjC,QAAQ,CAAC,mBAAmB,CAAC;aAC7B,cAAc,CAAC,gCAAgC,CAAC;aAChD,UAAU,CAAC,KAAK,CAAC;aACjB,aAAa,EAAE;aACf,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC;aAC1C,MAAM,CAAC,SAAS,EAAE,mBAAmB,CAAC;aACtC,MAAM,CAAC,OAAO,EAAE,iBAAiB,CAAC;aAClC,MAAM,CAAC,SAAS,EAAE,2BAA2B,CAAC;aAC9C,KAAK,EAAE,CAAC;QAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC3D,uBAAa,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;IAGD,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAS,MAAM,CAAC,IAAI,IAAI,CAAC;IACvD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC;;;;yCAI2B,IAAI;gCACb,IAAI;kBAClB,aAAa,CAAC,GAAG,CAAS,UAAU,CAAC,IAAI,aAAa;;GAErE,CAAC,CAAC;AACL,CAAC;AAED,SAAS,EAAE,CAAC"}

View File

@ -0,0 +1,33 @@
import { AuditService } from './services/audit.service';
import { QueryAuditLogsDto } from './dto/query-audit.dto';
import { QueryActivityLogsDto } from './dto/query-activity.dto';
import { CreateActivityLogDto } from './dto/create-activity.dto';
import { RequestUser } from '../auth/strategies/jwt.strategy';
export declare class AuditController {
private readonly auditService;
constructor(auditService: AuditService);
queryAuditLogs(user: RequestUser, query: QueryAuditLogsDto): Promise<import("./services/audit.service").PaginatedResult<import("./entities").AuditLog>>;
getAuditLogById(user: RequestUser, id: string): Promise<import("./entities").AuditLog | null>;
getEntityAuditHistory(user: RequestUser, entityType: string, entityId: string): Promise<import("./entities").AuditLog[]>;
getAuditStats(user: RequestUser, days?: number): Promise<{
total_actions: number;
actions_by_type: {
action: import("./entities").AuditAction;
count: number;
}[];
top_users: {
user_id: string;
count: number;
}[];
}>;
queryActivityLogs(user: RequestUser, query: QueryActivityLogsDto): Promise<import("./services/audit.service").PaginatedResult<import("./entities").ActivityLog>>;
createActivityLog(user: RequestUser, dto: CreateActivityLogDto, request: any): Promise<import("./entities").ActivityLog>;
getUserActivitySummary(user: RequestUser, days?: number): Promise<{
activity_type: import("./entities").ActivityType;
count: number;
}[]>;
getSpecificUserActivitySummary(user: RequestUser, userId: string, days?: number): Promise<{
activity_type: import("./entities").ActivityType;
count: number;
}[]>;
}

View File

@ -0,0 +1,153 @@
"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.AuditController = void 0;
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const audit_service_1 = require("./services/audit.service");
const query_audit_dto_1 = require("./dto/query-audit.dto");
const query_activity_dto_1 = require("./dto/query-activity.dto");
const create_activity_dto_1 = require("./dto/create-activity.dto");
const guards_1 = require("../auth/guards");
const decorators_1 = require("../auth/decorators");
let AuditController = class AuditController {
constructor(auditService) {
this.auditService = auditService;
}
async queryAuditLogs(user, query) {
return this.auditService.queryAuditLogs(user.tenant_id, query);
}
async getAuditLogById(user, id) {
return this.auditService.getAuditLogById(user.tenant_id, id);
}
async getEntityAuditHistory(user, entityType, entityId) {
return this.auditService.getEntityAuditHistory(user.tenant_id, entityType, entityId);
}
async getAuditStats(user, days) {
return this.auditService.getAuditStats(user.tenant_id, days || 7);
}
async queryActivityLogs(user, query) {
return this.auditService.queryActivityLogs(user.tenant_id, query);
}
async createActivityLog(user, dto, request) {
return this.auditService.createActivityLog(user.tenant_id, user.id, dto, {
ip_address: request.ip,
user_agent: request.headers['user-agent'],
session_id: request.headers['x-session-id'],
});
}
async getUserActivitySummary(user, days) {
return this.auditService.getUserActivitySummary(user.tenant_id, user.id, days || 30);
}
async getSpecificUserActivitySummary(user, userId, days) {
return this.auditService.getUserActivitySummary(user.tenant_id, userId, days || 30);
}
};
exports.AuditController = AuditController;
__decorate([
(0, common_1.Get)('logs'),
(0, swagger_1.ApiOperation)({ summary: 'Query audit logs with filters' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Paginated audit logs' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, query_audit_dto_1.QueryAuditLogsDto]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "queryAuditLogs", null);
__decorate([
(0, common_1.Get)('logs/:id'),
(0, swagger_1.ApiOperation)({ summary: 'Get audit log by ID' }),
(0, swagger_1.ApiParam)({ name: 'id', description: 'Audit log ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Audit log details' }),
(0, swagger_1.ApiResponse)({ status: 404, description: 'Audit log not found' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "getAuditLogById", null);
__decorate([
(0, common_1.Get)('entity/:entityType/:entityId'),
(0, swagger_1.ApiOperation)({ summary: 'Get audit history for a specific entity' }),
(0, swagger_1.ApiParam)({ name: 'entityType', description: 'Entity type (e.g., user, product)' }),
(0, swagger_1.ApiParam)({ name: 'entityId', description: 'Entity ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Entity audit history' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Param)('entityType')),
__param(2, (0, common_1.Param)('entityId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, String]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "getEntityAuditHistory", null);
__decorate([
(0, common_1.Get)('stats'),
(0, swagger_1.ApiOperation)({ summary: 'Get audit statistics for dashboard' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Audit statistics' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Query)('days')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Number]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "getAuditStats", null);
__decorate([
(0, common_1.Get)('activities'),
(0, swagger_1.ApiOperation)({ summary: 'Query activity logs with filters' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Paginated activity logs' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, query_activity_dto_1.QueryActivityLogsDto]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "queryActivityLogs", null);
__decorate([
(0, common_1.Post)('activities'),
(0, swagger_1.ApiOperation)({ summary: 'Create an activity log entry' }),
(0, swagger_1.ApiResponse)({ status: 201, description: 'Activity log created' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Body)()),
__param(2, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, create_activity_dto_1.CreateActivityLogDto, Object]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "createActivityLog", null);
__decorate([
(0, common_1.Get)('activities/summary'),
(0, swagger_1.ApiOperation)({ summary: 'Get user activity summary' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Activity summary by type' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Query)('days')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Number]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "getUserActivitySummary", null);
__decorate([
(0, common_1.Get)('activities/user/:userId'),
(0, swagger_1.ApiOperation)({ summary: 'Get activity summary for a specific user' }),
(0, swagger_1.ApiParam)({ name: 'userId', description: 'User ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'User activity summary' }),
__param(0, (0, decorators_1.CurrentUser)()),
__param(1, (0, common_1.Param)('userId')),
__param(2, (0, common_1.Query)('days')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, Number]),
__metadata("design:returntype", Promise)
], AuditController.prototype, "getSpecificUserActivitySummary", null);
exports.AuditController = AuditController = __decorate([
(0, swagger_1.ApiTags)('Audit'),
(0, swagger_1.ApiBearerAuth)(),
(0, common_1.UseGuards)(guards_1.JwtAuthGuard),
(0, common_1.Controller)('audit'),
__metadata("design:paramtypes", [audit_service_1.AuditService])
], AuditController);
//# sourceMappingURL=audit.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"audit.controller.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.controller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,2 @@
export declare class AuditModule {
}

View File

@ -0,0 +1,35 @@
"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.AuditModule = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const core_1 = require("@nestjs/core");
const audit_controller_1 = require("./audit.controller");
const audit_service_1 = require("./services/audit.service");
const entities_1 = require("./entities");
const audit_interceptor_1 = require("./interceptors/audit.interceptor");
let AuditModule = class AuditModule {
};
exports.AuditModule = AuditModule;
exports.AuditModule = AuditModule = __decorate([
(0, common_1.Global)(),
(0, common_1.Module)({
imports: [typeorm_1.TypeOrmModule.forFeature([entities_1.AuditLog, entities_1.ActivityLog])],
controllers: [audit_controller_1.AuditController],
providers: [
audit_service_1.AuditService,
{
provide: core_1.APP_INTERCEPTOR,
useClass: audit_interceptor_1.AuditInterceptor,
},
],
exports: [audit_service_1.AuditService],
})
], AuditModule);
//# sourceMappingURL=audit.module.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"audit.module.js","sourceRoot":"","sources":["../../../src/modules/audit/audit.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,6CAAgD;AAChD,uCAA+C;AAC/C,yDAAqD;AACrD,4DAAwD;AACxD,yCAAmD;AACnD,wEAAoE;AAgB7D,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IAdvB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,mBAAQ,EAAE,sBAAW,CAAC,CAAC,CAAC;QAC5D,WAAW,EAAE,CAAC,kCAAe,CAAC;QAC9B,SAAS,EAAE;YACT,4BAAY;YAEZ;gBACE,OAAO,EAAE,sBAAe;gBACxB,QAAQ,EAAE,oCAAgB;aAC3B;SACF;QACD,OAAO,EAAE,CAAC,4BAAY,CAAC;KACxB,CAAC;GACW,WAAW,CAAG"}

View File

@ -0,0 +1,8 @@
import { ActivityType } from '../entities/activity-log.entity';
export declare class CreateActivityLogDto {
activity_type: ActivityType;
resource_type?: string;
resource_id?: string;
description?: string;
metadata?: Record<string, any>;
}

View File

@ -0,0 +1,48 @@
"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.CreateActivityLogDto = void 0;
const class_validator_1 = require("class-validator");
const swagger_1 = require("@nestjs/swagger");
const activity_log_entity_1 = require("../entities/activity-log.entity");
class CreateActivityLogDto {
}
exports.CreateActivityLogDto = CreateActivityLogDto;
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Activity type', enum: activity_log_entity_1.ActivityType }),
(0, class_validator_1.IsEnum)(activity_log_entity_1.ActivityType),
__metadata("design:type", String)
], CreateActivityLogDto.prototype, "activity_type", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Resource type' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateActivityLogDto.prototype, "resource_type", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Resource ID' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], CreateActivityLogDto.prototype, "resource_id", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Activity description' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateActivityLogDto.prototype, "description", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Additional metadata' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsObject)(),
__metadata("design:type", Object)
], CreateActivityLogDto.prototype, "metadata", void 0);
//# sourceMappingURL=create-activity.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"create-activity.dto.js","sourceRoot":"","sources":["../../../../src/modules/audit/dto/create-activity.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAiF;AACjF,6CAAmE;AACnE,yEAA+D;AAE/D,MAAa,oBAAoB;CAwBhC;AAxBD,oDAwBC;AArBC;IAFC,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,eAAe,EAAE,IAAI,EAAE,kCAAY,EAAE,CAAC;IACjE,IAAA,wBAAM,EAAC,kCAAY,CAAC;;2DACO;AAK5B;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;IACrD,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2DACY;AAKvB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;IACnD,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,GAAE;;yDACY;AAKrB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;IAC5D,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;yDACU;AAKrB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;IAC3D,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACoB"}

View File

@ -0,0 +1,3 @@
export * from './query-audit.dto';
export * from './query-activity.dto';
export * from './create-activity.dto';

View File

@ -0,0 +1,20 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./query-audit.dto"), exports);
__exportStar(require("./query-activity.dto"), exports);
__exportStar(require("./create-activity.dto"), exports);
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/audit/dto/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,uDAAqC;AACrC,wDAAsC"}

View File

@ -0,0 +1,11 @@
import { ActivityType } from '../entities/activity-log.entity';
export declare class QueryActivityLogsDto {
user_id?: string;
activity_type?: ActivityType;
resource_type?: string;
resource_id?: string;
from_date?: string;
to_date?: string;
page?: number;
limit?: number;
}

View File

@ -0,0 +1,77 @@
"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.QueryActivityLogsDto = void 0;
const class_validator_1 = require("class-validator");
const swagger_1 = require("@nestjs/swagger");
const class_transformer_1 = require("class-transformer");
const activity_log_entity_1 = require("../entities/activity-log.entity");
class QueryActivityLogsDto {
constructor() {
this.page = 1;
this.limit = 20;
}
}
exports.QueryActivityLogsDto = QueryActivityLogsDto;
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by user ID' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], QueryActivityLogsDto.prototype, "user_id", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by activity type', enum: activity_log_entity_1.ActivityType }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsEnum)(activity_log_entity_1.ActivityType),
__metadata("design:type", String)
], QueryActivityLogsDto.prototype, "activity_type", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by resource type' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], QueryActivityLogsDto.prototype, "resource_type", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by resource ID' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], QueryActivityLogsDto.prototype, "resource_id", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Start date filter (ISO 8601)' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsDateString)(),
__metadata("design:type", String)
], QueryActivityLogsDto.prototype, "from_date", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'End date filter (ISO 8601)' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsDateString)(),
__metadata("design:type", String)
], QueryActivityLogsDto.prototype, "to_date", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Page number', default: 1 }),
(0, class_validator_1.IsOptional)(),
(0, class_transformer_1.Type)(() => Number),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(1),
__metadata("design:type", Number)
], QueryActivityLogsDto.prototype, "page", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Items per page', default: 20 }),
(0, class_validator_1.IsOptional)(),
(0, class_transformer_1.Type)(() => Number),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(1),
(0, class_validator_1.Max)(100),
__metadata("design:type", Number)
], QueryActivityLogsDto.prototype, "limit", void 0);
//# sourceMappingURL=query-activity.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"query-activity.dto.js","sourceRoot":"","sources":["../../../../src/modules/audit/dto/query-activity.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDASyB;AACzB,6CAAsD;AACtD,yDAAyC;AACzC,yEAA+D;AAE/D,MAAa,oBAAoB;IAAjC;QAoCE,SAAI,GAAY,CAAC,CAAC;QAQlB,UAAK,GAAY,EAAE,CAAC;IACtB,CAAC;CAAA;AA7CD,oDA6CC;AAzCC;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACzD,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,GAAE;;qDACQ;AAKjB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,IAAI,EAAE,kCAAY,EAAE,CAAC;IACnF,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,kCAAY,CAAC;;2DACQ;AAK7B;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;IAC/D,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;2DACY;AAKvB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;IAC7D,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,GAAE;;yDACY;AAKrB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;IACpE,IAAA,4BAAU,GAAE;IACZ,IAAA,8BAAY,GAAE;;uDACI;AAKnB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IAClE,IAAA,4BAAU,GAAE;IACZ,IAAA,8BAAY,GAAE;;qDACE;AAOjB;IALC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC/D,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,CAAC;;kDACW;AAQlB;IANC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACnE,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,CAAC;IACN,IAAA,qBAAG,EAAC,GAAG,CAAC;;mDACW"}

View File

@ -0,0 +1,11 @@
import { AuditAction } from '../entities/audit-log.entity';
export declare class QueryAuditLogsDto {
user_id?: string;
action?: AuditAction;
entity_type?: string;
entity_id?: string;
from_date?: string;
to_date?: string;
page?: number;
limit?: number;
}

View File

@ -0,0 +1,77 @@
"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.QueryAuditLogsDto = void 0;
const class_validator_1 = require("class-validator");
const swagger_1 = require("@nestjs/swagger");
const class_transformer_1 = require("class-transformer");
const audit_log_entity_1 = require("../entities/audit-log.entity");
class QueryAuditLogsDto {
constructor() {
this.page = 1;
this.limit = 20;
}
}
exports.QueryAuditLogsDto = QueryAuditLogsDto;
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by user ID' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], QueryAuditLogsDto.prototype, "user_id", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by action', enum: audit_log_entity_1.AuditAction }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsEnum)(audit_log_entity_1.AuditAction),
__metadata("design:type", String)
], QueryAuditLogsDto.prototype, "action", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by entity type' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], QueryAuditLogsDto.prototype, "entity_type", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Filter by entity ID' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], QueryAuditLogsDto.prototype, "entity_id", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Start date filter (ISO 8601)' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsDateString)(),
__metadata("design:type", String)
], QueryAuditLogsDto.prototype, "from_date", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'End date filter (ISO 8601)' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsDateString)(),
__metadata("design:type", String)
], QueryAuditLogsDto.prototype, "to_date", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Page number', default: 1 }),
(0, class_validator_1.IsOptional)(),
(0, class_transformer_1.Type)(() => Number),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(1),
__metadata("design:type", Number)
], QueryAuditLogsDto.prototype, "page", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Items per page', default: 20 }),
(0, class_validator_1.IsOptional)(),
(0, class_transformer_1.Type)(() => Number),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(1),
(0, class_validator_1.Max)(100),
__metadata("design:type", Number)
], QueryAuditLogsDto.prototype, "limit", void 0);
//# sourceMappingURL=query-audit.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"query-audit.dto.js","sourceRoot":"","sources":["../../../../src/modules/audit/dto/query-audit.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDASyB;AACzB,6CAAsD;AACtD,yDAAyC;AACzC,mEAA2D;AAE3D,MAAa,iBAAiB;IAA9B;QAoCE,SAAI,GAAY,CAAC,CAAC;QAQlB,UAAK,GAAY,EAAE,CAAC;IACtB,CAAC;CAAA;AA7CD,8CA6CC;AAzCC;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACzD,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,GAAE;;kDACQ;AAKjB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,8BAAW,EAAE,CAAC;IAC3E,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,8BAAW,CAAC;;iDACC;AAKrB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;IAC7D,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACU;AAKrB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;IAC3D,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,GAAE;;oDACU;AAKnB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;IACpE,IAAA,4BAAU,GAAE;IACZ,IAAA,8BAAY,GAAE;;oDACI;AAKnB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;IAClE,IAAA,4BAAU,GAAE;IACZ,IAAA,8BAAY,GAAE;;kDACE;AAOjB;IALC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC/D,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,CAAC;;+CACW;AAQlB;IANC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACnE,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,CAAC;IACN,IAAA,qBAAG,EAAC,GAAG,CAAC;;gDACW"}

View File

@ -0,0 +1,26 @@
export declare enum ActivityType {
PAGE_VIEW = "page_view",
FEATURE_USE = "feature_use",
SEARCH = "search",
DOWNLOAD = "download",
UPLOAD = "upload",
SHARE = "share",
INVITE = "invite",
SETTINGS_CHANGE = "settings_change",
SUBSCRIPTION_CHANGE = "subscription_change",
PAYMENT = "payment"
}
export declare class ActivityLog {
id: string;
tenant_id: string;
user_id: string;
activity_type: ActivityType;
resource_type: string;
resource_id: string;
description: string;
metadata: Record<string, any>;
ip_address: string;
user_agent: string;
session_id: string;
created_at: Date;
}

View File

@ -0,0 +1,86 @@
"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.ActivityLog = exports.ActivityType = void 0;
const typeorm_1 = require("typeorm");
var ActivityType;
(function (ActivityType) {
ActivityType["PAGE_VIEW"] = "page_view";
ActivityType["FEATURE_USE"] = "feature_use";
ActivityType["SEARCH"] = "search";
ActivityType["DOWNLOAD"] = "download";
ActivityType["UPLOAD"] = "upload";
ActivityType["SHARE"] = "share";
ActivityType["INVITE"] = "invite";
ActivityType["SETTINGS_CHANGE"] = "settings_change";
ActivityType["SUBSCRIPTION_CHANGE"] = "subscription_change";
ActivityType["PAYMENT"] = "payment";
})(ActivityType || (exports.ActivityType = ActivityType = {}));
let ActivityLog = class ActivityLog {
};
exports.ActivityLog = ActivityLog;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], ActivityLog.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'uuid' }),
__metadata("design:type", String)
], ActivityLog.prototype, "tenant_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'uuid' }),
__metadata("design:type", String)
], ActivityLog.prototype, "user_id", void 0);
__decorate([
(0, typeorm_1.Column)({
type: 'enum',
enum: ActivityType,
}),
__metadata("design:type", String)
], ActivityLog.prototype, "activity_type", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
__metadata("design:type", String)
], ActivityLog.prototype, "resource_type", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'uuid', nullable: true }),
__metadata("design:type", String)
], ActivityLog.prototype, "resource_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], ActivityLog.prototype, "description", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
__metadata("design:type", Object)
], ActivityLog.prototype, "metadata", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 45, nullable: true }),
__metadata("design:type", String)
], ActivityLog.prototype, "ip_address", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: true }),
__metadata("design:type", String)
], ActivityLog.prototype, "user_agent", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
__metadata("design:type", String)
], ActivityLog.prototype, "session_id", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ type: 'timestamp with time zone' }),
__metadata("design:type", Date)
], ActivityLog.prototype, "created_at", void 0);
exports.ActivityLog = ActivityLog = __decorate([
(0, typeorm_1.Entity)({ name: 'activity_logs', schema: 'audit' }),
(0, typeorm_1.Index)(['tenant_id', 'created_at']),
(0, typeorm_1.Index)(['user_id', 'activity_type'])
], ActivityLog);
//# sourceMappingURL=activity-log.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"activity-log.entity.js","sourceRoot":"","sources":["../../../../src/modules/audit/entities/activity-log.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAMiB;AAEjB,IAAY,YAWX;AAXD,WAAY,YAAY;IACtB,uCAAuB,CAAA;IACvB,2CAA2B,CAAA;IAC3B,iCAAiB,CAAA;IACjB,qCAAqB,CAAA;IACrB,iCAAiB,CAAA;IACjB,+BAAe,CAAA;IACf,iCAAiB,CAAA;IACjB,mDAAmC,CAAA;IACnC,2DAA2C,CAAA;IAC3C,mCAAmB,CAAA;AACrB,CAAC,EAXW,YAAY,4BAAZ,YAAY,QAWvB;AAKM,IAAM,WAAW,GAAjB,MAAM,WAAW;CAuCvB,CAAA;AAvCY,kCAAW;AAEtB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;uCACpB;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;8CACP;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;4CACT;AAMhB;IAJC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,YAAY;KACnB,CAAC;;kDAC0B;AAG5B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACnC;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACrB;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACrB;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CACZ;AAG9B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CACrC;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CACtC;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CACtC;AAGnB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;8BAC3C,IAAI;+CAAC;sBAtCN,WAAW;IAHvB,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAClD,IAAA,eAAK,EAAC,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAClC,IAAA,eAAK,EAAC,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;GACvB,WAAW,CAuCvB"}

View File

@ -0,0 +1,30 @@
export declare enum AuditAction {
CREATE = "create",
UPDATE = "update",
DELETE = "delete",
READ = "read",
LOGIN = "login",
LOGOUT = "logout",
EXPORT = "export",
IMPORT = "import"
}
export declare class AuditLog {
id: string;
tenant_id: string;
user_id: string;
action: AuditAction;
entity_type: string;
entity_id: string;
old_values: Record<string, any>;
new_values: Record<string, any>;
changed_fields: string[];
ip_address: string;
user_agent: string;
endpoint: string;
http_method: string;
response_status: number;
duration_ms: number;
description: string;
metadata: Record<string, any>;
created_at: Date;
}

View File

@ -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.AuditLog = exports.AuditAction = void 0;
const typeorm_1 = require("typeorm");
var AuditAction;
(function (AuditAction) {
AuditAction["CREATE"] = "create";
AuditAction["UPDATE"] = "update";
AuditAction["DELETE"] = "delete";
AuditAction["READ"] = "read";
AuditAction["LOGIN"] = "login";
AuditAction["LOGOUT"] = "logout";
AuditAction["EXPORT"] = "export";
AuditAction["IMPORT"] = "import";
})(AuditAction || (exports.AuditAction = AuditAction = {}));
let AuditLog = class AuditLog {
};
exports.AuditLog = AuditLog;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], AuditLog.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'uuid' }),
__metadata("design:type", String)
], AuditLog.prototype, "tenant_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'uuid', nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "user_id", void 0);
__decorate([
(0, typeorm_1.Column)({
type: 'enum',
enum: AuditAction,
}),
__metadata("design:type", String)
], AuditLog.prototype, "action", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 100 }),
__metadata("design:type", String)
], AuditLog.prototype, "entity_type", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'uuid', nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "entity_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
__metadata("design:type", Object)
], AuditLog.prototype, "old_values", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
__metadata("design:type", Object)
], AuditLog.prototype, "new_values", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
__metadata("design:type", Array)
], AuditLog.prototype, "changed_fields", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 45, nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "ip_address", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "user_agent", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "endpoint", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'varchar', length: 10, nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "http_method", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'smallint', nullable: true }),
__metadata("design:type", Number)
], AuditLog.prototype, "response_status", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'integer', nullable: true }),
__metadata("design:type", Number)
], AuditLog.prototype, "duration_ms", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], AuditLog.prototype, "description", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
__metadata("design:type", Object)
], AuditLog.prototype, "metadata", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ type: 'timestamp with time zone' }),
__metadata("design:type", Date)
], AuditLog.prototype, "created_at", void 0);
exports.AuditLog = AuditLog = __decorate([
(0, typeorm_1.Entity)({ name: 'audit_logs', schema: 'audit' }),
(0, typeorm_1.Index)(['tenant_id', 'created_at']),
(0, typeorm_1.Index)(['entity_type', 'entity_id']),
(0, typeorm_1.Index)(['user_id', 'created_at'])
], AuditLog);
//# sourceMappingURL=audit-log.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"audit-log.entity.js","sourceRoot":"","sources":["../../../../src/modules/audit/entities/audit-log.entity.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,2 @@
export * from './audit-log.entity';
export * from './activity-log.entity';

View File

@ -0,0 +1,19 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./audit-log.entity"), exports);
__exportStar(require("./activity-log.entity"), exports);
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/audit/entities/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,wDAAsC"}

View File

@ -0,0 +1,6 @@
export * from './audit.module';
export * from './audit.controller';
export * from './services';
export * from './entities';
export * from './dto';
export { AuditInterceptor, AuditActionDecorator, AuditEntity, SkipAudit, AUDIT_ACTION_KEY, AUDIT_ENTITY_KEY, SKIP_AUDIT_KEY } from './interceptors';

View File

@ -0,0 +1,31 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SKIP_AUDIT_KEY = exports.AUDIT_ENTITY_KEY = exports.AUDIT_ACTION_KEY = exports.SkipAudit = exports.AuditEntity = exports.AuditActionDecorator = exports.AuditInterceptor = void 0;
__exportStar(require("./audit.module"), exports);
__exportStar(require("./audit.controller"), exports);
__exportStar(require("./services"), exports);
__exportStar(require("./entities"), exports);
__exportStar(require("./dto"), exports);
var interceptors_1 = require("./interceptors");
Object.defineProperty(exports, "AuditInterceptor", { enumerable: true, get: function () { return interceptors_1.AuditInterceptor; } });
Object.defineProperty(exports, "AuditActionDecorator", { enumerable: true, get: function () { return interceptors_1.AuditActionDecorator; } });
Object.defineProperty(exports, "AuditEntity", { enumerable: true, get: function () { return interceptors_1.AuditEntity; } });
Object.defineProperty(exports, "SkipAudit", { enumerable: true, get: function () { return interceptors_1.SkipAudit; } });
Object.defineProperty(exports, "AUDIT_ACTION_KEY", { enumerable: true, get: function () { return interceptors_1.AUDIT_ACTION_KEY; } });
Object.defineProperty(exports, "AUDIT_ENTITY_KEY", { enumerable: true, get: function () { return interceptors_1.AUDIT_ENTITY_KEY; } });
Object.defineProperty(exports, "SKIP_AUDIT_KEY", { enumerable: true, get: function () { return interceptors_1.SKIP_AUDIT_KEY; } });
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/audit/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,iDAA+B;AAC/B,qDAAmC;AACnC,6CAA2B;AAC3B,6CAA2B;AAC3B,wCAAsB;AACtB,+CAAoJ;AAA3I,gHAAA,gBAAgB,OAAA;AAAE,oHAAA,oBAAoB,OAAA;AAAE,2GAAA,WAAW,OAAA;AAAE,yGAAA,SAAS,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAAE,8GAAA,cAAc,OAAA"}

View File

@ -0,0 +1,22 @@
import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import { AuditService } from '../services/audit.service';
import { AuditAction as AuditActionEnum } from '../entities/audit-log.entity';
export declare const AUDIT_ACTION_KEY = "audit_action";
export declare const AUDIT_ENTITY_KEY = "audit_entity";
export declare const SKIP_AUDIT_KEY = "skip_audit";
export declare function AuditActionDecorator(action: AuditActionEnum): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
export declare function AuditEntity(entityType: string): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
export declare function SkipAudit(): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
export declare class AuditInterceptor implements NestInterceptor {
private readonly auditService;
private readonly reflector;
constructor(auditService: AuditService, reflector: Reflector);
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
private inferActionFromMethod;
private logAudit;
private inferEntityFromPath;
private sanitizeBody;
private getClientIp;
}

View File

@ -0,0 +1,153 @@
"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.AuditInterceptor = exports.SKIP_AUDIT_KEY = exports.AUDIT_ENTITY_KEY = exports.AUDIT_ACTION_KEY = void 0;
exports.AuditActionDecorator = AuditActionDecorator;
exports.AuditEntity = AuditEntity;
exports.SkipAudit = SkipAudit;
const common_1 = require("@nestjs/common");
const rxjs_1 = require("rxjs");
const core_1 = require("@nestjs/core");
const audit_service_1 = require("../services/audit.service");
const audit_log_entity_1 = require("../entities/audit-log.entity");
exports.AUDIT_ACTION_KEY = 'audit_action';
exports.AUDIT_ENTITY_KEY = 'audit_entity';
exports.SKIP_AUDIT_KEY = 'skip_audit';
function AuditActionDecorator(action) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(exports.AUDIT_ACTION_KEY, action, descriptor.value);
return descriptor;
};
}
function AuditEntity(entityType) {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(exports.AUDIT_ENTITY_KEY, entityType, descriptor.value);
return descriptor;
};
}
function SkipAudit() {
return (target, propertyKey, descriptor) => {
Reflect.defineMetadata(exports.SKIP_AUDIT_KEY, true, descriptor.value);
return descriptor;
};
}
let AuditInterceptor = class AuditInterceptor {
constructor(auditService, reflector) {
this.auditService = auditService;
this.reflector = reflector;
}
intercept(context, next) {
const request = context.switchToHttp().getRequest();
const handler = context.getHandler();
const startTime = Date.now();
const skipAudit = this.reflector.get(exports.SKIP_AUDIT_KEY, handler);
if (skipAudit) {
return next.handle();
}
const auditAction = this.reflector.get(exports.AUDIT_ACTION_KEY, handler);
const entityType = this.reflector.get(exports.AUDIT_ENTITY_KEY, handler);
const action = auditAction || this.inferActionFromMethod(request.method);
if (!action) {
return next.handle();
}
return next.handle().pipe((0, rxjs_1.tap)({
next: async (response) => {
const duration = Date.now() - startTime;
await this.logAudit(request, action, entityType, response, 200, duration);
},
error: async (error) => {
const duration = Date.now() - startTime;
const statusCode = error.status || 500;
await this.logAudit(request, action, entityType, null, statusCode, duration);
},
}));
}
inferActionFromMethod(method) {
switch (method.toUpperCase()) {
case 'POST':
return audit_log_entity_1.AuditAction.CREATE;
case 'PUT':
case 'PATCH':
return audit_log_entity_1.AuditAction.UPDATE;
case 'DELETE':
return audit_log_entity_1.AuditAction.DELETE;
case 'GET':
return null;
default:
return null;
}
}
async logAudit(request, action, entityType, response, statusCode, duration) {
try {
const tenantId = request.user?.tenantId || request.headers['x-tenant-id'];
if (!tenantId) {
return;
}
const params = {
tenant_id: tenantId,
user_id: request.user?.sub || request.user?.id,
action,
entity_type: entityType || this.inferEntityFromPath(request.path),
entity_id: request.params?.id,
old_values: request.body?._oldValues,
new_values: this.sanitizeBody(request.body),
ip_address: this.getClientIp(request),
user_agent: request.headers['user-agent'],
endpoint: request.path,
http_method: request.method,
response_status: statusCode,
duration_ms: duration,
metadata: {
query: request.query,
response_id: response?.id,
},
};
await this.auditService.createAuditLog(params);
}
catch (error) {
console.error('Failed to create audit log:', error);
}
}
inferEntityFromPath(path) {
const segments = path.split('/').filter(Boolean);
const apiIndex = segments.findIndex((s) => s === 'api');
if (apiIndex !== -1 && segments.length > apiIndex + 2) {
return segments[apiIndex + 2];
}
return segments[segments.length - 1] || 'unknown';
}
sanitizeBody(body) {
if (!body)
return undefined;
const sanitized = { ...body };
const sensitiveFields = ['password', 'token', 'secret', 'creditCard', 'cvv', '_oldValues'];
for (const field of sensitiveFields) {
if (field in sanitized) {
sanitized[field] = '[REDACTED]';
}
}
return sanitized;
}
getClientIp(request) {
return (request.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
request.headers['x-real-ip'] ||
request.connection?.remoteAddress ||
request.ip ||
'unknown');
}
};
exports.AuditInterceptor = AuditInterceptor;
exports.AuditInterceptor = AuditInterceptor = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [audit_service_1.AuditService,
core_1.Reflector])
], AuditInterceptor);
//# sourceMappingURL=audit.interceptor.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"audit.interceptor.js","sourceRoot":"","sources":["../../../../src/modules/audit/interceptors/audit.interceptor.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1 @@
export { AuditInterceptor, AuditActionDecorator, AuditEntity, SkipAudit, AUDIT_ACTION_KEY, AUDIT_ENTITY_KEY, SKIP_AUDIT_KEY } from './audit.interceptor';

View File

@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SKIP_AUDIT_KEY = exports.AUDIT_ENTITY_KEY = exports.AUDIT_ACTION_KEY = exports.SkipAudit = exports.AuditEntity = exports.AuditActionDecorator = exports.AuditInterceptor = void 0;
var audit_interceptor_1 = require("./audit.interceptor");
Object.defineProperty(exports, "AuditInterceptor", { enumerable: true, get: function () { return audit_interceptor_1.AuditInterceptor; } });
Object.defineProperty(exports, "AuditActionDecorator", { enumerable: true, get: function () { return audit_interceptor_1.AuditActionDecorator; } });
Object.defineProperty(exports, "AuditEntity", { enumerable: true, get: function () { return audit_interceptor_1.AuditEntity; } });
Object.defineProperty(exports, "SkipAudit", { enumerable: true, get: function () { return audit_interceptor_1.SkipAudit; } });
Object.defineProperty(exports, "AUDIT_ACTION_KEY", { enumerable: true, get: function () { return audit_interceptor_1.AUDIT_ACTION_KEY; } });
Object.defineProperty(exports, "AUDIT_ENTITY_KEY", { enumerable: true, get: function () { return audit_interceptor_1.AUDIT_ENTITY_KEY; } });
Object.defineProperty(exports, "SKIP_AUDIT_KEY", { enumerable: true, get: function () { return audit_interceptor_1.SKIP_AUDIT_KEY; } });
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/audit/interceptors/index.ts"],"names":[],"mappings":";;;AAAA,yDAQ6B;AAP3B,qHAAA,gBAAgB,OAAA;AAChB,yHAAA,oBAAoB,OAAA;AACpB,gHAAA,WAAW,OAAA;AACX,8GAAA,SAAS,OAAA;AACT,qHAAA,gBAAgB,OAAA;AAChB,qHAAA,gBAAgB,OAAA;AAChB,mHAAA,cAAc,OAAA"}

View File

@ -0,0 +1,61 @@
import { Repository } from 'typeorm';
import { AuditLog, AuditAction } from '../entities/audit-log.entity';
import { ActivityLog, ActivityType } from '../entities/activity-log.entity';
import { QueryAuditLogsDto } from '../dto/query-audit.dto';
import { QueryActivityLogsDto } from '../dto/query-activity.dto';
import { CreateActivityLogDto } from '../dto/create-activity.dto';
export interface CreateAuditLogParams {
tenant_id: string;
user_id?: string;
action: AuditAction;
entity_type: string;
entity_id?: string;
old_values?: Record<string, any>;
new_values?: Record<string, any>;
ip_address?: string;
user_agent?: string;
endpoint?: string;
http_method?: string;
response_status?: number;
duration_ms?: number;
description?: string;
metadata?: Record<string, any>;
}
export interface PaginatedResult<T> {
data: T[];
total: number;
page: number;
limit: number;
totalPages: number;
}
export declare class AuditService {
private readonly auditLogRepository;
private readonly activityLogRepository;
constructor(auditLogRepository: Repository<AuditLog>, activityLogRepository: Repository<ActivityLog>);
createAuditLog(params: CreateAuditLogParams): Promise<AuditLog>;
queryAuditLogs(tenantId: string, query: QueryAuditLogsDto): Promise<PaginatedResult<AuditLog>>;
getAuditLogById(tenantId: string, id: string): Promise<AuditLog | null>;
getEntityAuditHistory(tenantId: string, entityType: string, entityId: string): Promise<AuditLog[]>;
createActivityLog(tenantId: string, userId: string, dto: CreateActivityLogDto, context?: {
ip_address?: string;
user_agent?: string;
session_id?: string;
}): Promise<ActivityLog>;
queryActivityLogs(tenantId: string, query: QueryActivityLogsDto): Promise<PaginatedResult<ActivityLog>>;
getUserActivitySummary(tenantId: string, userId: string, days?: number): Promise<{
activity_type: ActivityType;
count: number;
}[]>;
getAuditStats(tenantId: string, days?: number): Promise<{
total_actions: number;
actions_by_type: {
action: AuditAction;
count: number;
}[];
top_users: {
user_id: string;
count: number;
}[];
}>;
private detectChangedFields;
}

View File

@ -0,0 +1,221 @@
"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.AuditService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const audit_log_entity_1 = require("../entities/audit-log.entity");
const activity_log_entity_1 = require("../entities/activity-log.entity");
let AuditService = class AuditService {
constructor(auditLogRepository, activityLogRepository) {
this.auditLogRepository = auditLogRepository;
this.activityLogRepository = activityLogRepository;
}
async createAuditLog(params) {
const changedFields = this.detectChangedFields(params.old_values, params.new_values);
const auditLog = this.auditLogRepository.create({
...params,
changed_fields: changedFields,
});
return this.auditLogRepository.save(auditLog);
}
async queryAuditLogs(tenantId, query) {
const { user_id, action, entity_type, entity_id, from_date, to_date, page = 1, limit = 20 } = query;
const queryBuilder = this.auditLogRepository
.createQueryBuilder('audit')
.where('audit.tenant_id = :tenantId', { tenantId });
if (user_id) {
queryBuilder.andWhere('audit.user_id = :user_id', { user_id });
}
if (action) {
queryBuilder.andWhere('audit.action = :action', { action });
}
if (entity_type) {
queryBuilder.andWhere('audit.entity_type = :entity_type', { entity_type });
}
if (entity_id) {
queryBuilder.andWhere('audit.entity_id = :entity_id', { entity_id });
}
if (from_date && to_date) {
queryBuilder.andWhere('audit.created_at BETWEEN :from_date AND :to_date', {
from_date,
to_date,
});
}
else if (from_date) {
queryBuilder.andWhere('audit.created_at >= :from_date', { from_date });
}
else if (to_date) {
queryBuilder.andWhere('audit.created_at <= :to_date', { to_date });
}
queryBuilder
.orderBy('audit.created_at', 'DESC')
.skip((page - 1) * limit)
.take(limit);
const [data, total] = await queryBuilder.getManyAndCount();
return {
data,
total,
page,
limit,
totalPages: Math.ceil(total / limit),
};
}
async getAuditLogById(tenantId, id) {
return this.auditLogRepository.findOne({
where: { id, tenant_id: tenantId },
});
}
async getEntityAuditHistory(tenantId, entityType, entityId) {
return this.auditLogRepository.find({
where: {
tenant_id: tenantId,
entity_type: entityType,
entity_id: entityId,
},
order: { created_at: 'DESC' },
});
}
async createActivityLog(tenantId, userId, dto, context) {
const activityLog = this.activityLogRepository.create({
tenant_id: tenantId,
user_id: userId,
...dto,
ip_address: context?.ip_address,
user_agent: context?.user_agent,
session_id: context?.session_id,
});
return this.activityLogRepository.save(activityLog);
}
async queryActivityLogs(tenantId, query) {
const { user_id, activity_type, resource_type, from_date, to_date, page = 1, limit = 20 } = query;
const queryBuilder = this.activityLogRepository
.createQueryBuilder('activity')
.where('activity.tenant_id = :tenantId', { tenantId });
if (user_id) {
queryBuilder.andWhere('activity.user_id = :user_id', { user_id });
}
if (activity_type) {
queryBuilder.andWhere('activity.activity_type = :activity_type', { activity_type });
}
if (resource_type) {
queryBuilder.andWhere('activity.resource_type = :resource_type', { resource_type });
}
if (from_date && to_date) {
queryBuilder.andWhere('activity.created_at BETWEEN :from_date AND :to_date', {
from_date,
to_date,
});
}
else if (from_date) {
queryBuilder.andWhere('activity.created_at >= :from_date', { from_date });
}
else if (to_date) {
queryBuilder.andWhere('activity.created_at <= :to_date', { to_date });
}
queryBuilder
.orderBy('activity.created_at', 'DESC')
.skip((page - 1) * limit)
.take(limit);
const [data, total] = await queryBuilder.getManyAndCount();
return {
data,
total,
page,
limit,
totalPages: Math.ceil(total / limit),
};
}
async getUserActivitySummary(tenantId, userId, days = 30) {
const fromDate = new Date();
fromDate.setDate(fromDate.getDate() - days);
const result = await this.activityLogRepository
.createQueryBuilder('activity')
.select('activity.activity_type', 'activity_type')
.addSelect('COUNT(*)', 'count')
.where('activity.tenant_id = :tenantId', { tenantId })
.andWhere('activity.user_id = :userId', { userId })
.andWhere('activity.created_at >= :fromDate', { fromDate })
.groupBy('activity.activity_type')
.getRawMany();
return result.map((r) => ({
activity_type: r.activity_type,
count: parseInt(r.count, 10),
}));
}
async getAuditStats(tenantId, days = 7) {
const fromDate = new Date();
fromDate.setDate(fromDate.getDate() - days);
const [totalActions, actionsByType, topUsers] = await Promise.all([
this.auditLogRepository.count({
where: {
tenant_id: tenantId,
created_at: (0, typeorm_2.MoreThanOrEqual)(fromDate),
},
}),
this.auditLogRepository
.createQueryBuilder('audit')
.select('audit.action', 'action')
.addSelect('COUNT(*)', 'count')
.where('audit.tenant_id = :tenantId', { tenantId })
.andWhere('audit.created_at >= :fromDate', { fromDate })
.groupBy('audit.action')
.getRawMany(),
this.auditLogRepository
.createQueryBuilder('audit')
.select('audit.user_id', 'user_id')
.addSelect('COUNT(*)', 'count')
.where('audit.tenant_id = :tenantId', { tenantId })
.andWhere('audit.created_at >= :fromDate', { fromDate })
.andWhere('audit.user_id IS NOT NULL')
.groupBy('audit.user_id')
.orderBy('count', 'DESC')
.limit(10)
.getRawMany(),
]);
return {
total_actions: totalActions,
actions_by_type: actionsByType.map((r) => ({
action: r.action,
count: parseInt(r.count, 10),
})),
top_users: topUsers.map((r) => ({
user_id: r.user_id,
count: parseInt(r.count, 10),
})),
};
}
detectChangedFields(oldValues, newValues) {
if (!oldValues || !newValues)
return [];
const changedFields = [];
const allKeys = new Set([...Object.keys(oldValues), ...Object.keys(newValues)]);
for (const key of allKeys) {
if (JSON.stringify(oldValues[key]) !== JSON.stringify(newValues[key])) {
changedFields.push(key);
}
}
return changedFields;
}
};
exports.AuditService = AuditService;
exports.AuditService = AuditService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(audit_log_entity_1.AuditLog)),
__param(1, (0, typeorm_1.InjectRepository)(activity_log_entity_1.ActivityLog)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository])
], AuditService);
//# sourceMappingURL=audit.service.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
export * from './audit.service';

View File

@ -0,0 +1,18 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./audit.service"), exports);
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/audit/services/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC"}

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,159 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const auth_controller_1 = require("../auth.controller");
const auth_service_1 = require("../services/auth.service");
describe('AuthController', () => {
let controller;
let authService;
const mockUser = {
id: '550e8400-e29b-41d4-a716-446655440000',
tenant_id: '550e8400-e29b-41d4-a716-446655440001',
email: 'test@example.com',
first_name: 'Test',
last_name: 'User',
};
const mockAuthResponse = {
user: mockUser,
accessToken: 'access_token',
refreshToken: 'refresh_token',
};
const mockRequestUser = {
id: mockUser.id,
email: mockUser.email,
tenant_id: mockUser.tenant_id,
};
const mockRequest = {
ip: '127.0.0.1',
headers: {
'user-agent': 'test-agent',
'x-tenant-id': mockUser.tenant_id,
},
};
beforeEach(async () => {
const mockAuthService = {
register: jest.fn(),
login: jest.fn(),
logout: jest.fn(),
logoutAll: jest.fn(),
refreshToken: jest.fn(),
changePassword: jest.fn(),
requestPasswordReset: jest.fn(),
resetPassword: jest.fn(),
verifyEmail: jest.fn(),
getProfile: jest.fn(),
};
const module = await testing_1.Test.createTestingModule({
controllers: [auth_controller_1.AuthController],
providers: [{ provide: auth_service_1.AuthService, useValue: mockAuthService }],
}).compile();
controller = module.get(auth_controller_1.AuthController);
authService = module.get(auth_service_1.AuthService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('register', () => {
it('should register a new user', async () => {
const registerDto = {
email: 'new@example.com',
password: 'Password123!',
first_name: 'New',
last_name: 'User',
};
authService.register.mockResolvedValue(mockAuthResponse);
const result = await controller.register(registerDto, mockRequest);
expect(result).toEqual(mockAuthResponse);
expect(authService.register).toHaveBeenCalledWith(registerDto, mockUser.tenant_id, mockRequest.ip, mockRequest.headers['user-agent']);
});
});
describe('login', () => {
it('should login user', async () => {
const loginDto = {
email: 'test@example.com',
password: 'password123',
};
authService.login.mockResolvedValue(mockAuthResponse);
const result = await controller.login(loginDto, mockRequest);
expect(result).toEqual(mockAuthResponse);
expect(authService.login).toHaveBeenCalledWith(loginDto, mockUser.tenant_id, mockRequest.ip, mockRequest.headers['user-agent']);
});
});
describe('logout', () => {
it('should logout user', async () => {
authService.logout.mockResolvedValue(undefined);
const result = await controller.logout(mockRequestUser, 'session_token');
expect(result).toEqual({ message: 'Sesión cerrada correctamente' });
expect(authService.logout).toHaveBeenCalledWith(mockRequestUser.id, 'session_token');
});
});
describe('logoutAll', () => {
it('should logout all sessions', async () => {
authService.logoutAll.mockResolvedValue(undefined);
const result = await controller.logoutAll(mockRequestUser);
expect(result).toEqual({ message: 'Todas las sesiones cerradas' });
expect(authService.logoutAll).toHaveBeenCalledWith(mockRequestUser.id);
});
});
describe('refreshToken', () => {
it('should refresh tokens', async () => {
const newTokens = {
accessToken: 'new_access_token',
refreshToken: 'new_refresh_token',
};
authService.refreshToken.mockResolvedValue(newTokens);
const result = await controller.refreshToken({ refresh_token: 'old_refresh_token' }, mockRequest);
expect(result).toEqual(newTokens);
});
});
describe('changePassword', () => {
it('should change password', async () => {
const changePasswordDto = {
currentPassword: 'oldPassword',
newPassword: 'newPassword',
};
authService.changePassword.mockResolvedValue({
message: 'Password actualizado correctamente',
});
const result = await controller.changePassword(mockRequestUser, changePasswordDto);
expect(result.message).toBe('Password actualizado correctamente');
expect(authService.changePassword).toHaveBeenCalledWith(mockRequestUser.id, changePasswordDto);
});
});
describe('requestPasswordReset', () => {
it('should request password reset', async () => {
authService.requestPasswordReset.mockResolvedValue({
message: 'Si el email existe, recibirás instrucciones',
});
const result = await controller.requestPasswordReset({ email: 'test@example.com' }, mockRequest);
expect(result).toHaveProperty('message');
});
});
describe('resetPassword', () => {
it('should reset password', async () => {
authService.resetPassword.mockResolvedValue({
message: 'Password restablecido correctamente',
});
const result = await controller.resetPassword({ token: 'reset_token', password: 'newPassword123' }, mockRequest);
expect(result.message).toBe('Password restablecido correctamente');
});
});
describe('verifyEmail', () => {
it('should verify email', async () => {
authService.verifyEmail.mockResolvedValue({
message: 'Email verificado correctamente',
});
const result = await controller.verifyEmail('verification_token', mockRequest);
expect(result.message).toBe('Email verificado correctamente');
});
});
describe('getProfile', () => {
it('should get user profile', async () => {
authService.getProfile.mockResolvedValue(mockUser);
const result = await controller.getProfile(mockRequestUser);
expect(result).toEqual(mockUser);
expect(authService.getProfile).toHaveBeenCalledWith(mockRequestUser.id);
});
});
});
//# sourceMappingURL=auth.controller.spec.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,337 @@
"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 __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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const typeorm_1 = require("@nestjs/typeorm");
const jwt_1 = require("@nestjs/jwt");
const config_1 = require("@nestjs/config");
const typeorm_2 = require("typeorm");
const common_1 = require("@nestjs/common");
const bcrypt = __importStar(require("bcrypt"));
const auth_service_1 = require("../services/auth.service");
const entities_1 = require("../entities");
jest.mock('bcrypt');
const mockedBcrypt = bcrypt;
describe('AuthService', () => {
let service;
let userRepository;
let sessionRepository;
let tokenRepository;
let jwtService;
let configService;
const mockUser = {
id: '550e8400-e29b-41d4-a716-446655440000',
tenant_id: '550e8400-e29b-41d4-a716-446655440001',
email: 'test@example.com',
password_hash: 'hashed_password',
first_name: 'Test',
last_name: 'User',
status: 'active',
email_verified: true,
};
const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
beforeEach(async () => {
const mockUserRepo = {
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
update: jest.fn(),
};
const mockSessionRepo = {
findOne: jest.fn(),
save: jest.fn(),
update: jest.fn(),
};
const mockTokenRepo = {
findOne: jest.fn(),
save: jest.fn(),
update: jest.fn(),
};
const mockJwtService = {
sign: jest.fn(),
verify: jest.fn(),
};
const mockConfigService = {
get: jest.fn(),
};
const mockDataSource = {
createQueryRunner: jest.fn(),
};
const module = await testing_1.Test.createTestingModule({
providers: [
auth_service_1.AuthService,
{ provide: (0, typeorm_1.getRepositoryToken)(entities_1.User), useValue: mockUserRepo },
{ provide: (0, typeorm_1.getRepositoryToken)(entities_1.Session), useValue: mockSessionRepo },
{ provide: (0, typeorm_1.getRepositoryToken)(entities_1.Token), useValue: mockTokenRepo },
{ provide: jwt_1.JwtService, useValue: mockJwtService },
{ provide: config_1.ConfigService, useValue: mockConfigService },
{ provide: typeorm_2.DataSource, useValue: mockDataSource },
],
}).compile();
service = module.get(auth_service_1.AuthService);
userRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.User));
sessionRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.Session));
tokenRepository = module.get((0, typeorm_1.getRepositoryToken)(entities_1.Token));
jwtService = module.get(jwt_1.JwtService);
configService = module.get(config_1.ConfigService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('register', () => {
const registerDto = {
email: 'newuser@example.com',
password: 'SecurePass123!',
first_name: 'New',
last_name: 'User',
};
it('should register a new user successfully', async () => {
userRepository.findOne.mockResolvedValue(null);
mockedBcrypt.hash.mockResolvedValue('hashed_password');
userRepository.create.mockReturnValue({
...mockUser,
email: registerDto.email,
status: 'pending_verification',
});
userRepository.save.mockResolvedValue({
...mockUser,
email: registerDto.email,
status: 'pending_verification',
});
sessionRepository.save.mockResolvedValue({});
tokenRepository.save.mockResolvedValue({});
jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token');
configService.get.mockReturnValue('15m');
const result = await service.register(registerDto, mockTenantId);
expect(result).toHaveProperty('user');
expect(result).toHaveProperty('accessToken');
expect(result).toHaveProperty('refreshToken');
expect(userRepository.findOne).toHaveBeenCalled();
expect(userRepository.save).toHaveBeenCalled();
});
it('should throw ConflictException if email already exists', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
await expect(service.register(registerDto, mockTenantId)).rejects.toThrow(common_1.ConflictException);
});
});
describe('login', () => {
const loginDto = {
email: 'test@example.com',
password: 'password123',
};
it('should login user successfully', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
mockedBcrypt.compare.mockResolvedValue(true);
userRepository.save.mockResolvedValue(mockUser);
sessionRepository.save.mockResolvedValue({});
jwtService.sign.mockReturnValueOnce('access_token').mockReturnValueOnce('refresh_token');
configService.get.mockReturnValue('15m');
const result = await service.login(loginDto, mockTenantId);
expect(result).toHaveProperty('user');
expect(result).toHaveProperty('accessToken');
expect(result).toHaveProperty('refreshToken');
expect(result.user).not.toHaveProperty('password_hash');
});
it('should throw UnauthorizedException for invalid email', async () => {
userRepository.findOne.mockResolvedValue(null);
await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException);
});
it('should throw UnauthorizedException for invalid password', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
mockedBcrypt.compare.mockResolvedValue(false);
await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException);
});
it('should throw UnauthorizedException for suspended user', async () => {
userRepository.findOne.mockResolvedValue({
...mockUser,
status: 'suspended',
});
mockedBcrypt.compare.mockResolvedValue(true);
await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException);
});
it('should throw UnauthorizedException for inactive user', async () => {
userRepository.findOne.mockResolvedValue({
...mockUser,
status: 'inactive',
});
mockedBcrypt.compare.mockResolvedValue(true);
await expect(service.login(loginDto, mockTenantId)).rejects.toThrow(common_1.UnauthorizedException);
});
});
describe('logout', () => {
it('should invalidate session successfully', async () => {
sessionRepository.update.mockResolvedValue({ affected: 1 });
await service.logout(mockUser.id, 'session_token');
expect(sessionRepository.update).toHaveBeenCalledWith({ user_id: mockUser.id, session_token: 'session_token' }, { is_active: false });
});
});
describe('logoutAll', () => {
it('should invalidate all sessions for user', async () => {
sessionRepository.update.mockResolvedValue({ affected: 3 });
await service.logoutAll(mockUser.id);
expect(sessionRepository.update).toHaveBeenCalledWith({ user_id: mockUser.id }, { is_active: false });
});
});
describe('changePassword', () => {
const changePasswordDto = {
currentPassword: 'oldPassword123',
newPassword: 'newPassword456',
};
it('should change password successfully', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
mockedBcrypt.compare.mockResolvedValue(true);
mockedBcrypt.hash.mockResolvedValue('new_hashed_password');
userRepository.update.mockResolvedValue({ affected: 1 });
const result = await service.changePassword(mockUser.id, changePasswordDto);
expect(result).toHaveProperty('message');
expect(userRepository.update).toHaveBeenCalled();
});
it('should throw NotFoundException if user not found', async () => {
userRepository.findOne.mockResolvedValue(null);
await expect(service.changePassword('invalid-id', changePasswordDto)).rejects.toThrow(common_1.NotFoundException);
});
it('should throw BadRequestException for incorrect current password', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
mockedBcrypt.compare.mockResolvedValue(false);
await expect(service.changePassword(mockUser.id, changePasswordDto)).rejects.toThrow(common_1.BadRequestException);
});
it('should throw BadRequestException if new password same as current', async () => {
const samePasswordDto = {
currentPassword: 'samePassword',
newPassword: 'samePassword',
};
userRepository.findOne.mockResolvedValue(mockUser);
mockedBcrypt.compare.mockResolvedValue(true);
await expect(service.changePassword(mockUser.id, samePasswordDto)).rejects.toThrow(common_1.BadRequestException);
});
});
describe('requestPasswordReset', () => {
it('should create reset token for existing user', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
tokenRepository.save.mockResolvedValue({});
const result = await service.requestPasswordReset(mockUser.email, mockTenantId);
expect(result).toHaveProperty('message');
expect(tokenRepository.save).toHaveBeenCalled();
});
it('should return success message even for non-existing email (security)', async () => {
userRepository.findOne.mockResolvedValue(null);
const result = await service.requestPasswordReset('nonexistent@example.com', mockTenantId);
expect(result).toHaveProperty('message');
expect(tokenRepository.save).not.toHaveBeenCalled();
});
});
describe('resetPassword', () => {
const mockToken = {
id: 'token-id',
user_id: mockUser.id,
tenant_id: mockTenantId,
token_type: 'password_reset',
is_used: false,
expires_at: new Date(Date.now() + 3600000),
};
it('should reset password successfully', async () => {
tokenRepository.findOne.mockResolvedValue(mockToken);
mockedBcrypt.hash.mockResolvedValue('new_hashed_password');
userRepository.update.mockResolvedValue({ affected: 1 });
tokenRepository.update.mockResolvedValue({ affected: 1 });
sessionRepository.update.mockResolvedValue({ affected: 1 });
const result = await service.resetPassword('valid_token', 'newPassword123', mockTenantId);
expect(result).toHaveProperty('message');
expect(userRepository.update).toHaveBeenCalled();
expect(tokenRepository.update).toHaveBeenCalled();
});
it('should throw BadRequestException for invalid token', async () => {
tokenRepository.findOne.mockResolvedValue(null);
await expect(service.resetPassword('invalid_token', 'newPassword123', mockTenantId)).rejects.toThrow(common_1.BadRequestException);
});
it('should throw BadRequestException for expired token', async () => {
tokenRepository.findOne.mockResolvedValue({
...mockToken,
expires_at: new Date(Date.now() - 3600000),
});
await expect(service.resetPassword('expired_token', 'newPassword123', mockTenantId)).rejects.toThrow(common_1.BadRequestException);
});
});
describe('verifyEmail', () => {
const mockToken = {
id: 'token-id',
user_id: mockUser.id,
tenant_id: mockTenantId,
token_type: 'email_verification',
is_used: false,
expires_at: new Date(Date.now() + 3600000),
};
it('should verify email successfully', async () => {
tokenRepository.findOne.mockResolvedValue(mockToken);
userRepository.update.mockResolvedValue({ affected: 1 });
tokenRepository.update.mockResolvedValue({ affected: 1 });
const result = await service.verifyEmail('valid_token', mockTenantId);
expect(result).toHaveProperty('message');
expect(userRepository.update).toHaveBeenCalledWith({ id: mockToken.user_id }, expect.objectContaining({
email_verified: true,
status: 'active',
}));
});
it('should throw BadRequestException for invalid token', async () => {
tokenRepository.findOne.mockResolvedValue(null);
await expect(service.verifyEmail('invalid_token', mockTenantId)).rejects.toThrow(common_1.BadRequestException);
});
});
describe('validateUser', () => {
it('should return user if active', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
const result = await service.validateUser(mockUser.id);
expect(result).toEqual(mockUser);
});
it('should return null if user not found or not active', async () => {
userRepository.findOne.mockResolvedValue(null);
const result = await service.validateUser('invalid-id');
expect(result).toBeNull();
});
});
describe('getProfile', () => {
it('should return sanitized user profile', async () => {
userRepository.findOne.mockResolvedValue(mockUser);
const result = await service.getProfile(mockUser.id);
expect(result).not.toHaveProperty('password_hash');
expect(result).toHaveProperty('email');
});
it('should throw NotFoundException if user not found', async () => {
userRepository.findOne.mockResolvedValue(null);
await expect(service.getProfile('invalid-id')).rejects.toThrow(common_1.NotFoundException);
});
});
});
//# sourceMappingURL=auth.service.spec.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const config_1 = require("@nestjs/config");
const common_1 = require("@nestjs/common");
const jwt_strategy_1 = require("../strategies/jwt.strategy");
const auth_service_1 = require("../services/auth.service");
describe('JwtStrategy', () => {
let strategy;
let authService;
const mockUser = {
id: '550e8400-e29b-41d4-a716-446655440000',
tenant_id: '550e8400-e29b-41d4-a716-446655440001',
email: 'test@example.com',
first_name: 'Test',
last_name: 'User',
status: 'active',
};
const mockPayload = {
sub: mockUser.id,
email: mockUser.email,
tenant_id: mockUser.tenant_id,
};
beforeEach(async () => {
const mockAuthService = {
validateUser: jest.fn(),
};
const mockConfigService = {
get: jest.fn().mockReturnValue('test-secret'),
};
const module = await testing_1.Test.createTestingModule({
providers: [
jwt_strategy_1.JwtStrategy,
{ provide: auth_service_1.AuthService, useValue: mockAuthService },
{ provide: config_1.ConfigService, useValue: mockConfigService },
],
}).compile();
strategy = module.get(jwt_strategy_1.JwtStrategy);
authService = module.get(auth_service_1.AuthService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('validate', () => {
it('should return RequestUser for valid payload', async () => {
authService.validateUser.mockResolvedValue(mockUser);
const result = await strategy.validate(mockPayload);
expect(result).toEqual({
id: mockUser.id,
email: mockUser.email,
tenant_id: mockUser.tenant_id,
});
expect(authService.validateUser).toHaveBeenCalledWith(mockPayload.sub);
});
it('should throw UnauthorizedException for invalid user', async () => {
authService.validateUser.mockResolvedValue(null);
await expect(strategy.validate(mockPayload)).rejects.toThrow(common_1.UnauthorizedException);
});
it('should include tenant_id from payload in result', async () => {
authService.validateUser.mockResolvedValue(mockUser);
const result = await strategy.validate(mockPayload);
expect(result.tenant_id).toBe(mockPayload.tenant_id);
});
});
});
//# sourceMappingURL=jwt.strategy.spec.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"jwt.strategy.spec.js","sourceRoot":"","sources":["../../../../src/modules/auth/__tests__/jwt.strategy.spec.ts"],"names":[],"mappings":";;AAAA,6CAAsD;AACtD,2CAA+C;AAC/C,2CAAuD;AACvD,6DAAsE;AACtE,2DAAuD;AAEvD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,QAAqB,CAAC;IAC1B,IAAI,WAAqC,CAAC;IAE1C,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,sCAAsC;QAC1C,SAAS,EAAE,sCAAsC;QACjD,KAAK,EAAE,kBAAkB;QACzB,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE,MAAM;QACjB,MAAM,EAAE,QAAQ;KACjB,CAAC;IAEF,MAAM,WAAW,GAAG;QAClB,GAAG,EAAE,QAAQ,CAAC,EAAE;QAChB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;KAC9B,CAAC;IAEF,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,eAAe,GAAG;YACtB,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE;SACxB,CAAC;QAEF,MAAM,iBAAiB,GAAG;YACxB,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,aAAa,CAAC;SAC9C,CAAC;QAEF,MAAM,MAAM,GAAkB,MAAM,cAAI,CAAC,mBAAmB,CAAC;YAC3D,SAAS,EAAE;gBACT,0BAAW;gBACX,EAAE,OAAO,EAAE,0BAAW,EAAE,QAAQ,EAAE,eAAe,EAAE;gBACnD,EAAE,OAAO,EAAE,sBAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE;aACxD;SACF,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAc,0BAAW,CAAC,CAAC;QAChD,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,0BAAW,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,WAAW,CAAC,YAAY,CAAC,iBAAiB,CAAC,QAAe,CAAC,CAAC;YAE5D,MAAM,MAAM,GAAgB,MAAM,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAEjE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBACrB,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;aAC9B,CAAC,CAAC;YACH,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,WAAW,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAEjD,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC1D,8BAAqB,CACtB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,WAAW,CAAC,YAAY,CAAC,iBAAiB,CAAC,QAAe,CAAC,CAAC;YAE5D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAEpD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@ -0,0 +1,33 @@
import { Request } from 'express';
import { AuthService, AuthResponse } from './services/auth.service';
import { LoginDto, RegisterDto, RequestPasswordResetDto, ResetPasswordDto, ChangePasswordDto } from './dto';
import { RequestUser } from './strategies/jwt.strategy';
export declare class AuthController {
private readonly authService;
constructor(authService: AuthService);
register(dto: RegisterDto, tenantId: string, req: Request): Promise<AuthResponse>;
login(dto: LoginDto, tenantId: string, req: Request): Promise<AuthResponse>;
logout(user: RequestUser, sessionToken: string): Promise<{
message: string;
}>;
logoutAll(user: RequestUser): Promise<{
message: string;
}>;
refresh(refreshToken: string, req: Request): Promise<{
accessToken: string;
refreshToken: string;
}>;
requestPasswordReset(dto: RequestPasswordResetDto, tenantId: string): Promise<{
message: string;
}>;
resetPassword(dto: ResetPasswordDto, tenantId: string): Promise<{
message: string;
}>;
changePassword(user: RequestUser, dto: ChangePasswordDto): Promise<{
message: string;
}>;
verifyEmail(token: string, tenantId: string): Promise<{
message: string;
}>;
getProfile(user: RequestUser): Promise<Partial<import("./entities").User>>;
}

View File

@ -0,0 +1,216 @@
"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 auth_service_1 = require("./services/auth.service");
const dto_1 = require("./dto");
const jwt_auth_guard_1 = require("./guards/jwt-auth.guard");
const public_decorator_1 = require("./decorators/public.decorator");
const current_user_decorator_1 = require("./decorators/current-user.decorator");
const tenant_decorator_1 = require("./decorators/tenant.decorator");
let AuthController = class AuthController {
constructor(authService) {
this.authService = authService;
}
async register(dto, tenantId, req) {
if (!tenantId) {
throw new common_1.BadRequestException('Tenant ID es requerido');
}
return this.authService.register(dto, tenantId, req.ip, req.headers['user-agent']);
}
async login(dto, tenantId, req) {
if (!tenantId) {
throw new common_1.BadRequestException('Tenant ID es requerido');
}
return this.authService.login(dto, tenantId, req.ip, req.headers['user-agent']);
}
async logout(user, sessionToken) {
await this.authService.logout(user.id, sessionToken);
return { message: 'Sesión cerrada correctamente' };
}
async logoutAll(user) {
await this.authService.logoutAll(user.id);
return { message: 'Todas las sesiones cerradas' };
}
async refresh(refreshToken, req) {
return this.authService.refreshToken(refreshToken, req.ip, req.headers['user-agent']);
}
async requestPasswordReset(dto, tenantId) {
if (!tenantId) {
throw new common_1.BadRequestException('Tenant ID es requerido');
}
return this.authService.requestPasswordReset(dto.email, tenantId);
}
async resetPassword(dto, tenantId) {
if (!tenantId) {
throw new common_1.BadRequestException('Tenant ID es requerido');
}
return this.authService.resetPassword(dto.token, dto.password, tenantId);
}
async changePassword(user, dto) {
return this.authService.changePassword(user.id, dto);
}
async verifyEmail(token, tenantId) {
if (!tenantId) {
throw new common_1.BadRequestException('Tenant ID es requerido');
}
return this.authService.verifyEmail(token, tenantId);
}
async getProfile(user) {
return this.authService.getProfile(user.id);
}
};
exports.AuthController = AuthController;
__decorate([
(0, common_1.Post)('register'),
(0, public_decorator_1.Public)(),
(0, swagger_1.ApiOperation)({ summary: 'Register new user' }),
(0, swagger_1.ApiHeader)({ name: 'x-tenant-id', required: true, description: 'Tenant ID' }),
(0, swagger_1.ApiResponse)({ status: 201, description: 'User registered successfully' }),
(0, swagger_1.ApiResponse)({ status: 400, description: 'Bad request' }),
(0, swagger_1.ApiResponse)({ status: 409, description: 'Email already exists' }),
__param(0, (0, common_1.Body)()),
__param(1, (0, tenant_decorator_1.CurrentTenant)()),
__param(2, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [dto_1.RegisterDto, String, Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "register", null);
__decorate([
(0, common_1.Post)('login'),
(0, public_decorator_1.Public)(),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({ summary: 'Login user' }),
(0, swagger_1.ApiHeader)({ name: 'x-tenant-id', required: true, description: 'Tenant ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Login successful' }),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Invalid credentials' }),
__param(0, (0, common_1.Body)()),
__param(1, (0, tenant_decorator_1.CurrentTenant)()),
__param(2, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [dto_1.LoginDto, String, Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "login", null);
__decorate([
(0, common_1.Post)('logout'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiBearerAuth)(),
(0, swagger_1.ApiOperation)({ summary: 'Logout user' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Logout successful' }),
__param(0, (0, current_user_decorator_1.CurrentUser)()),
__param(1, (0, common_1.Body)('sessionToken')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "logout", null);
__decorate([
(0, common_1.Post)('logout-all'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiBearerAuth)(),
(0, swagger_1.ApiOperation)({ summary: 'Logout all sessions' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'All sessions closed' }),
__param(0, (0, current_user_decorator_1.CurrentUser)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "logoutAll", null);
__decorate([
(0, common_1.Post)('refresh'),
(0, public_decorator_1.Public)(),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({ summary: 'Refresh access token' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Token refreshed' }),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Invalid refresh token' }),
__param(0, (0, common_1.Body)('refreshToken')),
__param(1, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "refresh", null);
__decorate([
(0, common_1.Post)('password/request-reset'),
(0, public_decorator_1.Public)(),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({ summary: 'Request password reset' }),
(0, swagger_1.ApiHeader)({ name: 'x-tenant-id', required: true, description: 'Tenant ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Reset email sent if user exists' }),
__param(0, (0, common_1.Body)()),
__param(1, (0, tenant_decorator_1.CurrentTenant)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [dto_1.RequestPasswordResetDto, String]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "requestPasswordReset", null);
__decorate([
(0, common_1.Post)('password/reset'),
(0, public_decorator_1.Public)(),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({ summary: 'Reset password with token' }),
(0, swagger_1.ApiHeader)({ name: 'x-tenant-id', required: true, description: 'Tenant ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Password reset successful' }),
(0, swagger_1.ApiResponse)({ status: 400, description: 'Invalid or expired token' }),
__param(0, (0, common_1.Body)()),
__param(1, (0, tenant_decorator_1.CurrentTenant)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [dto_1.ResetPasswordDto, String]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "resetPassword", null);
__decorate([
(0, common_1.Post)('password/change'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiBearerAuth)(),
(0, swagger_1.ApiOperation)({ summary: 'Change password' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Password changed' }),
(0, swagger_1.ApiResponse)({ status: 400, description: 'Invalid current password' }),
__param(0, (0, current_user_decorator_1.CurrentUser)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, dto_1.ChangePasswordDto]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "changePassword", null);
__decorate([
(0, common_1.Post)('verify-email'),
(0, public_decorator_1.Public)(),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({ summary: 'Verify email with token' }),
(0, swagger_1.ApiHeader)({ name: 'x-tenant-id', required: true, description: 'Tenant ID' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Email verified' }),
(0, swagger_1.ApiResponse)({ status: 400, description: 'Invalid or expired token' }),
__param(0, (0, common_1.Body)('token')),
__param(1, (0, tenant_decorator_1.CurrentTenant)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "verifyEmail", null);
__decorate([
(0, common_1.Get)('me'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, swagger_1.ApiBearerAuth)(),
(0, swagger_1.ApiOperation)({ summary: 'Get current user profile' }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Current user profile' }),
__param(0, (0, current_user_decorator_1.CurrentUser)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "getProfile", null);
exports.AuthController = AuthController = __decorate([
(0, swagger_1.ApiTags)('auth'),
(0, common_1.Controller)('auth'),
__metadata("design:paramtypes", [auth_service_1.AuthService])
], AuthController);
//# sourceMappingURL=auth.controller.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
export declare class AuthModule {
}

View File

@ -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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthModule = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const jwt_1 = require("@nestjs/jwt");
const passport_1 = require("@nestjs/passport");
const config_1 = require("@nestjs/config");
const entities_1 = require("./entities");
const auth_service_1 = require("./services/auth.service");
const auth_controller_1 = require("./auth.controller");
const jwt_strategy_1 = require("./strategies/jwt.strategy");
let AuthModule = class AuthModule {
};
exports.AuthModule = AuthModule;
exports.AuthModule = AuthModule = __decorate([
(0, common_1.Module)({
imports: [
passport_1.PassportModule.register({ defaultStrategy: 'jwt' }),
jwt_1.JwtModule.registerAsync({
imports: [config_1.ConfigModule],
useFactory: async (configService) => ({
secret: configService.get('jwt.secret'),
signOptions: {
expiresIn: configService.get('jwt.expiresIn') || '15m',
},
}),
inject: [config_1.ConfigService],
}),
typeorm_1.TypeOrmModule.forFeature([entities_1.User, entities_1.Session, entities_1.Token]),
],
controllers: [auth_controller_1.AuthController],
providers: [auth_service_1.AuthService, jwt_strategy_1.JwtStrategy],
exports: [auth_service_1.AuthService, jwt_1.JwtModule, passport_1.PassportModule],
})
], AuthModule);
//# sourceMappingURL=auth.module.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,qCAAwC;AACxC,+CAAkD;AAClD,2CAA6D;AAG7D,yCAAkD;AAGlD,0DAAsD;AAGtD,uDAAmD;AAGnD,4DAAwD;AA0BjD,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAxBtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YAEP,yBAAc,CAAC,QAAQ,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;YAGnD,eAAS,CAAC,aAAa,CAAC;gBACtB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,UAAU,EAAE,KAAK,EAAE,aAA4B,EAAE,EAAE,CAAC,CAAC;oBACnD,MAAM,EAAE,aAAa,CAAC,GAAG,CAAS,YAAY,CAAC;oBAC/C,WAAW,EAAE;wBACX,SAAS,EAAE,aAAa,CAAC,GAAG,CAAS,eAAe,CAAC,IAAI,KAAK;qBAC/D;iBACF,CAAC;gBACF,MAAM,EAAE,CAAC,sBAAa,CAAC;aACxB,CAAC;YAGF,uBAAa,CAAC,UAAU,CAAC,CAAC,eAAI,EAAE,kBAAO,EAAE,gBAAK,CAAC,CAAC;SACjD;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,EAAE,0BAAW,CAAC;QACrC,OAAO,EAAE,CAAC,0BAAW,EAAE,eAAS,EAAE,yBAAc,CAAC;KAClD,CAAC;GACW,UAAU,CAAG"}

View File

@ -0,0 +1,2 @@
import { RequestUser } from '../strategies/jwt.strategy';
export declare const CurrentUser: (...dataOrPipes: (keyof RequestUser | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>> | undefined)[]) => ParameterDecorator;

View File

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CurrentUser = void 0;
const common_1 = require("@nestjs/common");
exports.CurrentUser = (0, common_1.createParamDecorator)((data, ctx) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
if (!user) {
return null;
}
return data ? user[data] : user;
});
//# sourceMappingURL=current-user.decorator.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"current-user.decorator.js","sourceRoot":"","sources":["../../../../src/modules/auth/decorators/current-user.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwE;AAG3D,QAAA,WAAW,GAAG,IAAA,6BAAoB,EAC7C,CAAC,IAAmC,EAAE,GAAqB,EAAE,EAAE;IAC7D,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAmB,CAAC;IAEzC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClC,CAAC,CACF,CAAC"}

View File

@ -0,0 +1,3 @@
export * from './public.decorator';
export * from './current-user.decorator';
export * from './tenant.decorator';

View File

@ -0,0 +1,20 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./public.decorator"), exports);
__exportStar(require("./current-user.decorator"), exports);
__exportStar(require("./tenant.decorator"), exports);
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/auth/decorators/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,2DAAyC;AACzC,qDAAmC"}

View File

@ -0,0 +1,2 @@
export declare const IS_PUBLIC_KEY = "isPublic";
export declare const Public: () => import("@nestjs/common").CustomDecorator<string>;

View File

@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Public = exports.IS_PUBLIC_KEY = void 0;
const common_1 = require("@nestjs/common");
exports.IS_PUBLIC_KEY = 'isPublic';
const Public = () => (0, common_1.SetMetadata)(exports.IS_PUBLIC_KEY, true);
exports.Public = Public;
//# sourceMappingURL=public.decorator.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"public.decorator.js","sourceRoot":"","sources":["../../../../src/modules/auth/decorators/public.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAEhC,QAAA,aAAa,GAAG,UAAU,CAAC;AACjC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,IAAA,oBAAW,EAAC,qBAAa,EAAE,IAAI,CAAC,CAAC;AAAhD,QAAA,MAAM,UAA0C"}

View File

@ -0,0 +1 @@
export declare const CurrentTenant: (...dataOrPipes: unknown[]) => ParameterDecorator;

View File

@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CurrentTenant = void 0;
const common_1 = require("@nestjs/common");
exports.CurrentTenant = (0, common_1.createParamDecorator)((data, ctx) => {
const request = ctx.switchToHttp().getRequest();
if (request.user?.tenant_id) {
return request.user.tenant_id;
}
const tenantId = request.headers['x-tenant-id'];
if (tenantId) {
return tenantId;
}
const host = request.headers.host || '';
const subdomain = host.split('.')[0];
if (subdomain && subdomain !== 'www' && subdomain !== 'api') {
return subdomain;
}
return '';
});
//# sourceMappingURL=tenant.decorator.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"tenant.decorator.js","sourceRoot":"","sources":["../../../../src/modules/auth/decorators/tenant.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwE;AAE3D,QAAA,aAAa,GAAG,IAAA,6BAAoB,EAC/C,CAAC,IAAa,EAAE,GAAqB,EAAU,EAAE;IAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;IAGhD,IAAI,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;IAChC,CAAC;IAGD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAChD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAkB,CAAC;IAC5B,CAAC;IAGD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,SAAS,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;QAC5D,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC,CACF,CAAC"}

View File

@ -0,0 +1,3 @@
export * from './login.dto';
export * from './register.dto';
export * from './reset-password.dto';

View File

@ -0,0 +1,20 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./login.dto"), exports);
__exportStar(require("./register.dto"), exports);
__exportStar(require("./reset-password.dto"), exports);
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,iDAA+B;AAC/B,uDAAqC"}

View File

@ -0,0 +1,4 @@
export declare class LoginDto {
email: string;
password: string;
}

View File

@ -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 {
}
exports.LoginDto = LoginDto;
__decorate([
(0, swagger_1.ApiProperty)({ example: 'user@example.com' }),
(0, class_validator_1.IsEmail)({}, { message: 'Email inválido' }),
(0, class_validator_1.IsNotEmpty)({ message: 'Email es requerido' }),
__metadata("design:type", String)
], LoginDto.prototype, "email", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ example: 'password123' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)({ message: 'Password es requerido' }),
(0, class_validator_1.MinLength)(8, { message: 'Password debe tener al menos 8 caracteres' }),
__metadata("design:type", String)
], LoginDto.prototype, "password", void 0);
//# sourceMappingURL=login.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"login.dto.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/login.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAA2E;AAC3E,6CAA8C;AAE9C,MAAa,QAAQ;CAWpB;AAXD,4BAWC;AAPC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,EAAC,EAAE,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;;uCAChC;AAMd;IAJC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IACvC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IAChD,IAAA,2BAAS,EAAC,CAAC,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;;0CACtD"}

View File

@ -0,0 +1,7 @@
export declare class RegisterDto {
email: string;
password: string;
first_name?: string;
last_name?: string;
phone?: string;
}

View File

@ -0,0 +1,50 @@
"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.RegisterDto = void 0;
const class_validator_1 = require("class-validator");
const swagger_1 = require("@nestjs/swagger");
class RegisterDto {
}
exports.RegisterDto = RegisterDto;
__decorate([
(0, swagger_1.ApiProperty)({ example: 'user@example.com' }),
(0, class_validator_1.IsEmail)({}, { message: 'Email inválido' }),
(0, class_validator_1.IsNotEmpty)({ message: 'Email es requerido' }),
__metadata("design:type", String)
], RegisterDto.prototype, "email", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ example: 'SecurePass123!' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)({ message: 'Password es requerido' }),
(0, class_validator_1.MinLength)(8, { message: 'Password debe tener al menos 8 caracteres' }),
(0, class_validator_1.Matches)(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, { message: 'Password debe contener mayúsculas, minúsculas y números' }),
__metadata("design:type", String)
], RegisterDto.prototype, "password", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ example: 'John' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], RegisterDto.prototype, "first_name", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ example: 'Doe' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], RegisterDto.prototype, "last_name", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ example: '+1234567890' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], RegisterDto.prototype, "phone", void 0);
//# sourceMappingURL=register.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"register.dto.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/register.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAOyB;AACzB,6CAAmE;AAEnE,MAAa,WAAW;CA8BvB;AA9BD,kCA8BC;AA1BC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,EAAC,EAAE,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;;0CAChC;AAUd;IARC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IAChD,IAAA,2BAAS,EAAC,CAAC,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;IACtE,IAAA,yBAAO,EACN,iCAAiC,EACjC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CACvE;;6CACgB;AAKjB;IAHC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACxC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;+CACS;AAKpB;IAHC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACvC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;8CACQ;AAKnB;IAHC,IAAA,6BAAmB,EAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAC/C,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;0CACI"}

View File

@ -0,0 +1,11 @@
export declare class RequestPasswordResetDto {
email: string;
}
export declare class ResetPasswordDto {
token: string;
password: string;
}
export declare class ChangePasswordDto {
currentPassword: string;
newPassword: string;
}

View File

@ -0,0 +1,58 @@
"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.ChangePasswordDto = exports.ResetPasswordDto = exports.RequestPasswordResetDto = void 0;
const class_validator_1 = require("class-validator");
const swagger_1 = require("@nestjs/swagger");
class RequestPasswordResetDto {
}
exports.RequestPasswordResetDto = RequestPasswordResetDto;
__decorate([
(0, swagger_1.ApiProperty)({ example: 'user@example.com' }),
(0, class_validator_1.IsEmail)({}, { message: 'Email inválido' }),
(0, class_validator_1.IsNotEmpty)({ message: 'Email es requerido' }),
__metadata("design:type", String)
], RequestPasswordResetDto.prototype, "email", void 0);
class ResetPasswordDto {
}
exports.ResetPasswordDto = ResetPasswordDto;
__decorate([
(0, swagger_1.ApiProperty)({ example: 'abc123token' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)({ message: 'Token es requerido' }),
__metadata("design:type", String)
], ResetPasswordDto.prototype, "token", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ example: 'NewSecurePass123!' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)({ message: 'Password es requerido' }),
(0, class_validator_1.MinLength)(8, { message: 'Password debe tener al menos 8 caracteres' }),
(0, class_validator_1.Matches)(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, { message: 'Password debe contener mayúsculas, minúsculas y números' }),
__metadata("design:type", String)
], ResetPasswordDto.prototype, "password", void 0);
class ChangePasswordDto {
}
exports.ChangePasswordDto = ChangePasswordDto;
__decorate([
(0, swagger_1.ApiProperty)({ example: 'OldPassword123!' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)({ message: 'Password actual es requerido' }),
__metadata("design:type", String)
], ChangePasswordDto.prototype, "currentPassword", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ example: 'NewPassword456!' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)({ message: 'Nuevo password es requerido' }),
(0, class_validator_1.MinLength)(8, { message: 'Password debe tener al menos 8 caracteres' }),
(0, class_validator_1.Matches)(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, { message: 'Password debe contener mayúsculas, minúsculas y números' }),
__metadata("design:type", String)
], ChangePasswordDto.prototype, "newPassword", void 0);
//# sourceMappingURL=reset-password.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"reset-password.dto.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/reset-password.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAoF;AACpF,6CAA8C;AAE9C,MAAa,uBAAuB;CAKnC;AALD,0DAKC;AADC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC5C,IAAA,yBAAO,EAAC,EAAE,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC1C,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;;sDAChC;AAGhB,MAAa,gBAAgB;CAe5B;AAfD,4CAeC;AAXC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IACvC,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;;+CAChC;AAUd;IARC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC7C,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IAChD,IAAA,2BAAS,EAAC,CAAC,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;IACtE,IAAA,yBAAO,EACN,iCAAiC,EACjC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CACvE;;kDACgB;AAGnB,MAAa,iBAAiB;CAe7B;AAfD,8CAeC;AAXC;IAHC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC3C,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC;;0DAChC;AAUxB;IARC,IAAA,qBAAW,EAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC3C,IAAA,0BAAQ,GAAE;IACV,IAAA,4BAAU,EAAC,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC;IACtD,IAAA,2BAAS,EAAC,CAAC,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;IACtE,IAAA,yBAAO,EACN,iCAAiC,EACjC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CACvE;;sDACmB"}

View File

@ -0,0 +1,3 @@
export * from './user.entity';
export * from './session.entity';
export * from './token.entity';

View File

@ -0,0 +1,20 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./user.entity"), exports);
__exportStar(require("./session.entity"), exports);
__exportStar(require("./token.entity"), exports);
//# sourceMappingURL=index.js.map

Some files were not shown because too many files have changed in this diff Show More