Configuração

Guia de configuração e configuração para Qwen Image Edit

Configuração Inicial

Variáveis de Ambiente

Configure as variáveis de ambiente para diferentes ambientes:

# .env.development
QWEN_API_KEY=sua-chave-api-desenvolvimento
QWEN_REGION=us-east-1
QWEN_DEBUG=true
QWEN_CACHE_ENABLED=true
QWEN_CACHE_TTL=3600
QWEN_MAX_RETRIES=3
QWEN_TIMEOUT=30000

# .env.production
QWEN_API_KEY=sua-chave-api-producao
QWEN_REGION=us-east-1
QWEN_DEBUG=false
QWEN_CACHE_ENABLED=true
QWEN_CACHE_TTL=7200
QWEN_MAX_RETRIES=5
QWEN_TIMEOUT=60000

# .env.test
QWEN_API_KEY=sua-chave-api-teste
QWEN_REGION=us-east-1
QWEN_DEBUG=true
QWEN_CACHE_ENABLED=false
QWEN_MOCK_ENABLED=true

Configuração do Cliente

import { QwenImageEdit } from 'qwen-image-edit';

// Configuração básica
const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  region: process.env.QWEN_REGION || 'us-east-1'
});

// Configuração avançada
const editor = new QwenImageEdit({
  // Autenticação
  apiKey: process.env.QWEN_API_KEY,
  region: 'us-east-1',
  
  // Timeouts
  timeout: 30000,           // Timeout geral (30s)
  uploadTimeout: 60000,     // Timeout de upload (60s)
  downloadTimeout: 30000,   // Timeout de download (30s)
  
  // Retry e Rate Limiting
  maxRetries: 3,            // Máximo de tentativas
  retryDelay: 1000,         // Delay inicial entre tentativas
  retryMultiplier: 2,       // Multiplicador do delay (backoff exponencial)
  maxRetryDelay: 10000,     // Delay máximo entre tentativas
  
  // Cache
  cache: {
    enabled: true,          // Habilitar cache
    ttl: 3600,             // TTL em segundos (1 hora)
    maxSize: 100,          // Máximo de itens no cache
    storage: 'memory'      // 'memory', 'redis', 'file'
  },
  
  // Logging
  debug: process.env.NODE_ENV === 'development',
  logLevel: 'info',         // 'error', 'warn', 'info', 'debug'
  
  // Headers customizados
  headers: {
    'User-Agent': 'MeuApp/1.0.0',
    'X-App-Version': '1.0.0'
  }
});

Configuração Avançada

Cache

Cache em Memória

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  cache: {
    enabled: true,
    storage: 'memory',
    ttl: 3600,              // 1 hora
    maxSize: 100,           // 100 itens
    checkPeriod: 600        // Verificar expiração a cada 10 min
  }
});

Cache com Redis

import Redis from 'ioredis';

const redis = new Redis({
  host: 'localhost',
  port: 6379,
  password: 'sua-senha-redis'
});

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  cache: {
    enabled: true,
    storage: 'redis',
    client: redis,
    ttl: 7200,              // 2 horas
    keyPrefix: 'qwen:cache:'
  }
});

Cache em Arquivo

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  cache: {
    enabled: true,
    storage: 'file',
    directory: './cache',   // Diretório do cache
    ttl: 86400,            // 24 horas
    maxSize: 1000          // 1000 arquivos
  }
});

Proxy

Proxy HTTP

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  proxy: {
    protocol: 'http',
    host: 'proxy.empresa.com',
    port: 8080,
    auth: {
      username: 'usuario',
      password: 'senha'
    }
  }
});

Proxy SOCKS

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  proxy: {
    protocol: 'socks5',
    host: 'socks-proxy.com',
    port: 1080
  }
});

Configurações por Ambiente

Desenvolvimento

// config/development.js
export default {
  qwen: {
    apiKey: process.env.QWEN_API_KEY,
    region: 'us-east-1',
    debug: true,
    timeout: 30000,
    maxRetries: 2,
    cache: {
      enabled: true,
      storage: 'memory',
      ttl: 1800,            // 30 minutos
      maxSize: 50
    },
    mock: {
      enabled: false,       // Usar API real em desenvolvimento
      delay: 1000          // Simular delay da API
    }
  }
};

Produção

