مستندات Admin API

رابط برنامه‌نویسی مدیریتی (Admin API) به شما امکان مدیریت کامل سرور از طریق HTTP را می‌دهد. تمام عملیات‌هایی که از طریق خط فرمان (CLI) قابل انجام هستند، از طریق این API نیز در دسترس‌اند.

فهرست مطالب

  1. معماری و نحوه کار
  2. احراز هویت و توکن
  3. ساختار درخواست و پاسخ
  4. شروع سریع
  5. وضعیت سرور — /admin/status
  6. اطلاعات فضای ذخیره‌سازی — /admin/storage
  7. مدیریت ثبت‌نام — /admin/registration
  8. ثبت‌نام آنی — /admin/registration/jit
  9. سرویس تماس — /admin/services/turn
  10. سرویس Iroh — /admin/services/iroh
  11. سرویس Shadowsocks — /admin/services/shadowsocks
  12. مدیریت لاگ — /admin/services/log
  13. تنظیمات یکجا — /admin/settings
  14. تنظیمات پورت‌ها
  15. تنظیمات پیکربندی
  16. مدیریت حساب‌ها — /admin/accounts
  17. لیست مسدودی — /admin/blocklist
  18. مدیریت سهمیه — /admin/quota
  19. عملیات صف — /admin/queue
  20. اشتراک‌گذاری مخاطبین — /admin/shares
  21. بازنویسی DNS — /admin/dns
  22. رابط وب مدیریت
  23. کدهای خطا

معماری و نحوه کار

Admin API از یک معماری RPC تک‌نقطه (Single-Endpoint RPC) استفاده می‌کند. به جای داشتن مسیرهای HTTP مختلف برای هر عملیات، تمام درخواست‌ها به یک آدرس واحد ارسال می‌شوند:

Client → POST /api/admin → JSON Request → Auth Check → Resource Handler → JSON Response
چرا تک‌نقطه؟ این طراحی امکان مخفی‌سازی بهتر API را فراهم می‌کند. از بیرون، تنها یک مسیر HTTP قابل مشاهده است و تمام پاسخ‌ها (حتی خطاها) با کد HTTP 200 بازگردانده می‌شوند. وضعیت واقعی عملیات فقط در بدنه JSON قابل مشاهده است.

احراز هویت و توکن

توکن خودکار

توکن مدیریتی به صورت خودکار در اولین اجرای سرور تولید و در مسیر /var/lib/maddy/admin_token ذخیره می‌شود. این توکن شامل ۲۵۶ بیت آنتروپی رمزنگاری‌شده است و در هر بار راه‌اندازی مجدد حفظ می‌شود.

دریافت توکن با CLI

# Show current token
maddy admin-token

# Save to variable
TOKEN=$(maddy admin-token)

تنظیمات توکن سفارشی

شما می‌توانید در فایل maddy.conf یکی از تنظیمات زیر را اعمال کنید:

# Set custom token
admin_token your-custom-secret-token

# Disable API entirely
admin_token disabled
🔒 امنیت توکن: فایل توکن با مجوز 0600 (فقط قابل خواندن توسط root) ذخیره می‌شود. توکن هیچ‌گاه در لاگ‌ها ثبت نمی‌شود و مقایسه آن به صورت ثابت‌زمان (constant-time) انجام می‌شود.

ساختار درخواست و پاسخ

درخواست (Request)

تمام درخواست‌ها باید به صورت POST به آدرس /api/admin ارسال شوند:

{
    "method":   "GET",
    "resource": "/admin/status",
    "headers":  {
        "Authorization": "Bearer YOUR_TOKEN"
    },
    "body":     {}
}

پاسخ (Response)

تمام پاسخ‌ها با فرمت یکسان بازگردانده می‌شوند:

{
    "status":   200,
    "resource": "/admin/status",
    "body":     { ... },
    "error":    null
}
⚠️ توجه: کد HTTP خارجی همیشه 200 است. برای بررسی نتیجه واقعی، فیلد status در بدنه JSON را بررسی کنید.

شروع سریع

نمونه‌ای ساده از استفاده با curl:

# Get token
TOKEN=$(maddy admin-token)

