Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Directadmin Laravel Package

solitweb/laravel-directadmin

Laravel integration for the DirectAdmin control panel API. Manage hosting accounts, domains, email, databases and more from your Laravel app with a clean, fluent interface. Includes config publishing, easy credentials setup and support for common DirectAdmin API commands.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require solitweb/laravel-directadmin

On Laravel 5.5+ the service provider and DirectAdmin facade are auto-discovered. On older 5.x apps, register manually:

// config/app.php
'providers' => [
    Solitweb\DirectAdmin\DirectAdminServiceProvider::class,
],
'aliases' => [
    'DirectAdmin' => Solitweb\DirectAdmin\Facades\DirectAdmin::class,
],

Publish the config file — this is the first thing you should read before writing any code:

php artisan vendor:publish --provider="Solitweb\DirectAdmin\DirectAdminServiceProvider"

You'll end up with config/directadmin.php (host, port, username, password/login key, HTTPS toggle). Populate it from .env:

DIRECTADMIN_HOST=panel.example.com
DIRECTADMIN_PORT=2222
DIRECTADMIN_USERNAME=reseller_account
DIRECTADMIN_PASSWORD=secret
DIRECTADMIN_HTTPS=true

Where to look first

  1. config/directadmin.php — the auth pair here is the single most common source of "it just returns an error."
  2. The DirectAdmin class behind the facade — this is a thin wrapper, so the method list is your API surface. Skim its methods before designing anything.
  3. The DirectAdmin API docs (/CMD_API_*) — the package mirrors those endpoints, so when a method feels "missing," it's usually just not wrapped and you'll fall back to the generic request method.

First use case: provision a hosting account

The canonical use case is a signup flow: a customer pays, and you create their DirectAdmin user + domain + mailbox in one go.

use Solitweb\DirectAdmin\Facades\DirectAdmin;

$result = DirectAdmin::createUser([
    'username' => 'acme',
    'email'    => '[email protected]',
    'password' => $plainPassword,
    'domain'   => 'acme.test',
    'package'  => 'default',
]);

if (! $result) {
    // throw, roll back the order, notify ops
}

Prove this works against a staging panel with a throwaway account before wiring it into your order pipeline — the response handling is the part you want to learn early.

Implementation Patterns

Wrap the facade, don't sprinkle it

The facade is convenient but exposes DirectAdmin's raw semantics (URL-encoded strings, error=0/1, numeric result codes). Put it behind your own service so the rest of the app speaks your domain language:

class HostingProvisioner
{
    public function provision(Order $order): HostingAccount
    {
        $response = DirectAdmin::createUser([...]);

        if (! $this->succeeded($response)) {
            throw new ProvisioningFailed($this->message($response));
        }

        return HostingAccount::create([...]);
    }

    private function succeeded($response): bool
    {
        // DirectAdmin returns error=0 on success for most endpoints
        return (string) ($response['error'] ?? '1') === '0';
    }
}

Wrap this in an interface (HostingProvider) so tests and future migrations to cPanel/Plesk don't ripple through controllers.

Always provision inside a queued job

DirectAdmin calls are synchronous HTTP requests to a remote panel — they can take seconds and they fail. Never call them in an HTTP request that a user is staring at.

class ProvisionHostingAccount implements ShouldQueue
{
    public $tries = 3;
    public $backoff = [30, 120, 300];

    public function handle(HostingProvisioner $provisioner)
    {
        $provisioner->provision($this->order);
    }

    public function failed(Throwable $e)
    {
        // mark order as needs_attention, ping ops
    }
}

This gives you retries, backoff, and a failed() hook for the "customer paid but no account" scenario, which is the one that actually hurts.

Compensating rollback

DirectAdmin has no transactions. If user creation succeeds and mailbox creation fails, you must undo manually. Record each successful step and reverse them in catch:

$steps = [];
try {
    DirectAdmin::createUser($userData);   $steps[] = 'user';
    DirectAdmin::createDatabase($dbData); $steps[] = 'db';
} catch (Throwable $e) {
    foreach (array_reverse($steps) as $step) {
        match ($step) {
            'user' => DirectAdmin::deleteUser($username),
            'db'   => DirectAdmin::deleteDatabase($dbName),
        };
    }
    throw $e;
}

Per-request credentials for multi-server setups

If you host across several DirectAdmin boxes, the single global config is a limitation. Either build a small factory that constructs the underlying client with per-server credentials (the wrapper class is instantiable directly), or keep one panel for now and note the constraint. Don't fight the config file — instantiate the class yourself where you need a second panel.

Idempotency

createUser on an existing username typically fails. Before provisioning, call the "list/show user" endpoint, or treat the "already exists" error as success. This makes your job retries safe.

Testing

There's no built-in fake, so bind a stub in the container:

$this->mock(HostingProvisioner::class, function ($mock) {
    $mock->shouldReceive('provision')->once()->andReturn(new HostingAccount);
});

Keep a thin seam so tests never touch the panel.

Gotchas and Tips

It is not a REST client. DirectAdmin answers with URL-encoded query strings (error=0&text=...&details=...), not JSON. The wrapper parses these into arrays/objects, but the semantics leak through: success is error=0, and details is the human-readable message. Read text/details before showing anything to ops.

Error codes are numeric strings. 0 is success; 1 is generic failure; various codes signal specific conditions (auth, package missing, user exists). Log the raw response — the message is often the only clue. Build a small map of the codes you see in practice.

HTTPS + self-signed certs. Panels frequently use self-signed certs on port 2222. The wrapper will fail SSL verification. Either install a valid cert (preferred), or configure the client to skip verification for this host only. Don't globally disable TLS verification in your app.

Port 2222 must be reachable from your app server. This is the #1 ops issue after credentials. If calls work locally but hang in production, it's a firewall/security-group rule, not a code bug. DirectAdmin also supports a custom endpoint — check the config.

Use a login key, not the password. DirectAdmin supports API login keys scoped to specific commands. Create a key with only the CMD_API_* commands you need and use it for DIRECTADMIN_PASSWORD. Rotating a key is painless; rotating the reseller password is not.

Config caching bites. If you change panel credentials in .env, run php artisan config:clear. A stale bootstrap/cache/config.php will silently keep the old host, which looks like "the package is broken."

Special characters in credentials. Passwords with &, +, or =, or spaces, break form-encoded auth. Either quote them in .env (DIRECTADMIN_PASSWORD="p&ss") or just use a login key with an alphanumeric token.

No rate limiting, no built-in queue. Bulk operations (migrating 500 accounts) will slam the panel. Throttle in your job ($this->release(2) or RateLimited middleware) and set a sane retry_after.

It's a thin wrapper, not a full SDK. Methods map roughly to CMD_API_* endpoints; missing features are best handled by constructing a raw request against the panel rather than forking the package. If you find yourself needing five unwrapped endpoints, subclass the DirectAdmin class and bind your subclass in a service provider — that's the cleanest extension point.

Laravel 5 in the name. Check the package's compatibility before relying on it in a Laravel 9/10/11 app. If it targets illuminate/support ^5.x, you'll either need --ignore-platform-reqs (risky) or to treat the package as a reference implementation and copy the ~200-line client into your own codebase. Given the small size, the latter is often the pragmatic move.

Don't leak panel errors to end users. DirectAdmin's details can contain server paths and internal usernames. Map to a friendly message ("We couldn't set up your hosting; our team has been notified") and log the raw response with the order ID attached.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route