@miguelmorales13/nestkit 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +305 -1
- package/dist/auth/auth-user.entity.d.ts +8 -0
- package/dist/auth/auth-user.port.d.ts +16 -0
- package/dist/auth/auth.controller.d.ts +25 -0
- package/dist/auth/auth.dto.d.ts +13 -0
- package/dist/auth/auth.service.d.ts +24 -0
- package/dist/auth/authenticated-user.d.ts +7 -0
- package/dist/auth/current-tenant.decorator.d.ts +2 -0
- package/dist/auth/current-user.decorator.d.ts +1 -0
- package/dist/auth/hash.util.d.ts +2 -0
- package/dist/auth/index.cjs +265 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.js +265 -0
- package/dist/auth/jwt-auth.guard.d.ts +5 -0
- package/dist/auth/require-tenant.guard.d.ts +9 -0
- package/dist/auth/roles.decorator.d.ts +3 -0
- package/dist/auth/roles.guard.d.ts +11 -0
- package/dist/auth/token.service.d.ts +7 -0
- package/dist/bootstrap/index.cjs +4 -3
- package/dist/bootstrap/index.js +3 -2
- package/dist/{chunk-4IDLJMQA.cjs → chunk-7FQQDWOZ.cjs} +6 -14
- package/dist/chunk-7SOM7EZP.cjs +1 -0
- package/dist/chunk-DQYAIQQ5.js +0 -0
- package/dist/{chunk-AR3EKJBR.cjs → chunk-FDNGAYTZ.cjs} +6 -6
- package/dist/{chunk-AUA4X3RE.cjs → chunk-O2ZMI3EK.cjs} +2 -2
- package/dist/{chunk-HGJX2GPE.js → chunk-ORWJ7LES.js} +1 -1
- package/dist/{chunk-EQXYK6AL.js → chunk-Q45IEPWU.js} +5 -13
- package/dist/chunk-R7BVS6CI.cjs +13 -0
- package/dist/{chunk-VPS5CLHT.js → chunk-RHNQGTAT.js} +1 -1
- package/dist/chunk-YFYHLYHN.js +13 -0
- package/dist/email/nodemailer/index.cjs +40 -0
- package/dist/email/nodemailer/index.d.ts +1 -0
- package/dist/email/nodemailer/index.js +40 -0
- package/dist/email/nodemailer/nodemailer.module.d.ts +4 -0
- package/dist/email/resend/index.cjs +31 -0
- package/dist/email/resend/index.d.ts +1 -0
- package/dist/email/resend/index.js +31 -0
- package/dist/email/resend/resend.module.d.ts +4 -0
- package/dist/errors/index.cjs +7 -4
- package/dist/errors/index.js +6 -3
- package/dist/index.cjs +17 -14
- package/dist/index.js +24 -21
- package/dist/stripe/index.cjs +78 -0
- package/dist/stripe/index.d.ts +3 -0
- package/dist/stripe/index.js +78 -0
- package/dist/stripe/stripe-webhook.controller.d.ts +20 -0
- package/dist/stripe/stripe.module.d.ts +4 -0
- package/dist/telegram/index.cjs +31 -0
- package/dist/telegram/index.d.ts +1 -0
- package/dist/telegram/index.js +31 -0
- package/dist/telegram/telegram.module.d.ts +8 -0
- package/dist/whatsapp/index.cjs +68 -0
- package/dist/whatsapp/index.d.ts +3 -0
- package/dist/whatsapp/index.js +68 -0
- package/dist/whatsapp/whatsapp-client.d.ts +21 -0
- package/dist/whatsapp/whatsapp.module.d.ts +4 -0
- package/package.json +74 -10
package/README.md
CHANGED
|
@@ -387,6 +387,304 @@ El idioma se resuelve por el `Accept-Language` del request (comportamiento por d
|
|
|
387
387
|
regresa el `fallback`, así una traducción faltante no se convierte en un error 500 encima del error
|
|
388
388
|
original.
|
|
389
389
|
|
|
390
|
+
### `telegram` — cliente de salida para un bot específico
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
import { Module } from '@nestjs/common';
|
|
394
|
+
import { TelegramModule } from '@miguelmorales13/nestkit/telegram';
|
|
395
|
+
|
|
396
|
+
@Module({ imports: [TelegramModule] })
|
|
397
|
+
export class AppModule {}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
402
|
+
import { TELEGRAM_BOT } from '@miguelmorales13/nestkit/telegram';
|
|
403
|
+
import type { Telegram as TelegramClient } from 'telegraf';
|
|
404
|
+
|
|
405
|
+
@Injectable()
|
|
406
|
+
export class NotificationsService {
|
|
407
|
+
constructor(@Inject(TELEGRAM_BOT) private readonly bot: TelegramClient) {}
|
|
408
|
+
|
|
409
|
+
async notify(chatId: number | string, text: string) {
|
|
410
|
+
await this.bot.sendMessage(chatId, text);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Requiere `TELEGRAM_BOT_TOKEN` en el entorno — falla explícito al arrancar si falta, igual que
|
|
416
|
+
`PgModule`/`SupabaseModule`. `TelegramModule` solo expone el cliente de **salida** (`Telegram`, la
|
|
417
|
+
clase de `telegraf` para llamar directo a la Bot API): no hay polling, no hay webhook, no hay manejo
|
|
418
|
+
de updates entrantes — es a propósito, este paquete no enruta mensajes recibidos. Importa el tipo del
|
|
419
|
+
cliente como `Telegram as TelegramClient` si tipas tu propia inyección — `telegraf` exporta una clase
|
|
420
|
+
llamada `Telegram`, que choca de nombre con el concepto genérico "Telegram" en tu propio código.
|
|
421
|
+
|
|
422
|
+
### `whatsapp` — cliente de salida sobre la Cloud API de Meta
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
import { Module } from '@nestjs/common';
|
|
426
|
+
import { WhatsAppModule } from '@miguelmorales13/nestkit/whatsapp';
|
|
427
|
+
|
|
428
|
+
@Module({ imports: [WhatsAppModule] })
|
|
429
|
+
export class AppModule {}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
434
|
+
import { WHATSAPP_CLIENT, WhatsAppClient } from '@miguelmorales13/nestkit/whatsapp';
|
|
435
|
+
|
|
436
|
+
@Injectable()
|
|
437
|
+
export class NotificationsService {
|
|
438
|
+
constructor(@Inject(WHATSAPP_CLIENT) private readonly whatsapp: WhatsAppClient) {}
|
|
439
|
+
|
|
440
|
+
async notify(to: string, body: string) {
|
|
441
|
+
await this.whatsapp.sendTextMessage(to, body);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Requiere `WHATSAPP_ACCESS_TOKEN` y `WHATSAPP_PHONE_NUMBER_ID`; `WHATSAPP_API_VERSION` es opcional
|
|
447
|
+
(default `v21.0`). No hay dependencia npm de por medio — Meta no publica un SDK oficial de Node
|
|
448
|
+
razonable para la Cloud API, que de todos modos es un REST plano, así que `WhatsAppClient` usa el
|
|
449
|
+
`fetch` global de Node (18+) directo. Solo manda mensajes de texto (`sendTextMessage`) — sin
|
|
450
|
+
templates, sin media, sin manejo de mensajes entrantes.
|
|
451
|
+
|
|
452
|
+
### `stripe` — cliente de la SDK oficial
|
|
453
|
+
|
|
454
|
+
```ts
|
|
455
|
+
import { Module } from '@nestjs/common';
|
|
456
|
+
import { StripeModule } from '@miguelmorales13/nestkit/stripe';
|
|
457
|
+
|
|
458
|
+
@Module({ imports: [StripeModule] })
|
|
459
|
+
export class AppModule {}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
464
|
+
import { STRIPE_CLIENT } from '@miguelmorales13/nestkit/stripe';
|
|
465
|
+
import type Stripe from 'stripe';
|
|
466
|
+
|
|
467
|
+
@Injectable()
|
|
468
|
+
export class BillingService {
|
|
469
|
+
constructor(@Inject(STRIPE_CLIENT) private readonly stripe: Stripe) {}
|
|
470
|
+
|
|
471
|
+
async createCustomer(email: string) {
|
|
472
|
+
return this.stripe.customers.create({ email });
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Requiere `STRIPE_SECRET_KEY`. `STRIPE_API_VERSION` es opcional — si no la fijas, la SDK usa su propia
|
|
478
|
+
versión pineada por default (no la fuerces a mano salvo que sepas exactamente por qué). Como
|
|
479
|
+
Postgres/Supabase, solo expone el cliente crudo por DI — no envuelve `Stripe` en tipos propios de
|
|
480
|
+
`nestkit`, así que tipa tu inyección directo contra `Stripe` de la librería `stripe`.
|
|
481
|
+
|
|
482
|
+
#### Webhooks de Stripe
|
|
483
|
+
|
|
484
|
+
> **Requisito obligatorio**: tu `main.ts` tiene que crear la app con
|
|
485
|
+
> `NestFactory.create(AppModule, { rawBody: true })`. La verificación de firma de Stripe necesita los
|
|
486
|
+
> bytes crudos del request tal cual llegaron — `applyNestKitDefaults` corre **después** de crear la
|
|
487
|
+
> app y no puede habilitar `rawBody` en retrospectiva. Sin esto, el controller lanza un error
|
|
488
|
+
> explícito en cuanto le llega un webhook, en vez de fallar silencioso.
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
// main.ts
|
|
492
|
+
const app = await NestFactory.create(AppModule, { rawBody: true });
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
import { Module } from '@nestjs/common';
|
|
497
|
+
import { createStripeWebhookController } from '@miguelmorales13/nestkit/stripe';
|
|
498
|
+
|
|
499
|
+
class StripeWebhookController extends createStripeWebhookController({
|
|
500
|
+
handlers: {
|
|
501
|
+
'checkout.session.completed': async (event) => {
|
|
502
|
+
// event.data.object es el Checkout Session
|
|
503
|
+
},
|
|
504
|
+
'customer.subscription.updated': async (event) => {
|
|
505
|
+
// actualiza el plan del tenant en tu propia DB
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
}) {}
|
|
509
|
+
|
|
510
|
+
@Module({ controllers: [StripeWebhookController] })
|
|
511
|
+
export class BillingModule {}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Requiere `STRIPE_WEBHOOK_SECRET` además de `STRIPE_SECRET_KEY`. Los tipos de evento sin handler
|
|
515
|
+
registrado se acusan recibo (`{ received: true }`) y se ignoran — Stripe espera un 200 rápido,
|
|
516
|
+
no que manejes cada tipo de evento que te manda.
|
|
517
|
+
|
|
518
|
+
### `auth` — flujo completo de registro/login, para empresa o para persona sola
|
|
519
|
+
|
|
520
|
+
Mismo espíritu que `crud`: `nestkit` define el puerto (`AuthUserPort`) y la orquestación
|
|
521
|
+
(`BaseAuthService`, `createAuthController`), vos traés tu propia entidad/persistencia. A diferencia
|
|
522
|
+
de los demás módulos, **no hay `Module` que importar** — son clases y guards planos que se usan
|
|
523
|
+
directo, sin registro en el contenedor de DI.
|
|
524
|
+
|
|
525
|
+
```ts
|
|
526
|
+
// user.repository.ts
|
|
527
|
+
import { Injectable } from '@nestjs/common';
|
|
528
|
+
import type { AuthUser, AuthUserPort } from '@miguelmorales13/nestkit/auth';
|
|
529
|
+
|
|
530
|
+
interface User extends AuthUser {}
|
|
531
|
+
|
|
532
|
+
@Injectable()
|
|
533
|
+
export class UserRepository implements AuthUserPort<User> {
|
|
534
|
+
async findByEmail(email: string): Promise<User | null> { /* ... */ return null; }
|
|
535
|
+
async findById(id: string): Promise<User | null> { /* ... */ return null; }
|
|
536
|
+
async create(data: { email: string; passwordHash: string; tenantId?: string; roles: string[] }): Promise<User> {
|
|
537
|
+
/* insertar y devolver el user creado */
|
|
538
|
+
throw new Error('not implemented');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
// auth.module.ts
|
|
545
|
+
import { Module } from '@nestjs/common';
|
|
546
|
+
import { BaseAuthService, createAuthController } from '@miguelmorales13/nestkit/auth';
|
|
547
|
+
import { UserRepository } from './user.repository';
|
|
548
|
+
|
|
549
|
+
const AUTH_SERVICE = 'AUTH_SERVICE';
|
|
550
|
+
|
|
551
|
+
class AuthController extends createAuthController(AUTH_SERVICE as any) {}
|
|
552
|
+
|
|
553
|
+
@Module({
|
|
554
|
+
controllers: [AuthController],
|
|
555
|
+
providers: [
|
|
556
|
+
UserRepository,
|
|
557
|
+
{ provide: AUTH_SERVICE, useFactory: (users: UserRepository) => new BaseAuthService(users), inject: [UserRepository] },
|
|
558
|
+
],
|
|
559
|
+
})
|
|
560
|
+
export class AuthModule {}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
> En la práctica, igual que con `crud`, usa una subclase concreta de `BaseAuthService` como
|
|
564
|
+
> provider/token para que TypeScript infiera los tipos sin castear.
|
|
565
|
+
|
|
566
|
+
Esto ya te da `POST /auth/register`, `POST /auth/login`, `POST /auth/refresh` funcionando. Para que
|
|
567
|
+
sirva **tanto para una empresa como para una persona que se registra sola**, `tenantId` es opcional
|
|
568
|
+
en todo el flujo: mandalo en el body de `register` si el usuario pertenece a una empresa, omitilo
|
|
569
|
+
para una cuenta individual — queda como `undefined` en el JWT y en la entidad, nada más se rompe.
|
|
570
|
+
|
|
571
|
+
**Proteger rutas** — el orden de los guards importa, `JwtAuthGuard` tiene que ir primero (es el que
|
|
572
|
+
puebla `request.user`):
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
import { Controller, Get, UseGuards } from '@nestjs/common';
|
|
576
|
+
import {
|
|
577
|
+
JwtAuthGuard,
|
|
578
|
+
RolesGuard,
|
|
579
|
+
Roles,
|
|
580
|
+
RequireTenantGuard,
|
|
581
|
+
CurrentUser,
|
|
582
|
+
CurrentTenant,
|
|
583
|
+
type AuthenticatedUser,
|
|
584
|
+
} from '@miguelmorales13/nestkit/auth';
|
|
585
|
+
|
|
586
|
+
@Controller('widgets')
|
|
587
|
+
@UseGuards(JwtAuthGuard)
|
|
588
|
+
export class WidgetsController {
|
|
589
|
+
@Get('me')
|
|
590
|
+
me(@CurrentUser() user: AuthenticatedUser) {
|
|
591
|
+
return user;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
@Get('admin-only')
|
|
595
|
+
@UseGuards(RolesGuard)
|
|
596
|
+
@Roles('admin')
|
|
597
|
+
adminOnly() { /* ... */ }
|
|
598
|
+
|
|
599
|
+
@Get('company-only')
|
|
600
|
+
@UseGuards(RequireTenantGuard)
|
|
601
|
+
companyOnly(@CurrentTenant() tenantId: string) { /* tenantId nunca es undefined acá */ }
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
`RequireTenantGuard` es el opt-in para endpoints que **sí** deben ser exclusivos de cuentas de
|
|
606
|
+
empresa — sin él, cualquier ruta protegida por `JwtAuthGuard` funciona igual de bien para una cuenta
|
|
607
|
+
individual (`tenantId: undefined`) que para una de empresa.
|
|
608
|
+
|
|
609
|
+
**Integración con `crud`** — `tenantIdExtractor` de `createCrudController` lee directo del JWT que
|
|
610
|
+
`JwtAuthGuard` ya decodificó:
|
|
611
|
+
|
|
612
|
+
```ts
|
|
613
|
+
class WidgetsController extends createCrudController(WIDGETS_SERVICE as any, {
|
|
614
|
+
path: 'widgets',
|
|
615
|
+
tenantIdExtractor: (req) => (req as { user?: AuthenticatedUser }).user?.tenantId,
|
|
616
|
+
}) {}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
Requiere `JWT_ACCESS_SECRET` y `JWT_REFRESH_SECRET`; `JWT_ACCESS_EXPIRES_IN` (default `15m`) y
|
|
620
|
+
`JWT_REFRESH_EXPIRES_IN` (default `30d`) son opcionales. Los passwords se hashean con `bcryptjs`
|
|
621
|
+
(oculto — no es peer dependency, no lo importás vos); los tokens usan `jsonwebtoken` por debajo,
|
|
622
|
+
también oculto. `login()` devuelve el mismo mensaje de error para "no existe el usuario" y
|
|
623
|
+
"contraseña incorrecta" — evita que alguien pueda usar el endpoint para enumerar emails registrados.
|
|
624
|
+
|
|
625
|
+
`register()` siempre asigna `roles: ['user']` — no hay forma de pasar otro rol inicial desde el
|
|
626
|
+
body (a propósito: dejarlo abierto desde el request sería que cualquiera se autoasigne `admin`). Si
|
|
627
|
+
necesitás otra lógica de roles al alta (ej. el primer usuario de un tenant nuevo es `owner`), extendé
|
|
628
|
+
`BaseAuthService` y sobreescribí `register()`, o llamá `users.create()` directo con los roles que
|
|
629
|
+
quieras desde tu propio controller.
|
|
630
|
+
|
|
631
|
+
### `email/resend` y `email/nodemailer` — dos transportes, elegís uno por proyecto
|
|
632
|
+
|
|
633
|
+
Mismo patrón que `database/postgres` vs `database/supabase`: dos módulos independientes, no una
|
|
634
|
+
abstracción unificada — importás el que le convenga al proyecto.
|
|
635
|
+
|
|
636
|
+
```ts
|
|
637
|
+
import { Module } from '@nestjs/common';
|
|
638
|
+
import { ResendModule } from '@miguelmorales13/nestkit/email/resend';
|
|
639
|
+
|
|
640
|
+
@Module({ imports: [ResendModule] })
|
|
641
|
+
export class AppModule {}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
```ts
|
|
645
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
646
|
+
import { RESEND_CLIENT } from '@miguelmorales13/nestkit/email/resend';
|
|
647
|
+
import type { Resend } from 'resend';
|
|
648
|
+
|
|
649
|
+
@Injectable()
|
|
650
|
+
export class NotificationsService {
|
|
651
|
+
constructor(@Inject(RESEND_CLIENT) private readonly resend: Resend) {}
|
|
652
|
+
|
|
653
|
+
async notify(to: string, subject: string, html: string) {
|
|
654
|
+
await this.resend.emails.send({ from: 'noreply@tudominio.com', to, subject, html });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
Requiere `RESEND_API_KEY`. Elegí Resend para proyectos nuevos: una sola API key, SDK oficial liviano.
|
|
660
|
+
|
|
661
|
+
```ts
|
|
662
|
+
import { Module } from '@nestjs/common';
|
|
663
|
+
import { NodemailerModule } from '@miguelmorales13/nestkit/email/nodemailer';
|
|
664
|
+
|
|
665
|
+
@Module({ imports: [NodemailerModule] })
|
|
666
|
+
export class AppModule {}
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
```ts
|
|
670
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
671
|
+
import { NODEMAILER_TRANSPORT } from '@miguelmorales13/nestkit/email/nodemailer';
|
|
672
|
+
import type { Transporter } from 'nodemailer';
|
|
673
|
+
|
|
674
|
+
@Injectable()
|
|
675
|
+
export class NotificationsService {
|
|
676
|
+
constructor(@Inject(NODEMAILER_TRANSPORT) private readonly mailer: Transporter) {}
|
|
677
|
+
|
|
678
|
+
async notify(to: string, subject: string, html: string) {
|
|
679
|
+
await this.mailer.sendMail({ from: 'noreply@tudominio.com', to, subject, html });
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
Requiere `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`; `SMTP_SECURE` es opcional (`'true'`
|
|
685
|
+
para forzar TLS directo). Elegí Nodemailer cuando ya tenés un proveedor SMTP propio (Gmail, SES,
|
|
686
|
+
Mailgun, tu propio servidor) en vez de sumar otra cuenta de terceros.
|
|
687
|
+
|
|
390
688
|
## Subpaths disponibles
|
|
391
689
|
|
|
392
690
|
| Subpath | Qué trae |
|
|
@@ -400,12 +698,18 @@ original.
|
|
|
400
698
|
| `@miguelmorales13/nestkit/database/postgres` | `PgModule`, `PG_POOL`, `withTenantScope` |
|
|
401
699
|
| `@miguelmorales13/nestkit/database/supabase` | `SupabaseModule`, `SUPABASE_ANON_CLIENT`, `SUPABASE_SERVICE_ROLE_CLIENT` |
|
|
402
700
|
| `@miguelmorales13/nestkit/database/mongo` | Solo el contrato `MongoRepositoryPort` — sin implementación |
|
|
701
|
+
| `@miguelmorales13/nestkit/telegram` | `TelegramModule`, `TELEGRAM_BOT` |
|
|
702
|
+
| `@miguelmorales13/nestkit/whatsapp` | `WhatsAppModule`, `WHATSAPP_CLIENT`, `WhatsAppClient` |
|
|
703
|
+
| `@miguelmorales13/nestkit/stripe` | `StripeModule`, `STRIPE_CLIENT`, `createStripeWebhookController` |
|
|
704
|
+
| `@miguelmorales13/nestkit/auth` | `AuthUserPort`, `BaseAuthService`, `createAuthController`, `TokenService`, `JwtAuthGuard`, `RolesGuard`, `RequireTenantGuard`, `CurrentUser`, `CurrentTenant`, `Roles` |
|
|
705
|
+
| `@miguelmorales13/nestkit/email/resend` | `ResendModule`, `RESEND_CLIENT` |
|
|
706
|
+
| `@miguelmorales13/nestkit/email/nodemailer` | `NodemailerModule`, `NODEMAILER_TRANSPORT` |
|
|
403
707
|
| `@miguelmorales13/nestkit/i18n` | `I18nModule` (wrapper de `nestjs-i18n`), `translateOr` |
|
|
404
708
|
| `@miguelmorales13/nestkit/bootstrap` | `applyNestKitDefaults` |
|
|
405
709
|
|
|
406
710
|
## Estado del paquete
|
|
407
711
|
|
|
408
|
-
`0.
|
|
712
|
+
`0.3.0`, no publicado en npm todavía (repo privado, se prueba localmente con `bun link` antes de
|
|
409
713
|
publicar). Sin adaptador Mongo real. Sin tests unitarios propios — es un paquete nuevo sin
|
|
410
714
|
consumidor productivo todavía; la primera integración real (ej. en `hr-pymes-saas`) es la que va a
|
|
411
715
|
ejercitar el código de verdad.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BaseEntity } from '../entities/base.entity.js';
|
|
2
|
+
export interface AuthUser extends BaseEntity {
|
|
3
|
+
email: string;
|
|
4
|
+
passwordHash: string;
|
|
5
|
+
/** Absent for an individual/solo account — present for a company-owned user. */
|
|
6
|
+
tenantId?: string;
|
|
7
|
+
roles: string[];
|
|
8
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AuthUser } from './auth-user.entity.js';
|
|
2
|
+
/**
|
|
3
|
+
* Persistence port for the Auth module — parallel to crud's Repository<T>.
|
|
4
|
+
* The consumer implements this against their own storage; nestkit only
|
|
5
|
+
* orchestrates hashing/token issuance on top of it.
|
|
6
|
+
*/
|
|
7
|
+
export interface AuthUserPort<U extends AuthUser = AuthUser> {
|
|
8
|
+
findByEmail(email: string): Promise<U | null>;
|
|
9
|
+
findById(id: string): Promise<U | null>;
|
|
10
|
+
create(data: {
|
|
11
|
+
email: string;
|
|
12
|
+
passwordHash: string;
|
|
13
|
+
tenantId?: string;
|
|
14
|
+
roles: string[];
|
|
15
|
+
}): Promise<U>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Type } from '@nestjs/common';
|
|
2
|
+
import { LoginDto, RefreshDto, RegisterDto } from './auth.dto.js';
|
|
3
|
+
import type { AuthUser } from './auth-user.entity.js';
|
|
4
|
+
import type { AuthTokens, BaseAuthService } from './auth.service.js';
|
|
5
|
+
export interface CreateAuthControllerOptions {
|
|
6
|
+
/** Route prefix for the generated controller. Defaults to 'auth'. */
|
|
7
|
+
path?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Mixin factory (function returning a class) that wires up register/login/
|
|
11
|
+
* refresh to a BaseAuthService — composition, not forced inheritance, same
|
|
12
|
+
* shape as crud's createCrudController.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* class AuthController extends createAuthController(AUTH_SERVICE as any) {}
|
|
16
|
+
*/
|
|
17
|
+
export declare function createAuthController<U extends AuthUser>(serviceToken: Type<BaseAuthService<U>>, options?: CreateAuthControllerOptions): Type<{
|
|
18
|
+
register(body: RegisterDto): Promise<{
|
|
19
|
+
user: U;
|
|
20
|
+
} & AuthTokens>;
|
|
21
|
+
login(body: LoginDto): Promise<{
|
|
22
|
+
user: U;
|
|
23
|
+
} & AuthTokens>;
|
|
24
|
+
refresh(body: RefreshDto): Promise<AuthTokens>;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class RegisterDto {
|
|
2
|
+
email: string;
|
|
3
|
+
password: string;
|
|
4
|
+
/** Omit for an individual/solo signup — provide for a company-owned user. */
|
|
5
|
+
tenantId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class LoginDto {
|
|
8
|
+
email: string;
|
|
9
|
+
password: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class RefreshDto {
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TokenService } from './token.service.js';
|
|
2
|
+
import type { AuthUser } from './auth-user.entity.js';
|
|
3
|
+
import type { AuthUserPort } from './auth-user.port.js';
|
|
4
|
+
export interface AuthTokens {
|
|
5
|
+
accessToken: string;
|
|
6
|
+
refreshToken: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class BaseAuthService<U extends AuthUser> {
|
|
9
|
+
protected readonly users: AuthUserPort<U>;
|
|
10
|
+
protected readonly tokens: TokenService;
|
|
11
|
+
constructor(users: AuthUserPort<U>, tokens?: TokenService);
|
|
12
|
+
register(data: {
|
|
13
|
+
email: string;
|
|
14
|
+
password: string;
|
|
15
|
+
tenantId?: string;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
user: U;
|
|
18
|
+
} & AuthTokens>;
|
|
19
|
+
login(email: string, password: string): Promise<{
|
|
20
|
+
user: U;
|
|
21
|
+
} & AuthTokens>;
|
|
22
|
+
refresh(refreshToken: string): Promise<AuthTokens>;
|
|
23
|
+
private issueTokens;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CurrentUser: (...dataOrPipes: unknown[]) => ParameterDecorator;
|