# Check server status
curl -s -X POST https://your-server/api/admin \
  -H 'Content-Type: application/json' \
  -d "{
    \"method\": \"GET\",
    \"resource\": \"/admin/status\",
    \"headers\": {\"Authorization\": \"Bearer $TOKEN\"}
  }" | python3 -m json.tool

یا با استفاده از اسکریپت پایتون:

import requests

TOKEN = "your-admin-token"
BASE  = "https://your-server"

def api(resource, method="GET", body=None):
    resp = requests.post(f"{BASE}/api/admin", json={
        "method": method,
        "resource": resource,
        "headers": {"Authorization": f"Bearer {TOKEN}"},
        "body": body or {},
    })
    return resp.json()

# Example: get status
print(api("/admin/status"))

وضعیت سرور

/admin/status GET

نمایش وضعیت کلی سرور شامل تعداد کاربران، آپتایم و آمار سرورهای ایمیل.

درخواست:
{
    "method": "GET",
    "resource": "/admin/status",
    "headers": {"Authorization": "Bearer TOKEN"}
}
پاسخ:
{
    "status": 200,
    "body": {
        "users": {
            "registered": 799
        },
        "uptime": {
            "boot_time": "2026-02-17T20:51:21Z",
            "duration": "2d 5h 30m 15s"
        },
        "email_servers": {
            "connection_ips": 12,
            "domain_servers": 8,
            "ip_servers": 3
        }
    }
}
Field Type Description
users.registered int Total registered accounts
uptime.boot_time string Server boot time (RFC3339)
uptime.duration string Human-readable uptime
email_servers.connection_ips int Unique connecting IPs since boot
email_servers.domain_servers int Unique domain servers seen
email_servers.ip_servers int Unique IP-based servers seen

اطلاعات فضای ذخیره‌سازی

/admin/storage GET

اطلاعات دیسک، پوشه داده و حجم دیتابیس.

پاسخ:
{
    "status": 200,
    "body": {
        "disk": {
            "total_bytes": 53687091200,
            "used_bytes": 18253611008,
            "available_bytes": 35433480192,
            "percent_used": 34.0
        },
        "state_dir": {
            "path": "/var/lib/maddy",
            "size_bytes": 1073741824
        },
        "database": {
            "driver": "sqlite3",
            "size_bytes": 52428800
        }
    }
}

مدیریت ثبت‌نام

/admin/registration GET POST

باز و بسته کردن ثبت‌نام کاربران جدید.

مشاهده وضعیت فعلی:
// Request
{"method": "GET", "resource": "/admin/registration", ...}

// Response
{"status": 200, "body": {"status": "open"}}
تغییر وضعیت:
// Close registration
{"method": "POST", "resource": "/admin/registration",
 "body": {"action": "close"}, ...}

// Open registration
{"method": "POST", "resource": "/admin/registration",
 "body": {"action": "open"}, ...}
Action Description
open Allow new user registration
close Block new registrations, existing users unaffected

ثبت‌نام آنی (JIT)

/admin/registration/jit GET POST

فعال/غیرفعال کردن ثبت‌نام آنی. وقتی فعال باشد، حساب کاربری در اولین لاگین به‌صورت خودکار ساخته می‌شود.

// Enable
{"method": "POST", "resource": "/admin/registration/jit",
 "body": {"action": "enable"}, ...}

// Disable
{"method": "POST", "resource": "/admin/registration/jit",
 "body": {"action": "disable"}, ...}

// Response
{"status": 200, "body": {"status": "enabled"}}

سرویس تماس (TURN)

/admin/services/turn GET POST

فعال/غیرفعال کردن سرور TURN برای تماس‌های صوتی و تصویری.

// Enable
{"method": "POST", "resource": "/admin/services/turn",
 "body": {"action": "enable"}, ...}

// Disable
{"method": "POST", "resource": "/admin/services/turn",
 "body": {"action": "disable"}, ...}

سرویس Iroh

/admin/services/iroh GET POST

فعال/غیرفعال کردن سرور رله Iroh برای اتصالات Webxdc بلادرنگ.

// Enable
{"method": "POST", "resource": "/admin/services/iroh",
 "body": {"action": "enable"}, ...}

