mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-17 05:47:52 +00:00
feat: adiciona redis e healthcheck
- Implementa cliente Redis com retry e cache - Adiciona healthcheck da API - Configura tipagem para Next.js API routes - Implementa cache de histórias - Adiciona tratamento de erros robusto - Configura monitoramento de conexões - Otimiza performance com cache distribuído
This commit is contained in:
parent
521a99a5c2
commit
cc23c83c05
@ -28,6 +28,11 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
|
|||||||
- Dockerfile com multi-stage build para otimização
|
- Dockerfile com multi-stage build para otimização
|
||||||
- Configuração de registry no Gitea
|
- Configuração de registry no Gitea
|
||||||
- Cache de histórias com Redis
|
- Cache de histórias com Redis
|
||||||
|
- Implementação de cliente Redis com:
|
||||||
|
- Retry strategy otimizada
|
||||||
|
- Reconexão automática
|
||||||
|
- Tipagem forte
|
||||||
|
- Funções utilitárias para cache
|
||||||
- Scripts de deploy e monitoramento
|
- Scripts de deploy e monitoramento
|
||||||
|
|
||||||
### Modificado
|
### Modificado
|
||||||
|
|||||||
1068
package-lock.json
generated
1068
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,8 +19,12 @@
|
|||||||
"@radix-ui/react-tabs": "^1.1.2",
|
"@radix-ui/react-tabs": "^1.1.2",
|
||||||
"@supabase/supabase-js": "^2.39.7",
|
"@supabase/supabase-js": "^2.39.7",
|
||||||
"@tanstack/react-query": "^5.62.8",
|
"@tanstack/react-query": "^5.62.8",
|
||||||
|
"@types/ioredis": "^4.28.10",
|
||||||
|
"@types/next": "^8.0.7",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"ioredis": "^5.4.2",
|
||||||
"lucide-react": "^0.344.0",
|
"lucide-react": "^0.344.0",
|
||||||
|
"next": "^15.1.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
|
|||||||
@ -1,5 +1,62 @@
|
|||||||
import { Redis } from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
|
|
||||||
const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
|
if (!process.env.REDIS_URL) {
|
||||||
|
throw new Error('REDIS_URL não configurada');
|
||||||
|
}
|
||||||
|
|
||||||
|
const redis = new Redis(process.env.REDIS_URL, {
|
||||||
|
maxRetriesPerRequest: 3,
|
||||||
|
retryStrategy(times) {
|
||||||
|
const delay = Math.min(times * 50, 2000);
|
||||||
|
return delay;
|
||||||
|
},
|
||||||
|
reconnectOnError(err) {
|
||||||
|
const targetError = 'READONLY';
|
||||||
|
if (err.message.includes(targetError)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
redis.on('error', (error) => {
|
||||||
|
console.error('[Redis] Erro de conexão:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
redis.on('connect', () => {
|
||||||
|
console.log('[Redis] Conectado com sucesso');
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function getFromCache<T>(key: string): Promise<T | null> {
|
||||||
|
try {
|
||||||
|
const cached = await redis.get(key);
|
||||||
|
if (cached) {
|
||||||
|
return JSON.parse(cached) as T;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Redis] Erro ao buscar do cache:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setInCache(key: string, value: any, expireInSeconds = 3600): Promise<void> {
|
||||||
|
try {
|
||||||
|
await redis.set(key, JSON.stringify(value), 'EX', expireInSeconds);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Redis] Erro ao salvar no cache:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function invalidateCache(pattern: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const keys = await redis.keys(pattern);
|
||||||
|
if (keys.length > 0) {
|
||||||
|
await redis.del(...keys);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Redis] Erro ao invalidar cache:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default redis;
|
export default redis;
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import redis from '@/lib/redis';
|
import redis from '@/lib/redis';
|
||||||
|
import { supabase } from '@/lib/supabase';
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(_: any, res: { status: (arg0: number) => { (): any; new(): any; json: { (arg0: { status: string; error?: any; }): void; new(): any; }; }; }) {
|
||||||
try {
|
try {
|
||||||
// Verifica conexão com Redis
|
// Verifica conexão com Redis
|
||||||
await redis.ping();
|
await redis.ping();
|
||||||
@ -11,7 +12,11 @@ export default async function handler(req, res) {
|
|||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
res.status(200).json({ status: 'healthy' });
|
res.status(200).json({ status: 'healthy' });
|
||||||
} catch (error) {
|
} catch (error: unknown) {
|
||||||
res.status(500).json({ status: 'unhealthy', error: error.message });
|
if (error instanceof Error) {
|
||||||
|
res.status(500).json({ status: 'unhealthy', error: error.message });
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ status: 'unhealthy', error: 'Erro desconhecido' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import redis from '@/lib/redis';
|
import redis from '@/lib/redis';
|
||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
const { id } = req.query;
|
const { id } = req.query;
|
||||||
|
|
||||||
// Tenta pegar do cache primeiro
|
// Tenta pegar do cache primeiro
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user