// config/production.js
export default {
  qwen: {
    apiKey: process.env.QWEN_API_KEY,
    region: process.env.QWEN_REGION || 'us-east-1',
    debug: false,
    timeout: 60000,
    maxRetries: 5,
    retryDelay: 2000,
    cache: {
      enabled: true,
      storage: 'redis',
      ttl: 7200,            // 2 horas
      maxSize: 1000
    },
    monitoring: {
      enabled: true,
      metricsEndpoint: '/metrics',
      healthEndpoint: '/health'
    },
    rateLimit: {
      enabled: true,
      maxRequests: 100,
      windowMs: 60000       // 1 minuto
    }
  }
};

Teste

// config/test.js
export default {
  qwen: {
    apiKey: 'test-api-key',
    region: 'us-east-1',
    debug: true,
    timeout: 10000,
    maxRetries: 1,
    cache: {
      enabled: false        // Desabilitar cache em testes
    },
    mock: {
      enabled: true,        // Usar mock em testes
      responses: {
        editText: {
          imagemUrl: 'https://mock.qwen.com/result.jpg',
          creditos: 1
        }
      }
    }
  }
};

Logging

Configuração de Logs

import winston from 'winston';

// Logger personalizado
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
    new winston.transports.File({ filename: 'logs/combined.log' })
  ]
});

if (process.env.NODE_ENV !== 'production') {
  logger.add(new winston.transports.Console({
    format: winston.format.simple()
  }));
}

// Configurar editor com logger personalizado
const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  logger: logger,
  logLevel: 'debug'
});

Logs Estruturados

// Configuração para logs estruturados
const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  logging: {
    enabled: true,
    level: 'info',
    format: 'json',
    includeRequestId: true,
    includeTimestamp: true,
    fields: {
      service: 'qwen-image-edit',
      version: '1.0.0',
      environment: process.env.NODE_ENV
    }
  }
});

Segurança

Validação de Entrada

import Joi from 'joi';

// Schema de validação
const editTextSchema = Joi.object({
  imagem: Joi.alternatives().try(
    Joi.string().uri(),
    Joi.string().dataUri(),
    Joi.object().instance(Buffer),
    Joi.object().instance(File)
  ).required(),
  prompt: Joi.string().min(1).max(1000).required(),
  cor: Joi.string().pattern(/^#[0-9A-F]{6}$/i),
  tamanho: Joi.alternatives().try(
    Joi.string().valid('pequeno', 'medio', 'grande'),
    Joi.number().min(8).max(72)
  )
});

// Wrapper com validação
class SecureQwenImageEdit {
  constructor(config) {
    this.editor = new QwenImageEdit(config);
  }
  
  async editText(opcoes) {
    // Validar entrada
    const { error, value } = editTextSchema.validate(opcoes);
    if (error) {
      throw new Error(`Entrada inválida: ${error.message}`);
    }
    
    // Sanitizar prompt
    value.prompt = this.sanitizePrompt(value.prompt);
    
    return this.editor.editText(value);
  }
  
  sanitizePrompt(prompt) {
    // Remover caracteres perigosos
    return prompt
      .replace(/<script[^>]*>.*?<\/script>/gi, '')
      .replace(/<[^>]*>/g, '')
      .trim();
  }
}

const secureEditor = new SecureQwenImageEdit({
  apiKey: process.env.QWEN_API_KEY
});

Rotação de Chaves API

class QwenWithKeyRotation {
  constructor(keys, rotationInterval = 3600000) { // 1 hora
    this.keys = keys;
    this.currentKeyIndex = 0;
    this.rotationInterval = rotationInterval;
    
    // Rotacionar chaves automaticamente
    setInterval(() => {
      this.rotateKey();
    }, rotationInterval);
    
    this.editor = new QwenImageEdit({
      apiKey: this.getCurrentKey()
    });
  }
  
  getCurrentKey() {
    return this.keys[this.currentKeyIndex];
  }
  
  rotateKey() {
    this.currentKeyIndex = (this.currentKeyIndex + 1) % this.keys.length;
    this.editor.updateApiKey(this.getCurrentKey());
    console.log(`Chave API rotacionada para índice ${this.currentKeyIndex}`);
  }
  
  async editText(opcoes) {
    try {
      return await this.editor.editText(opcoes);
    } catch (error) {
      if (error.codigo === 'AUTH_INVALID') {
        // Tentar próxima chave
        this.rotateKey();
        return this.editor.editText(opcoes);
      }
      throw error;
    }
  }
}

const editorComRotacao = new QwenWithKeyRotation([
  process.env.QWEN_API_KEY_1,
  process.env.QWEN_API_KEY_2,
  process.env.QWEN_API_KEY_3
]);

Performance

Pool de Conexões

import { Agent } from 'https';

// Configurar pool de conexões
const httpsAgent = new Agent({
  keepAlive: true,
  maxSockets: 50,
  maxFreeSockets: 10,
  timeout: 60000,
  freeSocketTimeout: 30000
});

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  httpsAgent: httpsAgent
});

