المرجع

CaptchaAI في الإنتاج: دليل إدارة التكوين

يقوم تكوين CaptchaAI في الإنتاج على قاعدة واحدة: افصل الإعدادات عن الكود، وحمّلها من ثلاث طبقات مرتّبة — متغيرات البيئة، ثم ملف التكوين، ثم القيم الافتراضية داخل الكود. بهذا الترتيب تُغيّر التزامن أو المهلة أو مفتاح الـ API لبيئة بعينها دون أن تلمس سطرًا واحدًا من الكود أو تعيد النشر. أما تثبيت المفاتيح والمهلات داخل الكود مباشرةً فيصلح للنماذج الأولية فقط، ويتحوّل إلى عبء تشغيلي فور وصولك إلى بيئات متعددة وفريق يتشارك الأسرار.

ترتيب أولوية طبقات التكوين

Priority (highest → lowest):

1. Environment variables     ← deployment-specific overrides
2. Config file (YAML/JSON)   ← version-controlled defaults
3. Application defaults      ← fallback values in code

القاعدة بسيطة: كلّما اقتربت الطبقة من مُشغّل الخدمة، ارتفعت أولويتها. تتجاوز متغيرات البيئة ما في ملف التكوين، ويتجاوز ملف التكوين القيم الافتراضية المدمجة في الكود. وإذا كانت خدمتك تدعم وسائط سطر الأوامر، فأضِفها كطبقة رابعة فوق متغيرات البيئة بالمنطق نفسه. وزّع القيم على الطبقات الثلاث بحسب حساسيتها وتكرار تغيّرها:

  • متغيرات البيئة: مفتاح الـ API والأسرار وأي قيمة تختلف بين النشرات مثل التزامن أو الخادم الوسيط.
  • ملف التكوين: القيم الافتراضية الآمنة المشتركة بين أعضاء الفريق والقابلة للحفظ في نظام التحكم بالمصادر.
  • القيم الافتراضية في الكود: شبكة أمان أخيرة تُبقي الخدمة قابلة للتشغيل حتى دون أي إعداد خارجي.

تحميل التكوين برمجيًا