// Disable
{"method": "POST", "resource": "/admin/services/iroh",
 "body": {"action": "disable"}, ...}

سرویس Shadowsocks

/admin/services/shadowsocks GET POST

فعال/غیرفعال کردن پروکسی Shadowsocks. این سرویس برای عبور از سانسور استفاده می‌شود.

// Enable
{"method": "POST", "resource": "/admin/services/shadowsocks",
 "body": {"action": "enable"}, ...}

// Disable
{"method": "POST", "resource": "/admin/services/shadowsocks",
 "body": {"action": "disable"}, ...}

مدیریت لاگ

/admin/services/log GET POST

فعال/غیرفعال کردن ثبت لاگ سرور (سیاست بدون لاگ).

// Enable
{"method": "POST", "resource": "/admin/services/log",
 "body": {"action": "enable"}, ...}

// Disable
{"method": "POST", "resource": "/admin/services/log",
 "body": {"action": "disable"}, ...}

تنظیمات یکجا

/admin/settings GET

دریافت تمام تنظیمات سرور در یک درخواست واحد. شامل کلیدهای روشن/خاموش، پورت‌ها و تنظیمات پیکربندی.

// Request
{"method": "GET", "resource": "/admin/settings", ...}

// Response (partial)
{
    "status": 200,
    "body": {
        "registration": "closed",
        "turn_enabled": "enabled",
        "iroh_enabled": "enabled",
        "ss_enabled": "enabled",
        "smtp_port": {"key": "__SMTP_PORT__", "value": "2525", "is_set": true},
        "turn_secret": {"key": "__TURN_SECRET__", "value": "", "is_set": false}
    }
}

تنظیمات پورت‌ها

هر پورت سرویسی از طریق یک مسیر اختصاصی قابل تنظیم است. مقادیر در دیتابیس ذخیره می‌شوند و بر مقادیر فایل پیکربندی اولویت دارند.

GET POST

Endpoint Description
/admin/settings/smtp_port SMTP server port
/admin/settings/submission_port Submission server port
/admin/settings/imap_port IMAP server port
/admin/settings/turn_port TURN relay port
/admin/settings/dovecot_port Dovecot SASL port
/admin/settings/iroh_port Iroh relay port
/admin/settings/ss_port Shadowsocks proxy port
Set:
{"method": "POST", "resource": "/admin/settings/smtp_port",
 "body": {"action": "set", "value": "2525"}, ...}

// Response
{"key": "__SMTP_PORT__", "value": "2525", "is_set": true}
Reset:
{"method": "POST", "resource": "/admin/settings/smtp_port",
 "body": {"action": "reset"}, ...}

تنظیمات پیکربندی

تنظیمات پیکربندی سرویس‌ها مانند hostname، secret و URL از طریق همان الگوی set/reset قابل مدیریت هستند.

GET POST

Endpoint Description
/admin/settings/smtp_hostname SMTP server hostname
/admin/settings/turn_realm TURN server realm
/admin/settings/turn_secret TURN shared secret
/admin/settings/turn_relay_ip TURN relay IP address
/admin/settings/turn_ttl TURN credential TTL (seconds)
/admin/settings/iroh_relay_url Iroh relay URL
/admin/settings/ss_cipher Shadowsocks cipher algorithm
/admin/settings/ss_password Shadowsocks password
Example — set TURN secret:
{"method": "POST", "resource": "/admin/settings/turn_secret",
 "body": {"action": "set", "value": "my-shared-secret"}, ...}
Example — read Iroh relay URL:
// Request
{"method": "GET", "resource": "/admin/settings/iroh_relay_url", ...}

// Response
{"key": "__IROH_RELAY_URL__", "value": "https://iroh.example.com", "is_set": true}
💡 نکته: وقتی یک تنظیم is_set: false باشد، به معنی استفاده از مقدار پیش‌فرض فایل پیکربندی است. با action: "reset" می‌توانید هر تنظیمی را به مقدار پیش‌فرض بازگردانید.

مدیریت حساب‌ها

/admin/accounts GET DELETE

لیست و حذف حساب‌های کاربری. ایجاد حساب از طریق API امکان‌پذیر نیست (رمز عبورها هیچ‌گاه از طریق API منتقل نمی‌شوند).

لیست تمام حساب‌ها:
// Request
{"method": "GET", "resource": "/admin/accounts", ...}

// Response
{
    "status": 200,
    "body": {
        "total": 3,
        "accounts": [
            {"username": "alice@example.com"},
            {"username": "bob@example.com"},
            {"username": "charlie@example.com"}
        ]
    }
}
حذف یک حساب:
// Request
{"method": "DELETE", "resource": "/admin/accounts",
 "body": {"username": "alice@example.com"}, ...}

// Response
{"status": 200, "body": {"deleted": "alice@example.com"}}
⚠️ هشدار: حذف حساب غیرقابل بازگشت است. تمام ایمیل‌ها، تنظیمات و اطلاعات کاربر به‌طور کامل حذف خواهند شد و نام کاربری برای همیشه مسدود می‌شود.

لیست مسدودی

/admin/blocklist GET POST DELETE

مدیریت لیست نام‌های کاربری مسدود. کاربران مسدود نمی‌توانند از طریق /new یا JIT ثبت‌نام کنند. حساب‌های حذف‌شده به‌طور خودکار به این لیست اضافه می‌شوند.

لیست کاربران مسدود:
// Request
{"method": "GET", "resource": "/admin/blocklist", ...}

// Response
{
    "status": 200,
    "body": {
        "total": 2,
        "blocked": [
            {
                "username": "spammer@example.com",
                "reason": "deleted via admin panel",
                "blocked_at": "2026-02-18T15:30:00Z"
            }
        ]
    }
}
مسدود کردن یک کاربر:
{"method": "POST", "resource": "/admin/blocklist",
 "body": {"username": "spammer@example.com", "reason": "abuse"}, ...}
رفع مسدودی:
{"method": "DELETE", "resource": "/admin/blocklist",
 "body": {"username": "spammer@example.com"}, ...}
Field Type Description
username string Blocked username (required)
reason string Reason for blocking (optional, default: "manually blocked")
blocked_at string RFC3339 timestamp (in GET responses only)
💡 نکته: وقتی یک حساب از طریق /admin/accounts حذف می‌شود، نام کاربری به‌طور خودکار به لیست مسدودی اضافه می‌شود. برای اجازه ثبت‌نام مجدد، ابتدا باید کاربر را از لیست مسدودی حذف کنید.

مدیریت سهمیه (Quota)

/admin/quota GET PUT DELETE

مدیریت سهمیه فضای ذخیره‌سازی کاربران.

آمار کلی (بدون body):
{
    "status": 200,
    "body": {
        "total_storage_bytes": 1073741824,
        "accounts_count": 42,
        "default_quota_bytes": 107374182400
    }
}
سهمیه یک کاربر خاص:
// Request
{"method": "GET", "resource": "/admin/quota",
 "body": {"username": "alice@example.com"}, ...}

// Response
{
    "body": {
        "username": "alice@example.com",
        "used_bytes": 52428800,
        "max_bytes": 107374182400,
        "is_default": true
    }
}
تنظیم سهمیه اختصاصی:
// Set quota for one user (1GB)
{"method": "PUT", "resource": "/admin/quota",
 "body": {"username": "alice@example.com", "max_bytes": 1073741824}, ...}

// Set default quota for all users (2GB)
{"method": "PUT", "resource": "/admin/quota",
 "body": {"max_bytes": 2147483648}, ...}
بازنشانی سهمیه به پیش‌فرض:
{"method": "DELETE", "resource": "/admin/quota",
 "body": {"username": "alice@example.com"}, ...}

عملیات صف (Queue)

/admin/queue POST

پاکسازی پیام‌های ذخیره‌شده. فقط متد POST پشتیبانی می‌شود.

Action Description
purge_user Delete all messages for a specific user (requires username)
purge_all Delete ALL stored messages for all users
purge_read Delete only read (Seen) messages for all users
// Purge messages for one user
{"method": "POST", "resource": "/admin/queue",
 "body": {"action": "purge_user", "username": "alice@example.com"}, ...}

// Purge read messages for all users
{"method": "POST", "resource": "/admin/queue",
 "body": {"action": "purge_read"}, ...}