Compressão de Imagem

import sharp from 'sharp';

class OptimizedQwenImageEdit {
  constructor(config) {
    this.editor = new QwenImageEdit(config);
    this.maxImageSize = 5 * 1024 * 1024; // 5MB
  }
  
  async optimizeImage(imagePath) {
    const stats = await sharp(imagePath).stats();
    
    if (stats.size > this.maxImageSize) {
      // Redimensionar se muito grande
      const optimized = await sharp(imagePath)
        .resize(1920, 1080, {
          fit: 'inside',
          withoutEnlargement: true
        })
        .jpeg({ quality: 85 })
        .toBuffer();
        
      return optimized;
    }
    
    return imagePath;
  }
  
  async editText(opcoes) {
    // Otimizar imagem antes de enviar
    if (typeof opcoes.imagem === 'string') {
      opcoes.imagem = await this.optimizeImage(opcoes.imagem);
    }
    
    return this.editor.editText(opcoes);
  }
}

const optimizedEditor = new OptimizedQwenImageEdit({
  apiKey: process.env.QWEN_API_KEY
});

Gestão de Memória

class MemoryEfficientEditor {
  constructor(config) {
    this.editor = new QwenImageEdit(config);
    this.activeRequests = new Map();
    this.maxConcurrentRequests = 10;
  }
  
  async editText(opcoes) {
    // Limitar requisições concorrentes
    if (this.activeRequests.size >= this.maxConcurrentRequests) {
      await this.waitForSlot();
    }
    
    const requestId = this.generateRequestId();
    this.activeRequests.set(requestId, Date.now());
    
    try {
      const resultado = await this.editor.editText(opcoes);
      
      // Limpar referências para liberar memória
      if (Buffer.isBuffer(opcoes.imagem)) {
        opcoes.imagem = null;
      }
      
      return resultado;
    } finally {
      this.activeRequests.delete(requestId);
    }
  }
  
  async waitForSlot() {
    return new Promise((resolve) => {
      const checkSlot = () => {
        if (this.activeRequests.size < this.maxConcurrentRequests) {
          resolve();
        } else {
          setTimeout(checkSlot, 100);
        }
      };
      checkSlot();
    });
  }
  
  generateRequestId() {
    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}

const memoryEfficientEditor = new MemoryEfficientEditor({
  apiKey: process.env.QWEN_API_KEY
});

Monitoramento

Métricas

import prometheus from 'prom-client';

// Métricas Prometheus
const requestCounter = new prometheus.Counter({
  name: 'qwen_requests_total',
  help: 'Total de requisições para Qwen API',
  labelNames: ['method', 'status']
});

const requestDuration = new prometheus.Histogram({
  name: 'qwen_request_duration_seconds',
  help: 'Duração das requisições para Qwen API',
  labelNames: ['method'],
  buckets: [0.1, 0.5, 1, 2, 5, 10]
});

const activeRequests = new prometheus.Gauge({
  name: 'qwen_active_requests',
  help: 'Número de requisições ativas'
});

class MonitoredQwenImageEdit {
  constructor(config) {
    this.editor = new QwenImageEdit(config);
  }
  
  async editText(opcoes) {
    const startTime = Date.now();
    activeRequests.inc();
    
    try {
      const resultado = await this.editor.editText(opcoes);
      
      requestCounter.inc({ method: 'editText', status: 'success' });
      return resultado;
    } catch (error) {
      requestCounter.inc({ method: 'editText', status: 'error' });
      throw error;
    } finally {
      const duration = (Date.now() - startTime) / 1000;
      requestDuration.observe({ method: 'editText' }, duration);
      activeRequests.dec();
    }
  }
}

const monitoredEditor = new MonitoredQwenImageEdit({
  apiKey: process.env.QWEN_API_KEY
});

Health Check

class HealthCheckEditor {
  constructor(config) {
    this.editor = new QwenImageEdit(config);
    this.lastHealthCheck = null;
    this.isHealthy = true;
  }
  
  async healthCheck() {
    try {
      // Fazer uma requisição simples para verificar saúde
      const testImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==';
      
      const startTime = Date.now();
      await this.editor.analyzeImage({
        imagem: testImage,
        detectar: ['cores']
      });
      
      const responseTime = Date.now() - startTime;
      
      this.lastHealthCheck = {
        timestamp: new Date().toISOString(),
        status: 'healthy',
        responseTime: responseTime
      };
      
      this.isHealthy = true;
      return this.lastHealthCheck;
    } catch (error) {
      this.lastHealthCheck = {
        timestamp: new Date().toISOString(),
        status: 'unhealthy',
        error: error.message
      };
      
      this.isHealthy = false;
      return this.lastHealthCheck;
    }
  }
  