يقرأ المُحمِّل التالي الطبقات الثلاث بالترتيب الصحيح، ثم يتحقق من القيم قبل تشغيل الخدمة: يفشل مبكرًا إذا كان مفتاح الـ API غائبًا أو كانت قيم التزامن أو الاستطلاع خارج النطاق المسموح.

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`);

المرجع الكامل لمعلمات التكوين

المعلمة متغير البيئة القيمة الافتراضية الوصف
مفتاح الـ API CAPTCHAAI_API_KEY مطلوب. مفتاح CaptchaAI API الخاص بك
رابط الإرسال CAPTCHAAI_SUBMIT_URL https://ocr.captchaai.com/in.php نقطة نهاية إرسال المهمة
رابط الاستطلاع CAPTCHAAI_POLL_URL https://ocr.captchaai.com/res.php نقطة نهاية استطلاع النتيجة
فاصل الاستطلاع CAPTCHAAI_POLL_INTERVAL 5 الثواني بين محاولات الاستطلاع
أقصى عدد لمحاولات الاستطلاع CAPTCHAAI_MAX_POLLS 60 الحد الأقصى للمحاولات قبل انتهاء المهلة
التزامن CAPTCHAAI_CONCURRENCY 10 أقصى عدد لمهام CAPTCHA المتوازية
المهلة CAPTCHAAI_TIMEOUT 300 المهلة الإجمالية بالثواني
الخادم الوسيط CAPTCHAAI_PROXY رابط الخادم الوسيط لحل اختبار CAPTCHA
رابط رد النداء CAPTCHAAI_CALLBACK_URL رابط الـ Webhook للنتائج غير المتزامنة
إعادة المحاولة CAPTCHAAI_RETRIES 3 إعادة المحاولة عند حالات الفشل العابرة
مستوى السجل CAPTCHAAI_LOG_LEVEL info مستوى تفصيل التسجيل

ملفات تكوين منفصلة لكل بيئة

احتفظ بملف أساسي واحد يضم القيم الآمنة المشتركة، ثم أضِف ملفًا لكل بيئة يتجاوز ما يلزم فقط. لاحظ أن مفتاح الـ API يبقى فارغًا في كل هذه الملفات ويُضبط دائمًا عبر متغير بيئة.

# config/captchaai.yaml — base
api_key: ""  # Always 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

سيناريو عملي: مطابقة التزامن مع عدد الـ Threads

تخيّل فريقًا في الرياض يشغّل خط استخراج بيانات ليليًا يعتمد على CaptchaAI. اشترك الفريق في خطة ADVANCE بسعر 90 دولارًا شهريًا وتمنحه 50 خيط معالجة (Thread) مع عدد حلول غير محدود لكل خيط. القاعدة هنا مباشرة: لا تضبط CAPTCHAAI_CONCURRENCY بقيمة تتجاوز عدد الـ Threads المتاح في خطتك. فبقيمة تزامن قدرها 20 في الإنتاج، يبقى للفريق هامش واسع ضمن حدّ الخطة، بينما تُخفَّض القيمة إلى 3 في بيئة الاختبار كي لا تستهلك الخيوط المخصّصة للعمل الفعلي. ولأن الفوترة قائمة على الخيوط لا على عدد الحلول، فإن ضبط التزامن بذكاء يمنحك أقصى إنتاجية دون أي رسوم إضافية لكل عملية حل.

إدارة الأسرار ومفاتيح الـ API

لا تخزّن مفاتيح الـ API إطلاقًا داخل ملفات التكوين أو نظام التحكم بالمصادر، والتزم بثلاث قواعد أساسية:

  • احقن المفتاح كمتغير بيئة عند التشغيل، لا داخل صورة الحاوية أو المستودع.
  • امنح كل بيئة مفتاحًا مستقلًا يسهل إبطاله وتدويره دون التأثير على البيئات الأخرى.
  • أخفِ المفتاح من السجلات ومخرجات التصحيح كي لا يتسرّب عبر لقطات الأخطاء.
الطريقة الأنسب لـ مثال
متغيرات البيئة الحاويات وأنظمة CI/CD export CAPTCHAAI_API_KEY=abc123
AWS Secrets Manager البنية التحتية على AWS جلب المفتاح عند بدء التشغيل مع تدوير تلقائي
HashiCorp Vault البيئات متعددة السحابة والاستضافة الداخلية أسرار ديناميكية بعمر محدد (TTL)
Docker Secrets Docker Swarm / Compose تُثبَّت على المسار /run/secrets/
ملف .env (للتطوير المحلي فقط) التطوير على جهاز المطوّر مكتبة dotenv مع إضافته إلى .gitignore

مثال Docker Compose

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 Flags) لتفعيل القدرات أو تعطيلها من متغيرات البيئة دون إعادة بناء الخدمة أو نشرها من جديد:

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)

الأسئلة الشائعة

كيف أضبط قيمة CAPTCHAAI_CONCURRENCY بما يناسب خطتي؟

اجعل قيمة التزامن مساوية لعدد الـ Threads في خطتك أو أقل منه. مثلًا خطة ADVANCE ($90 شهريًا، 50 خيطًا) تحتمل تزامنًا حتى 50 مهمة متوازية. وبما أن كل خطة تشمل حلولًا غير محدودة لكل خيط، فإن الحدّ الفعلي على إنتاجيتك هو عدد الخيوط وسرعة الحل لكل نوع، لا عدد العمليات.

أين أخزّن مفتاح الـ API في خطوط CI/CD بأمان؟

استخدم مخزن الأسرار المدمج في منصتك — مثل الأسرار المشفّرة في GitHub — وحقنه كمتغير بيئة عند التشغيل فقط. لا تكتب المفتاح داخل ملف التكوين أو سجلات البناء، ووجّه الخدمات المؤتمتة إلى AWS Secrets Manager أو HashiCorp Vault لجلبه لحظة بدء التشغيل.

لماذا تنجح بيئة الاختبار وتفشل بيئة الإنتاج؟

غالبًا لأن تجاوز البيئة الخاص بالإنتاج لم يُحمَّل، فتعمل الخدمة بقيم الاختبار. تأكد من ترتيب أولوية الطبقات، وأن ملف captchaai.production.yaml أو متغيرات البيئة المناسبة مضبوطة فعلًا، وأن سياق الخادم الوسيط والرؤوس مطابق لما نجح في الاختبار.

هل يمكنني تغيير الإعدادات دون إعادة النشر؟

نعم. اقرأ الإعدادات من متغيرات البيئة أو من خدمة تكوين مركزية مع كل دفعة مهام، لا عند بدء التشغيل فقط. عندها يكفي تحديث المتغير وإرسال إشارة إعادة تحميل لتطبيق القيمة الجديدة فورًا دون إيقاف الخدمة.

معالجة المشكلات الشائعة

المشكلة السبب المحتمل الحل
مفتاح الـ API لا يُحمَّل متغير البيئة غير مضبوط أو الاسم مكتوب خطأً تحقق من echo $CAPTCHAAI_API_KEY وراجع تهجئة الاسم
ملف التكوين يُتجاهَل المسار خاطئ أو مكتبة YAML غير مثبّتة تأكد من وجود الملف وثبّت pyyaml أو js-yaml
الإنتاج يعمل بإعدادات التطوير تجاوز البيئة الخاص لم يُطبَّق راجع أولوية المتغيرات وقيمة APP_ENV أو NODE_ENV
ظهور المفتاح داخل السجلات طباعة التكوين تتضمن مفتاح الـ API أخفِ الحقول الحساسة في مخرجات السجل

الخطوات التالية

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.