// Purge ALL messages
{"method": "POST", "resource": "/admin/queue",
 "body": {"action": "purge_all"}, ...}
⚠️ هشدار: عملیات purge_all تمام ایمیل‌های تمام کاربران در سرور را حذف می‌کند. حساب‌های کاربری حفظ می‌شوند.

اشتراک‌گذاری مخاطبین

/admin/shares GET POST PUT DELETE

مدیریت لینک‌های اشتراک‌گذاری مخاطبین (Contact Shares). فقط در صورت فعال بودن اشتراک‌گذاری در تنظیمات.

لیست:
// GET Response
{
    "body": {
        "total": 2,
        "shares": [
            {"slug": "support", "url": "openpgp4fpr:ABCD...", "name": "Support Team"},
            {"slug": "admin", "url": "openpgp4fpr:EFGH...", "name": "Admin"}
        ]
    }
}
ایجاد:
{"method": "POST", "resource": "/admin/shares",
 "body": {
    "slug": "support",
    "url": "openpgp4fpr:ABCDEF123456...",
    "name": "Support Team"
 }, ...}
ویرایش:
{"method": "PUT", "resource": "/admin/shares",
 "body": {
    "slug": "support",
    "name": "New Name"
 }, ...}
حذف:
{"method": "DELETE", "resource": "/admin/shares",
 "body": {"slug": "support"}, ...}

بازنویسی DNS

/admin/dns GET POST DELETE

مدیریت قوانین بازنویسی DNS داخلی. این قوانین مسیر ارسال ایمیل‌ها را تغییر می‌دهند.

لیست تمام قوانین:
// GET Response
{
    "body": {
        "total": 1,
        "overrides": [
            {
                "lookup_key": "old-server.example.com",
                "target_host": "new-server.example.com",
                "comment": "migration in progress"
            }
        ]
    }
}
ایجاد یا ویرایش:
{"method": "POST", "resource": "/admin/dns",
 "body": {
    "lookup_key": "old.example.com",
    "target_host": "10.0.0.5",
    "comment": "Route to internal server"
 }, ...}
حذف:
{"method": "DELETE", "resource": "/admin/dns",
 "body": {"lookup_key": "old.example.com"}, ...}
💡 نکته: تغییرات DNS بلافاصله اعمال می‌شوند و نیازی به ریستارت سرور نیست.

رابط وب مدیریت

تمام عملیات‌های Admin API از طریق یک رابط وب داخلی در مسیر /admin/ نیز قابل دسترسی هستند. این پنل از همان توکن احراز هویت استفاده می‌کند.

صفحات مدیریتی:
صفحه امکانات
Overview آمار سرور (کاربران، آپتایم، دیسک، ذخیره‌سازی)، اتصالات فعال (IMAP، TURN، Shadowsocks)، نوار مصرف دیسک، دکمه‌های پاکسازی صف
Services کلیدهای فعال/غیرفعال برای ثبت‌نام، JIT، TURN، Iroh، Shadowsocks و لاگ
Ports مشاهده و تغییر شماره پورت‌ها و تنظیمات پیکربندی
Accounts لیست حساب‌ها با مصرف فضا، حذف حساب (با پنجره تأیید)
Blocked مشاهده کاربران مسدود شده، رفع مسدودیت (با پنجره تأیید)
DNS مشاهده، افزودن، جستجو و حذف بازنویسی‌های DNS (با پنجره تأیید)
💡 نکته: پنل مدیریت از حالت روشن و تاریک پشتیبانی می‌کند (با دکمه تغییر تم در هدر) و به زبان‌های فارسی، انگلیسی، اسپانیایی و روسی موجود است.

کدهای خطا

کدهای وضعیت داخلی (در فیلد status پاسخ JSON):

Code Meaning When
200 Success Operation completed successfully
201 Created New resource created (DNS override, share)
400 Bad Request Missing required fields or invalid action
401 Unauthorized Missing, wrong, or rate-limited token
404 Not Found Unknown resource path or entry not found
405 Method Not Allowed Using wrong method for this resource
500 Internal Error Server-side failure (database, filesystem)
503 Unavailable Feature not enabled (e.g. sharing, DNS)