  getHealth() {
    return {
      status: this.isHealthy ? 'healthy' : 'unhealthy',
      lastCheck: this.lastHealthCheck
    };
  }
}

const healthCheckEditor = new HealthCheckEditor({
  apiKey: process.env.QWEN_API_KEY
});

// Verificar saúde a cada 5 minutos
setInterval(() => {
  healthCheckEditor.healthCheck();
}, 5 * 60 * 1000);

Configuração para Desenvolvimento

Hot Reload

// webpack.config.js
module.exports = {
  // ... outras configurações
  devServer: {
    hot: true,
    proxy: {
      '/api/qwen': {
        target: 'https://api.qwen.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api/qwen': ''
        }
      }
    }
  }
};

Mock para Desenvolvimento

// mock/qwen-mock.js
class QwenMock {
  constructor() {
    this.delay = 1000; // Simular delay da API
  }
  
  async editText(opcoes) {
    await this.simulateDelay();
    
    return {
      imagemUrl: `https://mock.qwen.com/result_${Date.now()}.jpg`,
      imagemOriginal: opcoes.imagem,
      alteracoes: [
        {
          tipo: 'texto_editado',
          textoOriginal: 'MOCK_ORIGINAL',
          textoNovo: 'MOCK_EDITADO',
          posicao: { x: 100, y: 100 }
        }
      ],
      tempoProcessamento: this.delay / 1000,
      creditos: 1
    };
  }
  
  async simulateDelay() {
    return new Promise(resolve => setTimeout(resolve, this.delay));
  }
}

// Usar mock em desenvolvimento
const editor = process.env.NODE_ENV === 'development' && process.env.USE_MOCK
  ? new QwenMock()
  : new QwenImageEdit({ apiKey: process.env.QWEN_API_KEY });

Melhores Práticas

1. Configuração por Ambiente

// config/index.js
const configs = {
  development: require('./development'),
  production: require('./production'),
  test: require('./test')
};

const env = process.env.NODE_ENV || 'development';
export default configs[env];

2. Validação de Configuração

import Joi from 'joi';

const configSchema = Joi.object({
  qwen: Joi.object({
    apiKey: Joi.string().required(),
    region: Joi.string().valid('us-east-1', 'us-west-1', 'eu-west-1', 'ap-southeast-1'),
    timeout: Joi.number().min(1000).max(300000),
    maxRetries: Joi.number().min(0).max(10),
    cache: Joi.object({
      enabled: Joi.boolean(),
      ttl: Joi.number().min(60),
      maxSize: Joi.number().min(1)
    })
  }).required()
});

export function validateConfig(config) {
  const { error, value } = configSchema.validate(config);
  if (error) {
    throw new Error(`Configuração inválida: ${error.message}`);
  }
  return value;
}

3. Configuração Centralizada

// services/qwen-service.js
import config from '../config';
import { QwenImageEdit } from 'qwen-image-edit';

class QwenService {
  constructor() {
    this.editor = new QwenImageEdit(config.qwen);
  }
  
  static getInstance() {
    if (!QwenService.instance) {
      QwenService.instance = new QwenService();
    }
    return QwenService.instance;
  }
  
  getEditor() {
    return this.editor;
  }
}

export default QwenService.getInstance();

Solução de Problemas de Configuração

Verificar Configuração

// scripts/check-config.js
import { QwenImageEdit } from 'qwen-image-edit';

async function checkConfiguration() {
  try {
    const editor = new QwenImageEdit({
      apiKey: process.env.QWEN_API_KEY,
      region: process.env.QWEN_REGION
    });
    
    // Testar conexão
    const quota = await editor.getQuota();
    console.log('✅ Configuração válida');
    console.log('📊 Cota atual:', quota);
    
  } catch (error) {
    console.error('❌ Erro na configuração:', error.message);
    process.exit(1);
  }
}

checkConfiguration();

Debug de Configuração

// Habilitar debug detalhado
const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY,
  debug: true,
  logLevel: 'debug',
  onRequest: (config) => {
    console.log('📤 Requisição:', {
      url: config.url,
      method: config.method,
      headers: config.headers
    });
  },
  onResponse: (response) => {
    console.log('📥 Resposta:', {
      status: response.status,
      headers: response.headers
    });
  },
  onError: (error) => {
    console.error('❌ Erro:', error);
  }
});

Próximos Passos: