Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions inc/integrations/host-providers/class-base-host-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,17 @@ public function init(): void {
// avoid text domain loaded at the wrong time.
add_action('init', [$this, 'alert_provider_not_setup']);
}
}

/*
* Load the dependencies.
*/
$this->load_dependencies();
} else {
/*
* Load the dependencies.
*/
$this->load_dependencies();

/*
* Initialize the hooks.
*/
$this->register_hooks();
/*
* Initialize the hooks.
*/
$this->register_hooks();
}
}

add_filter('wu_domain_manager_get_integrations', [$this, 'self_register']);
Expand Down
18 changes: 8 additions & 10 deletions inc/integrations/host-providers/class-closte-host-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public function on_add_domain($domain, $site_id): void {
if (wu_get_isset($domain_response, 'success', false)) {
wu_log_add('integration-closte', sprintf('Domain %s added successfully, requesting SSL certificate', $domain));
$this->request_ssl_certificate($domain);
} elseif (isset($domain_response['error']) && $domain_response['error'] === 'Invalid or empty domain: ' . $domain) {
} elseif (isset($domain_response['error']) && 'Invalid or empty domain: ' . $domain === $domain_response['error']) {
wu_log_add('integration-closte', sprintf('Domain %s rejected by Closte API as invalid. This may be expected for Closte subdomains or internal domains.', $domain));
} else {
wu_log_add('integration-closte', sprintf('Failed to add domain %s. Response: %s', $domain, wp_json_encode($domain_response)));
Expand Down Expand Up @@ -180,8 +180,6 @@ private function request_ssl_certificate($domain) {
'/certificate/install',
];

$ssl_response = null;

foreach ($ssl_endpoints as $endpoint) {
wu_log_add('integration-closte', sprintf('Trying SSL endpoint: %s', $endpoint));

Expand Down Expand Up @@ -217,7 +215,7 @@ private function request_ssl_certificate($domain) {
*
* @since 2.0.0
* @param string $domain The domain to check SSL status for.
* @return array|object
* @return array
*/
public function check_ssl_status($domain) {

Expand Down Expand Up @@ -264,21 +262,21 @@ public function test_connection(): void {
* @since 1.7.3
* @param string $endpoint Endpoint to send the call to.
* @param array $data Array containing the params to the call.
* @return object
* @return array
*/
public function send_closte_api_request($endpoint, $data) {

if (defined('CLOSTE_CLIENT_API_KEY') === false) {
wu_log_add('integration-closte', 'CLOSTE_CLIENT_API_KEY constant not defined');
return (object) [
return [
'success' => false,
'error' => 'Closte API Key not found.',
];
}

if (empty(CLOSTE_CLIENT_API_KEY)) {
wu_log_add('integration-closte', 'CLOSTE_CLIENT_API_KEY is empty');
return (object) [
return [
'success' => false,
'error' => 'Closte API Key is empty.',
];
Expand Down Expand Up @@ -324,7 +322,7 @@ public function send_closte_api_request($endpoint, $data) {
// Check for HTTP errors
if ($response_code >= 400) {
wu_log_add('integration-closte', sprintf('HTTP error %d for endpoint %s', $response_code, $endpoint));
return (object) [
return [
'success' => false,
'error' => sprintf('HTTP %d error', $response_code),
'response_body' => $response_body,
Expand All @@ -346,15 +344,15 @@ public function send_closte_api_request($endpoint, $data) {
}

wu_log_add('integration-closte', sprintf('JSON decode error: %s', json_last_error_msg()));
return (object) [
return [
'success' => false,
'error' => 'Invalid JSON response',
'json_error' => json_last_error_msg(),
];
}

wu_log_add('integration-closte', 'Empty response body');
return (object) [
return [
'success' => false,
'error' => 'Empty response',
];
Expand Down
27 changes: 13 additions & 14 deletions inc/integrations/host-providers/class-enhance-host-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public function get_fields() {
return [
'WU_ENHANCE_API_TOKEN' => [
'type' => 'password',
'html_attr' => ['autocomplete' => 'new-password'],
'title' => __('Enhance API Token', 'ultimate-multisite'),
'placeholder' => __('Your bearer token', 'ultimate-multisite'),
],
Expand Down Expand Up @@ -137,13 +138,11 @@ public function on_add_domain($domain, $site_id): void {
$domain_response = $this->send_enhance_api_request(
'/servers/' . $server_id . '/domains',
'POST',
[
'domain' => $domain,
]
$domain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n inc/integrations/host-providers/class-enhance-host-provider.php | sed -n '130,155p'

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1278


🌐 Web query:

Enhance Control Panel API documentation adding domain to server

💡 Result:

Which control panel (cPanel, Plesk, Cloud provider, or a custom Control Panel) and which doc format do you want (OpenAPI YAML/JSON, Markdown, or HTML)? Also confirm the required fields for the domain (e.g., domain name, server_id, DNS zone, SSL cert, aliases) and auth method (API key, OAuth, etc.).

Meanwhile — a concise OpenAPI-style endpoint template and minimal doc checklist you can drop into docs:

OpenAPI endpoint template (YAML — adapt names/types to your schema)
paths:
/servers/{server_id}/domains:
post:
summary: Add a domain to a server
tags: [Domains]
security:
- ApiKeyAuth: []
parameters:
- name: server_id
in: path
required: true
schema:
type: string
description: Server identifier
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [domain]
properties:
domain:
type: string
format: hostname
example: example.com
zone_template:
type: string
description: Optional DNS zone/template name
enable_ssl:
type: boolean
default: true
aliases:
type: array
items:
type: string
examples:
addDomain:
value: { "domain": "example.com", "enable_ssl": true }
responses:
'201':
description: Domain added
content:
application/json:
schema:
$ref: '#/components/schemas/Domain'
'400':
description: Invalid request (validation error)
'401':
description: Unauthorized
'404':
description: Server not found
'409':
description: Domain already exists on server
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
schemas:
Domain:
type: object
properties:
id:
type: string
domain:
type: string
server_id:
type: string
dns_status:
type: string
ssl_status:
type: string
created_at:
type: string
format: date-time

Minimal documentation checklist (recommended)

  • Purpose/behavior summary (what “adding a domain” does: create DNS records, bind vhost, enable SSL, etc.). [1][2]
  • URL, HTTP method, path parameters, and required headers/auth. [2]
  • Request body schema, validation rules (hostname format, max length), and examples. [2]
  • Success and error responses with status codes and example payloads (201, 400, 401, 404, 409, 500). [2]
  • Side effects & asynchronous behaviour (is SSL provisioning async? provide status endpoint). [4]
  • Security notes (who can add domains; rate limits). [1][3]
  • CLI / curl and SDK examples (one-liner curl, JS/Python snippets). [3]
  • OpenAPI validation / linting step and where the spec is stored (CI). [1]

If you confirm control panel and desired format, I’ll produce a finalized OpenAPI YAML or Markdown doc section (including curl and SDK examples) tailored to your API, plus sample error payloads and a small CI lint workflow.

Sources: OpenAPI best practices and API design guidance used above. [1] OpenAPI Best Practices. [2] API Design / REST best-practices. [3] Internal doc-generation & examples. (learn.openapis.org)

Citations:


🏁 Script executed:

grep -n "function send_enhance_api_request" inc/integrations/host-providers/class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 168


🏁 Script executed:

grep -n "send_enhance_api_request" inc/integrations/host-providers/class-enhance-host-provider.php | head -20

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 390


🏁 Script executed:

ast-grep --pattern $'function send_enhance_api_request($_) {
  $$$
}'

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 63


🏁 Script executed:

cat -n inc/integrations/host-providers/class-enhance-host-provider.php | sed -n '277,320p'

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1768


🏁 Script executed:

# Search for any comments or documentation about the Enhance API contract
rg -A 5 -B 5 "domainid|domain_id" inc/integrations/host-providers/class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1516


🏁 Script executed:

# Check git log for recent changes to understand what was changed
git log --oneline -20 -- inc/integrations/host-providers/class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 113


🏁 Script executed:

git diff 0e6b983^..0e6b983 -- inc/integrations/host-providers/class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 122


🏁 Script executed:

# Also search for any Enhance API documentation or references in the codebase
fd -e md -e txt -e json | xargs grep -l "Enhance\|enhance" 2>/dev/null | head -10

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 167


🏁 Script executed:

grep -A 10 -B 2 -i "enhance.*api\|domain.*add" README.md readme.txt 2>/dev/null | head -50

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 63


🏁 Script executed:

# Search for any comments or docs about the API payload structure
rg -i "domain.*payload|payload.*domain|api.*contract" inc/integrations/

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 63


🏁 Script executed:

# Check if there's a git show for the specific commit to see what changed
git show --stat 0e6b983

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 50393


🏁 Script executed:

cat .wiki/Enhance-Integration.md | head -100

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 3860


🏁 Script executed:

# Look for specific sections about domain addition
rg -A 20 "add.*domain|POST.*domain" .wiki/Enhance-Integration.md

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 2641


🏁 Script executed:

# Check what the exact payload format should be according to docs
grep -A 5 "POST /servers" .wiki/Enhance-Integration.md

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 178


🏁 Script executed:

# Verify the exact behavior - when a plain string is json_encoded
python3 << 'EOF'
import json
# Current code behavior
domain = "example.com"
payload = json.dumps(domain)
print(f"Current payload (plain string): {payload}")
print(f"Type: {type(domain)}")

# What it should be
correct_payload = json.dumps({"domain": domain})
print(f"Expected payload (object): {correct_payload}")
EOF

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 167


Fix the API request payload format — domain must be wrapped in an object.

The Enhance API documentation specifies the POST body should be {"domain": "example.com"}, but the current code passes $domain as a plain string, which JSON-encodes to just "example.com". This will cause the API call to fail. Wrap the domain in an object: ['domain' => $domain] before passing to send_enhance_api_request().

The response field domainid appears correct based on the integration patterns, though the API documentation does not explicitly document the POST response schema.

🤖 Prompt for AI Agents
In inc/integrations/host-providers/class-enhance-host-provider.php around line
141, the POST payload currently passes $domain as a plain string which
JSON-encodes to "\"example.com\"" and breaks the API; change the payload to an
associative array wrapping the domain (e.g. ['domain' => $domain]) before
calling send_enhance_api_request(), then verify the call still reads the
response.domainid as before.

);

// Check if domain was added successfully
if (wu_get_isset($domain_response, 'id')) {
if (wu_get_isset($domain_response, 'domainid')) {
wu_log_add('integration-enhance', sprintf('Domain %s added successfully. SSL will be automatically provisioned via LetsEncrypt when DNS resolves.', $domain));
} elseif (isset($domain_response['error'])) {
wu_log_add('integration-enhance', sprintf('Failed to add domain %s. Error: %s', $domain, wp_json_encode($domain_response)));
Expand Down Expand Up @@ -179,10 +178,10 @@ public function on_remove_domain($domain, $site_id): void {

$domain_id = null;

if (isset($domains_list['items']) && is_array($domains_list['items'])) {
foreach ($domains_list['items'] as $item) {
if (isset($item['domain']) && $item['domain'] === $domain) {
$domain_id = $item['id'];
if (isset($domains_list['domains']) && is_array($domains_list['domains'])) {
foreach ($domains_list['domains'] as $item) {
if (isset($item['domainName']) && $item['domainName'] === $domain) {
$domain_id = $item['domainId'];
break;
}
}
Expand Down Expand Up @@ -250,8 +249,7 @@ public function test_connection(): void {

// Test by attempting to list domains
$response = $this->send_enhance_api_request(
'/servers/' . $server_id . '/domains',
'GET'
'/servers/' . $server_id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cd inc/integrations/host-providers && wc -l class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 116


🏁 Script executed:

cd inc/integrations/host-providers && sed -n '240,270p' class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1009


🏁 Script executed:

cd inc/integrations/host-providers && sed -n '1,50p' class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1179


🏁 Script executed:

git -C . log --oneline --all -- inc/integrations/host-providers/class-enhance-host-provider.php | head -20

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 151


🏁 Script executed:

git -C . show HEAD:inc/integrations/host-providers/class-enhance-host-provider.php | sed -n '240,270p'

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1009


🏁 Script executed:

git -C . log -p --all -- inc/integrations/host-providers/class-enhance-host-provider.php | grep -A 5 -B 5 "servers.*domains\|servers.*{id}" | head -100

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1784


🏁 Script executed:

git -C . show 0e6b983 --stat

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 50393


🏁 Script executed:

git -C . show 0e6b983 -- inc/integrations/host-providers/class-enhance-host-provider.php | grep -A 20 "test_connection"

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 690


🏁 Script executed:

rg "servers.*domains" inc/integrations/host-providers/class-enhance-host-provider.php -A 2 -B 2

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 564


🏁 Script executed:

sed -n '1,100p' inc/integrations/host-providers/class-enhance-host-provider.php | head -50

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 1179


🏁 Script executed:

sed -n '300,423p' inc/integrations/host-providers/class-enhance-host-provider.php

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 3585


🏁 Script executed:

git -C . show 0e6b983:inc/integrations/host-providers/class-enhance-host-provider.php | sed -n '240,260p'

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 669


🏁 Script executed:

git -C . show 1ddcfc1:inc/integrations/host-providers/class-enhance-host-provider.php 2>/dev/null | sed -n '240,260p'

Repository: Multisite-Ultimate/ultimate-multisite

Length of output: 685


Update inline comment and simplify success condition.

The endpoint change from /servers/{id}/domains to /servers/{id} is confirmed—the function now tests API connectivity by fetching server info rather than listing domains. However, the inline comment saying "Test by attempting to list domains" is stale and should reflect that it now retrieves server information. Additionally, the success condition's check for isset($response['items']) is dead code; the new endpoint returns a server object with an id field, not an array with items. Consider simplifying the condition to just isset($response['id']) or, if backward compatibility is desired, add a clarifying comment explaining why both checks remain.

🤖 Prompt for AI Agents
In inc/integrations/host-providers/class-enhance-host-provider.php around line
252, update the stale inline comment and simplify the success condition: replace
the comment that says "Test by attempting to list domains" with one that says
the function fetches server information from /servers/{id}, and change the
response check from isset($response['items']) to isset($response['id']) (or, if
you need backward compatibility, add a brief comment explaining you accept
either a server object with id or a legacy items array and use
isset($response['id']) || isset($response['items'])).

);

if (isset($response['items']) || isset($response['id'])) {
Expand All @@ -261,7 +259,8 @@ public function test_connection(): void {
]
);
} else {
$error = new \WP_Error('connection-failed', __('Failed to connect to Enhance API', 'ultimate-multisite'));
// Translators: %s the full error message.
$error = new \WP_Error('connection-failed', sprintf(__('Failed to connect to Enhance API: %s', 'ultimate-multisite'), $response['error'] ?? 'Unknown error'));
wp_send_json_error($error);
}
}
Expand All @@ -270,9 +269,9 @@ public function test_connection(): void {
* Sends a request to the Enhance API with the configured bearer token.
*
* @since 2.0.0
* @param string $endpoint API endpoint (relative to base URL).
* @param string $method HTTP method (GET, POST, DELETE, etc.).
* @param array $data Request body data (for POST/PUT/PATCH).
* @param string $endpoint API endpoint (relative to base URL).
* @param string $method HTTP method (GET, POST, DELETE, etc.).
* @param array|string $data Request body data (for POST/PUT/PATCH).
* @return array|object
*/
public function send_enhance_api_request($endpoint, $method = 'GET', $data = []) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ class Gridpane_Host_Provider extends Base_Host_Provider {
*/
protected $constants = [
'WU_GRIDPANE',
'WU_GRIDPANE_API_KEY',
'WU_GRIDPANE_APP_ID',
'WU_GRIDPANE_SERVER_ID',
];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ public function load_dependencies(): void {
* @return void
*/
public function on_add_domain($domain, $site_id): void {

if (! class_exists('WPE_API')) {
return;
}
$api = new \WPE_API();

$api->set_arg('method', 'domain');
Expand All @@ -124,6 +126,9 @@ public function on_add_domain($domain, $site_id): void {
* @return void
*/
public function on_remove_domain($domain, $site_id): void {
if (! class_exists('WPE_API')) {
return;
}

$api = new \WPE_API();

Expand Down Expand Up @@ -197,6 +202,9 @@ public function get_logo() {
* @return void
*/
public function test_connection(): void {
if (! class_exists('WPE_API')) {
wp_send_json_error(__('Class WPE_API is not installed.', 'ultimate-multisite'));
}

$api = new \WPE_API();

Expand Down
Loading