@sapphire-caitlyn/schema-kit-nest 0.0.0-stage → 0.0.2
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 +145 -2
- package/config/naming-strategy.d.ts +6 -0
- package/config/naming-strategy.js +18 -0
- package/config/redis.constants.d.ts +1 -0
- package/config/redis.constants.js +4 -0
- package/context-logger.service.d.ts +4 -0
- package/context-logger.service.js +23 -0
- package/decorators/date-range.d.ts +4 -0
- package/decorators/date-range.js +13 -0
- package/decorators/file-column.d.ts +17 -0
- package/decorators/file-column.js +20 -0
- package/decorators/luxon.d.ts +6 -0
- package/decorators/luxon.js +20 -0
- package/decorators/multipart.decorator.d.ts +4 -0
- package/decorators/multipart.decorator.js +51 -0
- package/decorators/post-get.decorator.d.ts +10 -0
- package/decorators/post-get.decorator.js +25 -0
- package/decorators/query-or-body.decorator.d.ts +8 -0
- package/decorators/query-or-body.decorator.js +15 -0
- package/decorators/when.decorator.d.ts +8 -0
- package/decorators/when.decorator.js +16 -0
- package/html/components/badge.d.ts +13 -0
- package/html/components/badge.js +29 -0
- package/html/components/colors.d.ts +11 -0
- package/html/components/colors.js +15 -0
- package/html/components/component.d.ts +11 -0
- package/html/components/component.js +35 -0
- package/html/components/default/default-footer.d.ts +6 -0
- package/html/components/default/default-footer.js +18 -0
- package/html/components/default/default-header.d.ts +13 -0
- package/html/components/default/default-header.js +30 -0
- package/html/components/env-badge.d.ts +7 -0
- package/html/components/env-badge.js +29 -0
- package/html/components/grid.d.ts +17 -0
- package/html/components/grid.js +44 -0
- package/html/components/shell.d.ts +18 -0
- package/html/components/shell.js +441 -0
- package/html/components/table.d.ts +16 -0
- package/html/components/table.js +55 -0
- package/html/components/unordered-list.d.ts +6 -0
- package/html/components/unordered-list.js +21 -0
- package/index.d.ts +29 -0
- package/index.js +45 -0
- package/interfaces/paginated-result.d.ts +21 -0
- package/interfaces/paginated-result.js +14 -0
- package/modules/pubsub/pubsub-dynamic-topic.service.d.ts +47 -0
- package/modules/pubsub/pubsub-dynamic-topic.service.js +243 -0
- package/modules/pubsub/pubsub.constants.d.ts +2 -0
- package/modules/pubsub/pubsub.constants.js +5 -0
- package/modules/pubsub/pubsub.methods.d.ts +10 -0
- package/modules/pubsub/pubsub.methods.js +10 -0
- package/modules/redis/redis.module.d.ts +7 -0
- package/modules/redis/redis.module.js +54 -0
- package/modules/storage/storage.service.d.ts +16 -0
- package/modules/storage/storage.service.js +61 -0
- package/monitoramento/http-request-log/http-request-log.controller.d.ts +184 -0
- package/monitoramento/http-request-log/http-request-log.controller.js +32 -0
- package/monitoramento/http-request-log/http-request-log.dto.d.ts +220 -0
- package/monitoramento/http-request-log/http-request-log.dto.js +42 -0
- package/monitoramento/http-request-log/http-request-log.entity.d.ts +17 -0
- package/monitoramento/http-request-log/http-request-log.entity.js +87 -0
- package/monitoramento/http-request-log/http-request-log.module.d.ts +2 -0
- package/monitoramento/http-request-log/http-request-log.module.js +30 -0
- package/monitoramento/http-request-log/http-request-log.service.d.ts +8 -0
- package/monitoramento/http-request-log/http-request-log.service.js +35 -0
- package/monitoramento/index.d.ts +12 -0
- package/monitoramento/index.js +28 -0
- package/monitoramento/migrations/1792689106394-create_http_request_logs_table.d.ts +5 -0
- package/monitoramento/migrations/1792689106394-create_http_request_logs_table.js +96 -0
- package/monitoramento/migrations/1792689106395-create_pubsub_message_logs_table.d.ts +5 -0
- package/monitoramento/migrations/1792689106395-create_pubsub_message_logs_table.js +151 -0
- package/monitoramento/monitoramento.constants.d.ts +2 -0
- package/monitoramento/monitoramento.constants.js +5 -0
- package/monitoramento/monitoramento.entities.d.ts +1 -0
- package/monitoramento/monitoramento.entities.js +10 -0
- package/monitoramento/monitoramento.module.d.ts +6 -0
- package/monitoramento/monitoramento.module.js +58 -0
- package/monitoramento/monitoramento.options.d.ts +15 -0
- package/monitoramento/monitoramento.options.js +2 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.controller.d.ts +249 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.controller.js +34 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.dto.d.ts +343 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.dto.js +55 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.entity.d.ts +34 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.entity.js +135 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.module.d.ts +2 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.module.js +30 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.service.d.ts +8 -0
- package/monitoramento/pubsub-message-log/pubsub-message-log.service.js +35 -0
- package/package.json +41 -4
- package/schema/abstract.entity.d.ts +28 -0
- package/schema/abstract.entity.js +68 -0
- package/schema/schema-builder.d.ts +131 -0
- package/schema/schema-builder.js +158 -0
- package/schema/schema.controller.d.ts +51 -0
- package/schema/schema.controller.js +119 -0
- package/schema/schema.service.d.ts +79 -0
- package/schema/schema.service.js +286 -0
- package/schema/typeorm-extensions.d.ts +6 -0
- package/schema/typeorm-extensions.js +6 -0
- package/schema/zed-extensions/cnpj.validator.d.ts +28 -0
- package/schema/zed-extensions/cnpj.validator.js +131 -0
- package/schema/zed-extensions/cpf-cnpj.validator.d.ts +2 -0
- package/schema/zed-extensions/cpf-cnpj.validator.js +8 -0
- package/schema/zed-extensions/cpf.validator.d.ts +18 -0
- package/schema/zed-extensions/cpf.validator.js +165 -0
- package/schema/zod-extensions.d.ts +43 -0
- package/schema/zod-extensions.js +136 -0
- package/schema-kit.module.d.ts +2 -0
- package/schema-kit.module.js +27 -0
- package/transformers/date-range.transformer.d.ts +6 -0
- package/transformers/date-range.transformer.js +44 -0
- package/transformers/luxon.transformer.d.ts +3 -0
- package/transformers/luxon.transformer.js +12 -0
- package/utils/utils.d.ts +49 -0
- package/utils/utils.js +79 -0
- package/validators/ip-validator.d.ts +3 -0
- package/validators/ip-validator.js +8 -0
- package/validators/url-validator.d.ts +3 -0
- package/validators/url-validator.js +8 -0
- package/validators/uuid-validator.d.ts +3 -0
- package/validators/uuid-validator.js +8 -0
package/README.md
CHANGED
|
@@ -1,3 +1,146 @@
|
|
|
1
|
-
#
|
|
1
|
+
# schema-kit-nest (`@sapphire-caitlyn/schema-kit-nest`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Base NestJS do schema-kit: o par `SchemaService` + `SchemaController` monta o CRUD que o `<schema-table>` e o
|
|
4
|
+
`<schema-view>` do `@sapphire-caitlyn/schema-kit-ng` consomem. Traz também decorators, validators, entidades base,
|
|
5
|
+
Pub/Sub, Storage, Redis e o módulo de monitoramento.
|
|
6
|
+
|
|
7
|
+
## Instalação
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @sapphire-caitlyn/schema-kit-nest
|
|
11
|
+
npm i @nestjs/common @nestjs/config @nestjs/platform-express @nestjs/typeorm typeorm zod luxon class-transformer \
|
|
12
|
+
handlebars ioredis @google-cloud/pubsub @google-cloud/storage reflect-metadata rxjs
|
|
13
|
+
npm i -D @types/express @types/luxon
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
O pacote é CommonJS e não tem `exports`: além do barrel, imports profundos funcionam
|
|
17
|
+
(`@sapphire-caitlyn/schema-kit-nest/schema/schema-builder`).
|
|
18
|
+
|
|
19
|
+
## Uso
|
|
20
|
+
|
|
21
|
+
### Módulo
|
|
22
|
+
|
|
23
|
+
Importe o `SchemaKitModule` uma vez (é `@Global()`). Ele provê o `ContextLogger`, o `PubSub` e o `StorageService`,
|
|
24
|
+
que o `SchemaService` injeta por propriedade. Precisa do `ConfigModule` global.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
@Module({
|
|
28
|
+
imports: [ConfigModule.forRoot({ isGlobal: true }), SchemaKitModule, ProdutoModule],
|
|
29
|
+
})
|
|
30
|
+
export class AppModule {}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Entidade e DTOs
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
@Entity()
|
|
37
|
+
export class Produto extends AbstractEntity { // uuid v7 + createdAt/updatedAt/deletedAt
|
|
38
|
+
@Column() nome: string;
|
|
39
|
+
@Column({ type: 'uuid', nullable: true }) uuidCategoria: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const modelSchemaProduto = z.object({
|
|
43
|
+
uuid: z.uuidv7(),
|
|
44
|
+
nome: z.string().min(1, 'Nome é obrigatório'),
|
|
45
|
+
uuidCategoria: z.uuidv7().nullable().optional(),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Campos aceitos na listagem (filtro e ordenação)
|
|
49
|
+
export const indexSchemaProduto: IIndexSchema = {
|
|
50
|
+
uuid: { type: FieldType.UUID },
|
|
51
|
+
nome: { type: FieldType.String },
|
|
52
|
+
uuidCategoria: { type: FieldType.UUID },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const { createValidator, patchValidator, indexValidator } = SchemaBuilder.build({
|
|
56
|
+
modelSchema: modelSchemaProduto,
|
|
57
|
+
indexSchema: indexSchemaProduto,
|
|
58
|
+
});
|
|
59
|
+
export type CreateProdutoDto = z.infer<typeof createValidator>;
|
|
60
|
+
export type PatchProdutoDto = z.infer<typeof patchValidator>;
|
|
61
|
+
export type IndexProdutoDto = z.infer<typeof indexValidator>;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Sem soft delete, use `TimestampedEntity`, ou componha com `UuidEntity`, `Timestamped(...)` e `SoftDeletable(...)`.
|
|
65
|
+
|
|
66
|
+
### Service e controller
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
@Injectable()
|
|
70
|
+
export class ProdutoService extends SchemaService<Produto, CreateProdutoDto, PatchProdutoDto, IndexProdutoDto> {
|
|
71
|
+
constructor(@InjectRepository(Produto) repository: Repository<Produto>) {
|
|
72
|
+
super(repository, indexSchemaProduto);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Controller('produto')
|
|
77
|
+
export class ProdutoController extends SchemaController<Produto, CreateProdutoDto, PatchProdutoDto, IndexProdutoDto>({
|
|
78
|
+
createSchema: createValidator,
|
|
79
|
+
patchSchema: patchValidator,
|
|
80
|
+
indexParamsSchema: indexValidator,
|
|
81
|
+
// routes: ['index', 'find'], // opcional: só essas rotas base
|
|
82
|
+
}) {
|
|
83
|
+
constructor(service: ProdutoService) {
|
|
84
|
+
super(service);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Rotas base (o contrato que a schema-kit-ng usa):
|
|
90
|
+
|
|
91
|
+
| Rota | Uso |
|
|
92
|
+
| ---------------------------- | -------------------------------------------------------------------- |
|
|
93
|
+
| `GET/POST /produto/index` | Listagem paginada `{ data, total, page, size }` (params em query ou body) |
|
|
94
|
+
| `GET /produto/:uuid` | Registro para edição |
|
|
95
|
+
| `POST /produto` | Cria (aceita `multipart/form-data` com arquivos) |
|
|
96
|
+
| `PATCH /produto/:uuid` | Salva só os campos enviados |
|
|
97
|
+
| `DELETE /produto/:uuid` | Exclui |
|
|
98
|
+
|
|
99
|
+
Pontos de extensão:
|
|
100
|
+
|
|
101
|
+
- **Controller:** `beforeRoute(route, args)` roda antes de cada rota base (lance exceção para bloquear, ex.: permissão).
|
|
102
|
+
`afterRoute(route, result)` pode transformar o resultado.
|
|
103
|
+
- **Service:** `beforeIndex(qb, params)` e `applyFilters(qb, params)`, para filtros que não são igualdade simples.
|
|
104
|
+
|
|
105
|
+
### Módulos opcionais
|
|
106
|
+
|
|
107
|
+
- **`RedisModule`** (`@Global()`): provê o cliente `ioredis` em `REDIS_CLIENT`. Lê `DB_REDIS_HOST`, `DB_REDIS_PORT`,
|
|
108
|
+
`DB_REDIS_USERNAME` e `DB_REDIS_PASSWORD` do `ConfigService`.
|
|
109
|
+
- **`MonitoramentoModule.forRoot(...)` / `forRootAsync(...)`:** abre uma conexão TypeORM nomeada
|
|
110
|
+
(`MONITORAMENTO_CONNECTION`), separada do DataSource default, para os logs de requisição HTTP e de mensagens Pub/Sub.
|
|
111
|
+
As migrations não rodam sozinhas. Aponte o DataSource de CLI para os arquivos do pacote:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
migrations: ['node_modules/@sapphire-caitlyn/schema-kit-nest/monitoramento/migrations/*.js'],
|
|
115
|
+
migrationsTableName: MONITORAMENTO_MIGRATIONS_TABLE,
|
|
116
|
+
entities: MONITORAMENTO_ENTITIES,
|
|
117
|
+
namingStrategy: new SnakeCaseNamingStrategy(),
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Atenção
|
|
121
|
+
|
|
122
|
+
- **Instale todos os peers**, mesmo sem usar Redis, Pub/Sub ou Storage: o barrel importa esses módulos, então o
|
|
123
|
+
`import` do pacote falha sem `ioredis` e `@google-cloud/*`. O `handlebars` só é carregado pelos componentes de
|
|
124
|
+
`html/components` (import profundo).
|
|
125
|
+
- **O `SchemaKitModule` exige `GOOGLE_CLOUD_STORAGE_BUCKET`** (`getOrThrow` na subida). As credenciais do Google vêm de
|
|
126
|
+
`GOOGLE_APPLICATION_CREDENTIALS`.
|
|
127
|
+
- **Importar o pacote altera o `Repository` do TypeORM** (`repository.use(manager)`, para usar o repositório dentro de
|
|
128
|
+
uma transação). Por isso o `typeorm` é peer: com duas cópias, o patch cai na cópia errada.
|
|
129
|
+
- **O default do `uuid` é `uuidv7()` do Postgres**, que só existe a partir do PostgreSQL 18.
|
|
130
|
+
|
|
131
|
+
## Build e publicação
|
|
132
|
+
|
|
133
|
+
No `api-backoffice` a lib não é usada pelo pacote: a API importa o código-fonte pelo alias do `tsconfig.json`
|
|
134
|
+
(`@sapphire-caitlyn/schema-kit-nest` → `libs/nest/src`). Na raiz do `api-backoffice`:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm run build:lib # tsc → dist/libs/nest + package.json e README
|
|
138
|
+
npm run publish:lib -- --dry-run # empacota sem publicar
|
|
139
|
+
npm run publish:lib # build:lib + npm publish ./dist/libs/nest
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- Antes de publicar, suba a `version` em `libs/nest/package.json`: o registry recusa uma versão já publicada.
|
|
143
|
+
- **Dentro da lib, use só imports relativos.** O `tsc` não reescreve `paths`: um import pelo alias compila aqui, mas
|
|
144
|
+
publica um `require` que não resolve em quem instala.
|
|
145
|
+
- Pacote externo novo: em `peerDependencies` quando a app precisa de uma cópia única (Nest, TypeORM, zod, luxon...),
|
|
146
|
+
em `dependencies` para utilitários sem estado (`juice`).
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DefaultNamingStrategy } from 'typeorm';
|
|
2
|
+
export declare class SnakeCaseNamingStrategy extends DefaultNamingStrategy {
|
|
3
|
+
tableName(className: string, customName: string): string;
|
|
4
|
+
columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string;
|
|
5
|
+
joinColumnName(relationName: string, referencedColumnName: string): string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SnakeCaseNamingStrategy = void 0;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
const StringUtils_1 = require("typeorm/util/StringUtils");
|
|
6
|
+
class SnakeCaseNamingStrategy extends typeorm_1.DefaultNamingStrategy {
|
|
7
|
+
tableName(className, customName) {
|
|
8
|
+
return customName || (0, StringUtils_1.snakeCase)(className);
|
|
9
|
+
}
|
|
10
|
+
columnName(propertyName, customName, embeddedPrefixes) {
|
|
11
|
+
return ((0, StringUtils_1.snakeCase)(embeddedPrefixes.concat('').join('_')) +
|
|
12
|
+
(customName || (0, StringUtils_1.snakeCase)(propertyName)));
|
|
13
|
+
}
|
|
14
|
+
joinColumnName(relationName, referencedColumnName) {
|
|
15
|
+
return (0, StringUtils_1.snakeCase)(`${referencedColumnName}_${relationName}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.SnakeCaseNamingStrategy = SnakeCaseNamingStrategy;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const REDIS_CLIENT = "REDIS_CLIENT";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ContextLogger = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
let ContextLogger = class ContextLogger extends common_1.ConsoleLogger {
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
exports.ContextLogger = ContextLogger;
|
|
20
|
+
exports.ContextLogger = ContextLogger = __decorate([
|
|
21
|
+
(0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }),
|
|
22
|
+
__metadata("design:paramtypes", [])
|
|
23
|
+
], ContextLogger);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ColumnOptions } from 'typeorm';
|
|
2
|
+
export type { Span } from '../transformers/date-range.transformer';
|
|
3
|
+
export declare const DateRangeColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
4
|
+
export declare const DateTimeRangeColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DateTimeRangeColumn = exports.DateRangeColumn = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const class_transformer_1 = require("class-transformer");
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const date_range_transformer_1 = require("../transformers/date-range.transformer");
|
|
8
|
+
const toIso = (0, class_transformer_1.Transform)(({ value }) => value?.map((p) => p?.toISO() ?? p) ?? value, { toPlainOnly: true });
|
|
9
|
+
const toIsoDate = (0, class_transformer_1.Transform)(({ value }) => value?.map((p) => p?.toISODate() ?? p) ?? value, { toPlainOnly: true });
|
|
10
|
+
const DateRangeColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.Column)({ type: 'daterange', ...o, transformer: date_range_transformer_1.dateRangeTransformer }), toIsoDate);
|
|
11
|
+
exports.DateRangeColumn = DateRangeColumn;
|
|
12
|
+
const DateTimeRangeColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.Column)({ type: 'tstzrange', ...o, transformer: date_range_transformer_1.dateTimeRangeTransformer }), toIso);
|
|
13
|
+
exports.DateTimeRangeColumn = DateTimeRangeColumn;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ColumnOptions } from 'typeorm';
|
|
2
|
+
/**
|
|
3
|
+
* Coluna com o caminho de um arquivo no Cloud Storage (`text`, ou `text[]` com `multiple`).
|
|
4
|
+
* O `SchemaService` envia os `File` recebidos para o bucket e devolve URLs assinadas no lugar dos caminhos.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FileColumn: ({ multiple, ...o }?: ColumnOptions & {
|
|
7
|
+
multiple?: boolean;
|
|
8
|
+
}) => PropertyDecorator;
|
|
9
|
+
/** Propriedades da entidade decoradas com `@FileColumn()` */
|
|
10
|
+
export declare function getFileColumns(target: unknown): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Entidade como o DTO a recebe: colunas de texto aceitam também `File`, que o `SchemaService` troca pelo caminho no bucket.
|
|
13
|
+
* O tipo não distingue as colunas `@FileColumn()`, então vale para qualquer coluna de texto.
|
|
14
|
+
*/
|
|
15
|
+
export type WithFileInput<T> = {
|
|
16
|
+
[K in keyof T]: T[K] extends string | string[] | null ? T[K] | File | File[] : T[K];
|
|
17
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileColumn = void 0;
|
|
4
|
+
exports.getFileColumns = getFileColumns;
|
|
5
|
+
const typeorm_1 = require("typeorm");
|
|
6
|
+
const FILE_COLUMNS = Symbol('FILE_COLUMNS');
|
|
7
|
+
/**
|
|
8
|
+
* Coluna com o caminho de um arquivo no Cloud Storage (`text`, ou `text[]` com `multiple`).
|
|
9
|
+
* O `SchemaService` envia os `File` recebidos para o bucket e devolve URLs assinadas no lugar dos caminhos.
|
|
10
|
+
*/
|
|
11
|
+
const FileColumn = ({ multiple = false, ...o } = {}) => (target, key) => {
|
|
12
|
+
(0, typeorm_1.Column)({ type: 'text', nullable: true, array: multiple, ...o })(target, key);
|
|
13
|
+
const columns = Reflect.getMetadata(FILE_COLUMNS, target.constructor) ?? [];
|
|
14
|
+
Reflect.defineMetadata(FILE_COLUMNS, [...columns, String(key)], target.constructor);
|
|
15
|
+
};
|
|
16
|
+
exports.FileColumn = FileColumn;
|
|
17
|
+
/** Propriedades da entidade decoradas com `@FileColumn()` */
|
|
18
|
+
function getFileColumns(target) {
|
|
19
|
+
return typeof target === 'function' ? (Reflect.getMetadata(FILE_COLUMNS, target) ?? []) : [];
|
|
20
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ColumnOptions } from 'typeorm';
|
|
2
|
+
export declare const DateTimeColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
3
|
+
export declare const CreateDateTimeColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
4
|
+
export declare const UpdateDateTimeColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
5
|
+
export declare const DeleteDateTimeColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
6
|
+
export declare const DateOnlyColumn: (o?: ColumnOptions) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DateOnlyColumn = exports.DeleteDateTimeColumn = exports.UpdateDateTimeColumn = exports.CreateDateTimeColumn = exports.DateTimeColumn = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const class_transformer_1 = require("class-transformer");
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const luxon_transformer_1 = require("../transformers/luxon.transformer");
|
|
8
|
+
const toIso = (0, class_transformer_1.Transform)(({ value }) => value?.toISO() ?? value, { toPlainOnly: true });
|
|
9
|
+
const toIsoDate = (0, class_transformer_1.Transform)(({ value }) => value?.toISODate() ?? value, { toPlainOnly: true });
|
|
10
|
+
const dt = (o) => ({ type: 'timestamptz', ...o, transformer: luxon_transformer_1.dateTimeTransformer });
|
|
11
|
+
const DateTimeColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.Column)(dt(o)), toIso);
|
|
12
|
+
exports.DateTimeColumn = DateTimeColumn;
|
|
13
|
+
const CreateDateTimeColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.CreateDateColumn)(dt(o)), toIso);
|
|
14
|
+
exports.CreateDateTimeColumn = CreateDateTimeColumn;
|
|
15
|
+
const UpdateDateTimeColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.UpdateDateColumn)(dt(o)), toIso);
|
|
16
|
+
exports.UpdateDateTimeColumn = UpdateDateTimeColumn;
|
|
17
|
+
const DeleteDateTimeColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.DeleteDateColumn)(dt(o)), toIso);
|
|
18
|
+
exports.DeleteDateTimeColumn = DeleteDateTimeColumn;
|
|
19
|
+
const DateOnlyColumn = (o = {}) => (0, common_1.applyDecorators)((0, typeorm_1.Column)({ type: 'date', ...o, transformer: luxon_transformer_1.dateOnlyTransformer }), toIsoDate);
|
|
20
|
+
exports.DateOnlyColumn = DateOnlyColumn;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aceita `multipart/form-data` na rota, além de JSON. Os arquivos ficam em memória até o handler.
|
|
3
|
+
*/
|
|
4
|
+
export declare function Multipart(): <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.Multipart = Multipart;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const platform_express_1 = require("@nestjs/platform-express");
|
|
12
|
+
const zod_extensions_1 = require("../schema/zod-extensions");
|
|
13
|
+
const MAX_FILE_SIZE = 20 * 1024 * 1024;
|
|
14
|
+
/**
|
|
15
|
+
* Reconstrói o body de uma requisição multipart no mesmo formato de um body JSON:
|
|
16
|
+
* cada campo de texto é o JSON do valor e cada arquivo vira um `File` (o que `z.file()` valida).
|
|
17
|
+
* Arquivos enviados como `campo[]` formam um array em `campo`.
|
|
18
|
+
*/
|
|
19
|
+
let MultipartBodyInterceptor = class MultipartBodyInterceptor {
|
|
20
|
+
intercept(context, next) {
|
|
21
|
+
const req = context.switchToHttp().getRequest();
|
|
22
|
+
if (!Array.isArray(req.files)) {
|
|
23
|
+
return next.handle();
|
|
24
|
+
}
|
|
25
|
+
const body = {};
|
|
26
|
+
for (const [key, value] of Object.entries(req.body ?? {})) {
|
|
27
|
+
body[key] = (0, zod_extensions_1.fromJson)(value);
|
|
28
|
+
}
|
|
29
|
+
for (const { fieldname, originalname, mimetype, buffer } of req.files) {
|
|
30
|
+
const file = new File([new Uint8Array(buffer)], originalname, { type: mimetype });
|
|
31
|
+
if (fieldname.endsWith('[]')) {
|
|
32
|
+
const key = fieldname.slice(0, -2);
|
|
33
|
+
body[key] = [...(Array.isArray(body[key]) ? body[key] : []), file];
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
body[fieldname] = file;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
req.body = body;
|
|
40
|
+
return next.handle();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
MultipartBodyInterceptor = __decorate([
|
|
44
|
+
(0, common_1.Injectable)()
|
|
45
|
+
], MultipartBodyInterceptor);
|
|
46
|
+
/**
|
|
47
|
+
* Aceita `multipart/form-data` na rota, além de JSON. Os arquivos ficam em memória até o handler.
|
|
48
|
+
*/
|
|
49
|
+
function Multipart() {
|
|
50
|
+
return (0, common_1.applyDecorators)((0, common_1.UseInterceptors)((0, platform_express_1.AnyFilesInterceptor)({ limits: { fileSize: MAX_FILE_SIZE } }), MultipartBodyInterceptor));
|
|
51
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers a route that answers to both GET and POST.
|
|
3
|
+
* Any other HTTP method on the same path is rejected with 405 Method Not Allowed.
|
|
4
|
+
*
|
|
5
|
+
* Stacking `@Get()` and `@Post()` doesn't work, since each overwrites the other's route metadata,
|
|
6
|
+
* so the route is registered with `@All()` and the method is filtered by a guard.
|
|
7
|
+
*
|
|
8
|
+
* @param endpoint - Route path(s), same as `@Get()` / `@Post()`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function PostGet(endpoint?: string | string[]): <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostGet = PostGet;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const ALLOWED_METHODS = ['GET', 'POST'];
|
|
6
|
+
class PostGetGuard {
|
|
7
|
+
canActivate(context) {
|
|
8
|
+
const { method } = context.switchToHttp().getRequest();
|
|
9
|
+
if (!ALLOWED_METHODS.includes(method?.toUpperCase()))
|
|
10
|
+
throw new common_1.MethodNotAllowedException();
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Registers a route that answers to both GET and POST.
|
|
16
|
+
* Any other HTTP method on the same path is rejected with 405 Method Not Allowed.
|
|
17
|
+
*
|
|
18
|
+
* Stacking `@Get()` and `@Post()` doesn't work, since each overwrites the other's route metadata,
|
|
19
|
+
* so the route is registered with `@All()` and the method is filtered by a guard.
|
|
20
|
+
*
|
|
21
|
+
* @param endpoint - Route path(s), same as `@Get()` / `@Post()`.
|
|
22
|
+
*/
|
|
23
|
+
function PostGet(endpoint) {
|
|
24
|
+
return (0, common_1.applyDecorators)((0, common_1.All)(endpoint), (0, common_1.UseGuards)(PostGetGuard));
|
|
25
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the request body on POST and the query string otherwise.
|
|
3
|
+
* Meant for `@PostGet()` routes, which answer to both methods.
|
|
4
|
+
*
|
|
5
|
+
* Accepts `{ schema }` like `@Query()` / `@Body()`, but the global `StandardSchemaValidationPipe`
|
|
6
|
+
* must be created with `validateCustomDecorators: true` for it to be validated.
|
|
7
|
+
*/
|
|
8
|
+
export declare const QueryOrBody: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueryOrBody = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
/**
|
|
6
|
+
* Reads the request body on POST and the query string otherwise.
|
|
7
|
+
* Meant for `@PostGet()` routes, which answer to both methods.
|
|
8
|
+
*
|
|
9
|
+
* Accepts `{ schema }` like `@Query()` / `@Body()`, but the global `StandardSchemaValidationPipe`
|
|
10
|
+
* must be created with `validateCustomDecorators: true` for it to be validated.
|
|
11
|
+
*/
|
|
12
|
+
exports.QueryOrBody = (0, common_1.createParamDecorator)((_, ctx) => {
|
|
13
|
+
const req = ctx.switchToHttp().getRequest();
|
|
14
|
+
return req.method.toUpperCase() === 'POST' ? (req.body ?? {}) : req.query;
|
|
15
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A decorator that conditionally applies other decorators based on the provided boolean value.
|
|
3
|
+
*
|
|
4
|
+
* @param enabled - A boolean value that determines whether the provided decorators should be applied.
|
|
5
|
+
* @param decorators - A list of method decorators to be conditionally applied.
|
|
6
|
+
* @returns A method decorator that applies the provided decorators if `enabled` is true.
|
|
7
|
+
*/
|
|
8
|
+
export declare function When(enabled: boolean, ...decorators: MethodDecorator[]): MethodDecorator;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.When = When;
|
|
4
|
+
/**
|
|
5
|
+
* A decorator that conditionally applies other decorators based on the provided boolean value.
|
|
6
|
+
*
|
|
7
|
+
* @param enabled - A boolean value that determines whether the provided decorators should be applied.
|
|
8
|
+
* @param decorators - A list of method decorators to be conditionally applied.
|
|
9
|
+
* @returns A method decorator that applies the provided decorators if `enabled` is true.
|
|
10
|
+
*/
|
|
11
|
+
function When(enabled, ...decorators) {
|
|
12
|
+
return (target, key, descriptor) => {
|
|
13
|
+
if (enabled)
|
|
14
|
+
decorators.forEach((d) => d(target, key, descriptor));
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Color } from './colors';
|
|
2
|
+
import { HtmlComponent } from './component';
|
|
3
|
+
export interface IBadgeHtmlContent {
|
|
4
|
+
text: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
color: Color;
|
|
7
|
+
}
|
|
8
|
+
declare class BadgeHtmlComponent extends HtmlComponent<IBadgeHtmlContent> {
|
|
9
|
+
constructor();
|
|
10
|
+
compile(content: IBadgeHtmlContent): string;
|
|
11
|
+
}
|
|
12
|
+
export declare const badgeHtmlComponent: BadgeHtmlComponent;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.badgeHtmlComponent = void 0;
|
|
4
|
+
const colors_1 = require("./colors");
|
|
5
|
+
const component_1 = require("./component");
|
|
6
|
+
// Variantes no estilo do badge do shadcn (modo claro): cores com escala usam a variante "soft" (fundo 50, borda 200, texto 700)
|
|
7
|
+
const variantClasses = {
|
|
8
|
+
[colors_1.Color.PRIMARY]: 'bg-primary text-primary-foreground border-primary',
|
|
9
|
+
[colors_1.Color.SECONDARY]: 'bg-secondary text-secondary-foreground border-secondary',
|
|
10
|
+
[colors_1.Color.SUCCESS]: 'bg-success-50 text-success-700 border-success-200',
|
|
11
|
+
[colors_1.Color.DANGER]: 'bg-danger-50 text-danger-700 border-danger-200',
|
|
12
|
+
[colors_1.Color.WARNING]: 'bg-warning-50 text-warning-700 border-warning-200',
|
|
13
|
+
[colors_1.Color.INFO]: 'bg-info-50 text-info-700 border-info-200',
|
|
14
|
+
[colors_1.Color.LIGHT]: 'bg-muted text-muted-foreground border-border',
|
|
15
|
+
[colors_1.Color.DARK]: 'bg-foreground text-background border-foreground',
|
|
16
|
+
[colors_1.Color.NEUTRAL]: 'bg-neutral-100 text-neutral-700 border-neutral-200',
|
|
17
|
+
};
|
|
18
|
+
class BadgeHtmlComponent extends component_1.HtmlComponent {
|
|
19
|
+
constructor() {
|
|
20
|
+
const content = `
|
|
21
|
+
<span class="badge {{variant}} {{class}}">{{text}}</span>
|
|
22
|
+
`;
|
|
23
|
+
super('', content);
|
|
24
|
+
}
|
|
25
|
+
compile(content) {
|
|
26
|
+
return super.compile({ ...content, variant: variantClasses[content.color] });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.badgeHtmlComponent = new BadgeHtmlComponent();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Color = void 0;
|
|
4
|
+
var Color;
|
|
5
|
+
(function (Color) {
|
|
6
|
+
Color["PRIMARY"] = "primary";
|
|
7
|
+
Color["SECONDARY"] = "secondary";
|
|
8
|
+
Color["SUCCESS"] = "success";
|
|
9
|
+
Color["DANGER"] = "danger";
|
|
10
|
+
Color["WARNING"] = "warning";
|
|
11
|
+
Color["INFO"] = "info";
|
|
12
|
+
Color["LIGHT"] = "light";
|
|
13
|
+
Color["DARK"] = "dark";
|
|
14
|
+
Color["NEUTRAL"] = "neutral";
|
|
15
|
+
})(Color || (exports.Color = Color = {}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class HtmlComponent<T = void> {
|
|
2
|
+
readonly css?: string | undefined;
|
|
3
|
+
readonly content?: string | undefined;
|
|
4
|
+
private options?;
|
|
5
|
+
constructor(css?: string | undefined, content?: string | undefined, options?: CompileOptions | undefined);
|
|
6
|
+
private template;
|
|
7
|
+
compile(content: T): string;
|
|
8
|
+
compile(content?: T): string;
|
|
9
|
+
compile(content?: unknown): string;
|
|
10
|
+
private setTemplate;
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HtmlComponent = void 0;
|
|
7
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
8
|
+
const juice_1 = __importDefault(require("juice"));
|
|
9
|
+
class HtmlComponent {
|
|
10
|
+
css;
|
|
11
|
+
content;
|
|
12
|
+
options;
|
|
13
|
+
constructor(css, content, options) {
|
|
14
|
+
this.css = css;
|
|
15
|
+
this.content = content;
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.setTemplate();
|
|
18
|
+
}
|
|
19
|
+
template;
|
|
20
|
+
compile(content) {
|
|
21
|
+
const html = this.template(content);
|
|
22
|
+
return (0, juice_1.default)(html);
|
|
23
|
+
}
|
|
24
|
+
setTemplate() {
|
|
25
|
+
if (!this.template) {
|
|
26
|
+
this.template = handlebars_1.default.compile(`
|
|
27
|
+
<style>
|
|
28
|
+
${this.css}
|
|
29
|
+
</style>
|
|
30
|
+
${this.content}
|
|
31
|
+
`, this.options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.HtmlComponent = HtmlComponent;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultFooterHtmlComponent = void 0;
|
|
4
|
+
const component_1 = require("../component");
|
|
5
|
+
class DefaultFooterHtmlComponent extends component_1.HtmlComponent {
|
|
6
|
+
constructor() {
|
|
7
|
+
const content = `
|
|
8
|
+
<div class="border-t px-8 py-4 text-center text-xs text-muted-foreground">
|
|
9
|
+
<p>Av. Vinte e Cinco de Julho, 3000,
|
|
10
|
+
Flores da Cunha - RS CEP: 95270-000
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
`;
|
|
14
|
+
super('', content);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.defaultFooterHtmlComponent = new DefaultFooterHtmlComponent();
|
|
18
|
+
exports.defaultFooterHtmlComponent.compile();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HtmlComponent } from '../component';
|
|
2
|
+
export interface IDefaultHeaderHtmlContent {
|
|
3
|
+
logoUrl: string;
|
|
4
|
+
headerWatermarkCss: string;
|
|
5
|
+
headerWatermarkUrl: string;
|
|
6
|
+
logoHeightPx: number;
|
|
7
|
+
}
|
|
8
|
+
declare class DefaultHeaderHtmlComponent extends HtmlComponent<IDefaultHeaderHtmlContent> {
|
|
9
|
+
constructor();
|
|
10
|
+
compile(data: IDefaultHeaderHtmlContent): string;
|
|
11
|
+
}
|
|
12
|
+
export declare const defaultHeaderHtmlComponent: DefaultHeaderHtmlComponent;
|
|
13
|
+
export {};
|