API key dan timeout yang di-hardcode berfungsi untuk prototipe. Production memerlukan konfigurasi spesifik per lingkungan, secret management, dan kemampuan mengubah pengaturan tanpa re-deploy.
Hierarki Konfigurasi
Priority (highest → lowest):
1. Environment variables ← deployment-specific overrides
2. Config file (YAML/JSON) ← version-controlled defaults
3. Application defaults ← fallback values in code
Referensi Konfigurasi Lengkap
| Parameter | Env Variable | Default | Deskripsi |
|---|---|---|---|
| API Key | CAPTCHAAI_API_KEY |
— | Wajib. API key CaptchaAI Anda |
| Submit URL | CAPTCHAAI_SUBMIT_URL |
https://ocr.captchaai.com/in.php |
Endpoint submit task |
| Poll URL | CAPTCHAAI_POLL_URL |
https://ocr.captchaai.com/res.php |
Endpoint polling hasil |
| Interval poll | CAPTCHAAI_POLL_INTERVAL |
5 |
Detik antar polling |
| Maks poll | CAPTCHAAI_MAX_POLLS |
60 |
Maksimum polling sebelum timeout |
| Concurrency | CAPTCHAAI_CONCURRENCY |
10 |
Maksimum task CAPTCHA paralel |
| Timeout | CAPTCHAAI_TIMEOUT |
300 |
Timeout keseluruhan dalam detik |
| Proxy | CAPTCHAAI_PROXY |
— | URL proxy untuk solve CAPTCHA |
| Callback URL | CAPTCHAAI_CALLBACK_URL |
— | URL webhook untuk hasil async |
| Retry | CAPTCHAAI_RETRIES |
3 |
Percobaan ulang untuk kegagalan sementara |
| Log level | CAPTCHAAI_LOG_LEVEL |
info |
Verbositas logging |
Config Loader
Python
import os
import yaml
from dataclasses import dataclass, field
from pathlib import Path
@dataclass
class CaptchaAIConfig:
api_key: str = ""
submit_url: str = "https://ocr.captchaai.com/in.php"
poll_url: str = "https://ocr.captchaai.com/res.php"
poll_interval: int = 5
max_polls: int = 60
concurrency: int = 10
timeout: int = 300
proxy: str = ""
callback_url: str = ""
retries: int = 3
log_level: str = "info"
@classmethod
def load(cls, config_path=None):
"""Load config: env vars override file, which overrides defaults."""
config = cls()
# Layer 2: Config file
if config_path and Path(config_path).exists():
with open(config_path) as f:
file_config = yaml.safe_load(f) or {}
for key, value in file_config.items():
if hasattr(config, key):
setattr(config, key, value)
# Layer 1: Environment variables (highest priority)
env_map = {
"CAPTCHAAI_API_KEY": "api_key",
"CAPTCHAAI_SUBMIT_URL": "submit_url",
"CAPTCHAAI_POLL_URL": "poll_url",
"CAPTCHAAI_POLL_INTERVAL": "poll_interval",
"CAPTCHAAI_MAX_POLLS": "max_polls",
"CAPTCHAAI_CONCURRENCY": "concurrency",
"CAPTCHAAI_TIMEOUT": "timeout",
"CAPTCHAAI_PROXY": "proxy",
"CAPTCHAAI_CALLBACK_URL": "callback_url",
"CAPTCHAAI_RETRIES": "retries",
"CAPTCHAAI_LOG_LEVEL": "log_level",
}
for env_key, attr_name in env_map.items():
value = os.environ.get(env_key)
if value is not None:
# Cast to correct type
current = getattr(config, attr_name)
if isinstance(current, int):
value = int(value)
setattr(config, attr_name, value)
config.validate()
return config
def validate(self):
if not self.api_key:
raise ValueError("CAPTCHAAI_API_KEY is required")
if self.poll_interval < 1:
raise ValueError("poll_interval must be >= 1")
if self.concurrency < 1:
raise ValueError("concurrency must be >= 1")
# Usage
config = CaptchaAIConfig.load("config/captchaai.yaml")
print(f"Concurrency: {config.concurrency}, Timeout: {config.timeout}s")
JavaScript
const fs = require("fs");
const yaml = require("js-yaml");
const path = require("path");
class CaptchaAIConfig {
static defaults = {
apiKey: "",
submitUrl: "https://ocr.captchaai.com/in.php",
pollUrl: "https://ocr.captchaai.com/res.php",
pollInterval: 5,
maxPolls: 60,
concurrency: 10,
timeout: 300,
proxy: "",
callbackUrl: "",
retries: 3,
logLevel: "info",
};
static envMap = {
CAPTCHAAI_API_KEY: "apiKey",
CAPTCHAAI_SUBMIT_URL: "submitUrl",
CAPTCHAAI_POLL_URL: "pollUrl",
CAPTCHAAI_POLL_INTERVAL: { key: "pollInterval", type: "int" },
CAPTCHAAI_MAX_POLLS: { key: "maxPolls", type: "int" },
CAPTCHAAI_CONCURRENCY: { key: "concurrency", type: "int" },
CAPTCHAAI_TIMEOUT: { key: "timeout", type: "int" },
CAPTCHAAI_PROXY: "proxy",
CAPTCHAAI_CALLBACK_URL: "callbackUrl",
CAPTCHAAI_RETRIES: { key: "retries", type: "int" },
CAPTCHAAI_LOG_LEVEL: "logLevel",
};
static load(configPath = null) {
let config = { ...CaptchaAIConfig.defaults };
// Layer 2: Config file
if (configPath && fs.existsSync(configPath)) {
const ext = path.extname(configPath);
const raw = fs.readFileSync(configPath, "utf8");
const fileConfig = ext === ".json" ? JSON.parse(raw) : yaml.load(raw);
config = { ...config, ...fileConfig };
}
// Layer 1: Environment variables
for (const [envKey, mapping] of Object.entries(CaptchaAIConfig.envMap)) {
const value = process.env[envKey];
if (value !== undefined) {
const attrKey = typeof mapping === "string" ? mapping : mapping.key;
const type = typeof mapping === "string" ? "string" : mapping.type;
config[attrKey] = type === "int" ? parseInt(value, 10) : value;
}
}
CaptchaAIConfig.validate(config);
return config;
}
static validate(config) {
if (!config.apiKey) throw new Error("CAPTCHAAI_API_KEY is required");
if (config.pollInterval < 1) throw new Error("pollInterval must be >= 1");
if (config.concurrency < 1) throw new Error("concurrency must be >= 1");
}
}
// Usage
const config = CaptchaAIConfig.load("config/captchaai.yaml");
console.log(`Concurrency: ${config.concurrency}, Timeout: ${config.timeout}s`);
File Konfigurasi Per Lingkungan
# config/captchaai.yaml — base
api_key: "" # Selalu set via env var
concurrency: 5
poll_interval: 5
retries: 3
log_level: info
# config/captchaai.production.yaml
concurrency: 20
poll_interval: 3
timeout: 180
log_level: warning
# config/captchaai.staging.yaml
concurrency: 3
poll_interval: 5
timeout: 300
log_level: debug
Secret Management
Jangan pernah menyimpan API key di file konfigurasi atau version control.
| Metode | Terbaik untuk | Contoh |
|---|---|---|
| Environment variable | Container, CI/CD | export CAPTCHAAI_API_KEY=abc123 |
| AWS Secrets Manager | Infrastruktur AWS | Ambil saat startup; rotasi otomatis |
| HashiCorp Vault | Multi-cloud, on-prem | Dynamic secret dengan TTL |
| Docker Secrets | Docker Swarm/Compose | Di-mount di /run/secrets/ |
File .env (dev only) |
Development lokal | Library dotenv; tambahkan ke .gitignore |
Contoh Penulisan Docker
services:
captcha-worker:
image: captcha-worker:latest
environment:
- CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
- CAPTCHAAI_CONCURRENCY=15
- CAPTCHAAI_LOG_LEVEL=warning
env_file:
- .env.production
Feature Flag
Alihkan kemampuan tanpa re-deploy:
class FeatureFlags:
def __init__(self):
self.flags = {
"use_callback": os.environ.get("FF_USE_CALLBACK", "false") == "true",
"enable_proxy": os.environ.get("FF_ENABLE_PROXY", "true") == "true",
"max_concurrent": int(os.environ.get("FF_MAX_CONCURRENT", "10")),
}
def is_enabled(self, flag):
return self.flags.get(flag, False)
def get(self, flag, default=None):
return self.flags.get(flag, default)
Pemecahan Masalah
| Masalah | Penyebab | Solusi |
|---|---|---|
| API key tidak termuat | Env var tidak ada; nama variabel salah | Cek echo $CAPTCHAAI_API_KEY; verifikasi ejaan |
| File konfigurasi diabaikan | Path salah atau library YAML tidak ada | Verifikasi file ada; install pyyaml / js-yaml |
| Production menggunakan pengaturan dev | Override spesifik lingkungan tidak diterapkan | Cek prioritas env var; verifikasi NODE_ENV / APP_ENV |
| Secret terlihat di log | Config dump menyertakan API key | Mask field sensitif dalam output log |
Pertanyaan Umum
Haruskah saya menggunakan YAML atau JSON untuk file konfigurasi?
YAML untuk file yang diedit manusia (mendukung komentar). JSON untuk konfigurasi yang dihasilkan mesin atau saat Anda ingin parsing yang ketat.
Seberapa sering saya harus rotasi API key?
Rotasi segera jika dikompromikan. Jadwalkan rotasi setiap 90 hari untuk compliance. Gunakan secret manager yang mendukung rotasi otomatis.
Bisakah saya mengubah concurrency tanpa restart?
Ya — baca pengaturan dari environment variable atau config service di setiap batch task, bukan hanya saat startup. Ini memungkinkan Anda menyesuaikan concurrency dengan memperbarui env var dan mengirim sinyal reload.
Artikel Terkait
- IP Whitelist CaptchaAI dan Keamanan API Key
- CaptchaAI dalam Docker
Siapkan konfigurasi production Anda — mulai dengan API key CaptchaAI dan buat dari template konfigurasi di atas.