feat: implementar melhorias na autenticação
- Adicionar refresh tokens para renovação automática de tokens - Implementar controle de sessões simultâneas - Adicionar blacklist de tokens para logout seguro - Implementar rate limiting para proteção contra ataques - Melhorar detecção de IP e identificação de sessão atual - Adicionar endpoints para gerenciamento de sessões - Corrigir inconsistências na validação de usuário - Atualizar configuração Redis com nova conexão
This commit is contained in:
3
src/core/configs/cache/IRedisClient.ts
vendored
3
src/core/configs/cache/IRedisClient.ts
vendored
@@ -2,5 +2,8 @@ export interface IRedisClient {
|
||||
get<T>(key: string): Promise<T | null>;
|
||||
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
||||
del(key: string): Promise<void>;
|
||||
del(...keys: string[]): Promise<void>;
|
||||
keys(pattern: string): Promise<string[]>;
|
||||
ttl(key: string): Promise<number>;
|
||||
}
|
||||
|
||||
145
src/core/configs/cache/index.html
vendored
145
src/core/configs/cache/index.html
vendored
@@ -1,145 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Documentação - Integração Redis</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f9f9f9;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
padding: 2rem;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
color: #007acc;
|
||||
}
|
||||
code, pre {
|
||||
background-color: #eee;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
th {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
}
|
||||
.tag {
|
||||
display: inline-block;
|
||||
background: #007acc;
|
||||
color: white;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>📦 Integração Redis com Abstração - Portal Juru API</h1>
|
||||
|
||||
<h2>🧱 Arquitetura</h2>
|
||||
<p>O projeto utiliza o Redis com uma interface genérica para garantir desacoplamento, facilidade de teste e reaproveitamento em múltiplos módulos.</p>
|
||||
|
||||
<h3>🔌 Interface IRedisClient</h3>
|
||||
<pre><code>export interface IRedisClient {
|
||||
get<T>(key: string): Promise<T | null>;
|
||||
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
||||
del(key: string): Promise<void>;
|
||||
}</code></pre>
|
||||
|
||||
<h3>🧩 Provider REDIS_CLIENT</h3>
|
||||
<p>Faz a conexão direta com o Redis usando a biblioteca <code>ioredis</code> e o <code>ConfigService</code> para pegar host e porta.</p>
|
||||
<pre><code>export const RedisProvider: Provider = {
|
||||
provide: 'REDIS_CLIENT',
|
||||
useFactory: (configService: ConfigService) => {
|
||||
const redis = new Redis({
|
||||
host: configService.get('REDIS_HOST', '10.1.1.109'),
|
||||
port: configService.get('REDIS_PORT', 6379),
|
||||
});
|
||||
|
||||
redis.on('error', (err) => {
|
||||
console.error('Erro ao conectar ao Redis:', err);
|
||||
});
|
||||
|
||||
return redis;
|
||||
},
|
||||
inject: [ConfigService],
|
||||
};</code></pre>
|
||||
|
||||
<h3>📦 RedisClientAdapter (Wrapper)</h3>
|
||||
<p>Classe que implementa <code>IRedisClient</code> e encapsula as operações de cache. É injetada em serviços via token.</p>
|
||||
<pre><code>@Injectable()
|
||||
export class RedisClientAdapter implements IRedisClient {
|
||||
constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) {}
|
||||
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
const data = await this.redis.get(key);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T, ttlSeconds = 300): Promise<void> {
|
||||
await this.redis.set(key, JSON.stringify(value), 'EX', ttlSeconds);
|
||||
}
|
||||
|
||||
async del(key: string): Promise<void> {
|
||||
await this.redis.del(key);
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h3>🔗 Token e Provider</h3>
|
||||
<p>Token de injeção definido para o adapter:</p>
|
||||
<pre><code>export const RedisClientToken = 'RedisClientInterface';
|
||||
|
||||
export const RedisClientAdapterProvider = {
|
||||
provide: RedisClientToken,
|
||||
useClass: RedisClientAdapter,
|
||||
};</code></pre>
|
||||
|
||||
<h3>📦 Módulo Global RedisModule</h3>
|
||||
<p>Torna o Redis disponível em toda a aplicação.</p>
|
||||
<pre><code>@Global()
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
providers: [RedisProvider, RedisClientAdapterProvider],
|
||||
exports: [RedisProvider, RedisClientAdapterProvider],
|
||||
})
|
||||
export class RedisModule {}</code></pre>
|
||||
|
||||
<h2>🧠 Uso em Serviços</h2>
|
||||
<p>Injetando o cache no seu service:</p>
|
||||
<pre><code>constructor(
|
||||
@Inject(RedisClientToken)
|
||||
private readonly redisClient: IRedisClient
|
||||
) {}</code></pre>
|
||||
|
||||
<p>Uso típico:</p>
|
||||
<pre><code>const data = await this.redisClient.get<T>('chave');
|
||||
if (!data) {
|
||||
const result = await fetchFromDb();
|
||||
await this.redisClient.set('chave', result, 3600);
|
||||
}</code></pre>
|
||||
|
||||
<h2>🧰 Boas práticas</h2>
|
||||
<ul>
|
||||
<li>✅ TTL por recurso (ex: produtos: 1h, lojas: 24h)</li>
|
||||
<li>✅ Nomear chaves com prefixos por domínio (ex: <code>data-consult:sellers</code>)</li>
|
||||
<li>✅ Centralizar helpers como <code>getOrSetCache</code> para evitar repetição</li>
|
||||
<li>✅ Usar <code>JSON.stringify</code> e <code>JSON.parse</code> no adapter</li>
|
||||
<li>✅ Marcar módulo como <code>@Global()</code> para acesso em toda a aplicação</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Última atualização:</strong> 29/03/2025</p>
|
||||
</body>
|
||||
</html>
|
||||
18
src/core/configs/cache/redis-client.adapter.ts
vendored
18
src/core/configs/cache/redis-client.adapter.ts
vendored
@@ -18,7 +18,21 @@ export class RedisClientAdapter implements IRedisClient {
|
||||
await this.redis.set(key, JSON.stringify(value), 'EX', ttlSeconds);
|
||||
}
|
||||
|
||||
async del(key: string): Promise<void> {
|
||||
await this.redis.del(key);
|
||||
async del(key: string): Promise<void>;
|
||||
async del(...keys: string[]): Promise<void>;
|
||||
async del(keyOrKeys: string | string[]): Promise<void> {
|
||||
if (Array.isArray(keyOrKeys)) {
|
||||
await this.redis.del(...keyOrKeys);
|
||||
} else {
|
||||
await this.redis.del(keyOrKeys);
|
||||
}
|
||||
}
|
||||
|
||||
async keys(pattern: string): Promise<string[]> {
|
||||
return this.redis.keys(pattern);
|
||||
}
|
||||
|
||||
async ttl(key: string): Promise<number> {
|
||||
return this.redis.ttl(key);
|
||||
}
|
||||
}
|
||||
|
||||
7
src/core/configs/cache/redis.provider.ts
vendored
7
src/core/configs/cache/redis.provider.ts
vendored
@@ -6,10 +6,9 @@
|
||||
provide: 'REDIS_CLIENT',
|
||||
useFactory: (configService: ConfigService) => {
|
||||
const redis = new Redis({
|
||||
host: configService.get<string>('REDIS_HOST', 'redis-17317.crce181.sa-east-1-2.ec2.redns.redis-cloud.com'),
|
||||
port: configService.get<number>('REDIS_PORT', 17317),
|
||||
username: configService.get<string>('REDIS_USERNAME', 'default' ),
|
||||
password: configService.get<string>('REDIS_PASSWORD', 'd8sVttpJdNxrWjYRK43QGAKzEt3I8HVc'),
|
||||
host: configService.get<string>('REDIS_HOST', '10.1.1.124'),
|
||||
port: configService.get<number>('REDIS_PORT', 6379),
|
||||
password: configService.get<string>('REDIS_PASSWORD', '1234'),
|
||||
});
|
||||
|
||||
redis.on('error', (err) => {
|
||||
|
||||
@@ -4,7 +4,6 @@ import * as oracledb from 'oracledb';
|
||||
|
||||
|
||||
|
||||
// Inicializar o cliente Oracle
|
||||
oracledb.initOracleClient({ libDir: process.env.ORACLE_CLIENT_LIB_DIR });
|
||||
|
||||
// Definir a estratégia de pool padrão para Oracle
|
||||
@@ -14,19 +13,16 @@ oracledb.poolIncrement = 1; // incremental de conexões
|
||||
|
||||
export function createOracleConfig(config: ConfigService): DataSourceOptions {
|
||||
|
||||
// Obter configurações de ambiente ou usar valores padrão
|
||||
const poolMin = parseInt(config.get('ORACLE_POOL_MIN', '5'));
|
||||
const poolMax = parseInt(config.get('ORACLE_POOL_MAX', '20'));
|
||||
const poolIncrement = parseInt(config.get('ORACLE_POOL_INCREMENT', '5'));
|
||||
const poolTimeout = parseInt(config.get('ORACLE_POOL_TIMEOUT', '30000'));
|
||||
const idleTimeout = parseInt(config.get('ORACLE_POOL_IDLE_TIMEOUT', '300000'));
|
||||
|
||||
// Validação de valores mínimos
|
||||
const validPoolMin = Math.max(1, poolMin);
|
||||
const validPoolMax = Math.max(validPoolMin + 1, poolMax);
|
||||
const validPoolIncrement = Math.max(1, poolIncrement);
|
||||
|
||||
// Certifique-se de que poolMax é maior que poolMin
|
||||
if (validPoolMax <= validPoolMin) {
|
||||
console.warn('Warning: poolMax deve ser maior que poolMin. Ajustando poolMax para poolMin + 1');
|
||||
}
|
||||
@@ -40,7 +36,6 @@ export function createOracleConfig(config: ConfigService): DataSourceOptions {
|
||||
logging: config.get('NODE_ENV') === 'development',
|
||||
entities: [__dirname + '/../**/*.entity.{ts,js}'],
|
||||
extra: {
|
||||
// Configurações de pool
|
||||
poolMin: validPoolMin,
|
||||
poolMax: validPoolMax,
|
||||
poolIncrement: validPoolIncrement,
|
||||
|
||||
Reference in New Issue
Block a user