Skip to content

MySQL / MariaDB

Server-level MySQL/MariaDB management: users, databases, replication, tuner, remote access.

All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.

Endpoint count: 36

Endpoints

Method Path Handler Guards
POST /api/mysql/install installMySQL RequirePermission(db, "administrative_operations")
GET /api/mysql/monitor-stats getMySQLMonitorStats RequirePermission(db, "administrative_operations")
GET /api/mysql/phpmyadmin getPHPMyAdminStatus RequirePermission(db, "administrative_operations")
POST /api/mysql/phpmyadmin/install installPHPMyAdmin RequirePermission(db, "administrative_operations")
POST /api/mysql/phpmyadmin/login pmaAutoLogin RequirePermission(db, "administrative_operations")
POST /api/mysql/phpmyadmin/update updatePHPMyAdmin RequirePermission(db, "administrative_operations")
GET /api/mysql/processes getMySQLProcesses RequirePermission(db, "administrative_operations")
POST /api/mysql/processes/:id/kill killMySQLProcess RequirePermission(db, "administrative_operations")
POST /api/mysql/processes/kill-all killAllMySQLProcesses RequirePermission(db, "administrative_operations")
DELETE /api/mysql/replication removeReplication RequirePermission(db, "administrative_operations")
GET /api/mysql/replication getReplicationStatus RequirePermission(db, "administrative_operations")
POST /api/mysql/replication/accept acceptReplication -
POST /api/mysql/replication/connect connectReplication RequirePermission(db, "administrative_operations")
POST /api/mysql/replication/databases listDatabasesForReplication -
POST /api/mysql/replication/db-sizes getDatabaseSizes -
POST /api/mysql/replication/dump dumpDatabases -
POST /api/mysql/replication/dump-db dumpSingleDatabase -
POST /api/mysql/replication/dump-users dumpUsers -
POST /api/mysql/replication/init initReplication RequirePermission(db, "administrative_operations")
POST /api/mysql/replication/position getMasterPosition -
POST /api/mysql/replication/setup-master setupMasterEndpoint -
GET /api/mysql/replication/slaves getReplicationSlaves RequirePermission(db, "administrative_operations")
POST /api/mysql/replication/slaves addReplicationSlave RequirePermission(db, "administrative_operations")
DELETE /api/mysql/replication/slaves/:id removeReplicationSlave RequirePermission(db, "administrative_operations")
POST /api/mysql/replication/start startReplication RequirePermission(db, "administrative_operations")
POST /api/mysql/replication/start-slave startSlaveReplication -
POST /api/mysql/replication/sync initialSyncReplication RequirePermission(db, "administrative_operations")
GET /api/mysql/replication/sync-stream syncStreamSSE RequirePermission(db, "administrative_operations")
POST /api/mysql/restart mySQLRestart RequirePermission(db, "administrative_operations")
GET /api/mysql/slow-log getMySQLSlowLog RequirePermission(db, "administrative_operations")
GET /api/mysql/status getMySQLStatus RequirePermission(db, "administrative_operations")
POST /api/mysql/stop mySQLStop RequirePermission(db, "administrative_operations")
POST /api/mysql/sync-users-ha syncUsersForHA RequirePermission(db, "administrative_operations")
GET /api/mysql/tuner getMySQLTuner RequirePermission(db, "administrative_operations")
POST /api/mysql/tuner/apply applyMySQLTuning RequirePermission(db, "administrative_operations")
POST /api/mysql/tuner/install installMySQLTuner RequirePermission(db, "administrative_operations")

Source

Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:

{ "data": { ... } }

On error:

{ "error": { "code": "string", "message": "string" } }

Reference

POST /api/mysql/install

Installs MariaDB (default) or MySQL via the system package manager, enables and starts the service, then generates a random root password and writes a credentials file.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "type": "string (optional)" }
Field Type Required Description
type string no "mariadb" (default) or "mysql". On bind error, defaults to "mariadb".

Response - 200 OK

{ "success": true, "message": "mariadb installed and started", "type": "mariadb" }

Errors

Status Code Meaning
500 install_failed Package install command failed; message contains the combined command output.

Side effects

  • Detects OS via /etc/redhat-release; runs dnf install (RHEL) or apt-get update && apt-get install (Debian) for the server/client packages, then systemctl enable --now.
  • Runs a CREATE USER/ALTER USER/GRANT ALL/FLUSH PRIVILEGES SQL block setting a random 24-char root password.
  • Creates /etc/starkone (0755) and writes /etc/starkone/.mysql.conf (0600) with [client] credentials.

Example

curl -X POST https://panel.example.com:2087/api/mysql/install \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "mariadb"}'

GET /api/mysql/monitor-stats

Returns live MySQL performance metrics (query rate, connections, buffer hit rate, lock ratio, InnoDB stats, per-database sizes) gathered from information_schema.GLOBAL_STATUS and related queries.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": {
    "stats": {
      "queries_per_sec": 12,
      "connections": 4,
      "threads_running": 1,
      "slow_queries": 0,
      "aborted_connects": 0,
      "buffer_hit_rate": 99
    },
    "locks": {
      "table_locks_immediate": 1000,
      "table_locks_waited": 2,
      "lock_ratio": 0
    },
    "innodb": {
      "buffer_pool_size": "128 MB",
      "buffer_pool_used_pct": 60,
      "row_reads_per_sec": 50,
      "row_writes_per_sec": 5,
      "deadlocks": 0
    },
    "databases": [
      { "name": "appdb", "size": "12 MB", "size_bytes": 12582912 }
    ]
  }
}

Side effects

  • Runs read-only mysql queries against information_schema using the stored root password or /etc/starkone/.mysql.conf.

Example

curl -X GET https://panel.example.com:2087/api/mysql/monitor-stats \
  -H "Authorization: Bearer $TOKEN"

GET /api/mysql/phpmyadmin

Reports whether phpMyAdmin is installed, its version, install path, and the randomized access URL (generating an alias on first call if none exists).

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": {
    "installed": true,
    "version": "5.2.1",
    "path": "/usr/share/phpmyadmin",
    "url": "https://panel.example.com/abcdef12345678/pma/"
  }
}

Side effects

  • Reads VERSION/README under /usr/share/phpmyadmin.
  • If installed but no alias file exists, generates a 14-char alias, writes /usr/share/phpmyadmin/.pma_alias (0600), and rewrites the phpMyAdmin nginx config.

Example

curl -X GET https://panel.example.com:2087/api/mysql/phpmyadmin \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/phpmyadmin/install

Downloads and extracts the latest phpMyAdmin tarball, writes its config, configures the webserver and sign-on session handler, and installs a monthly auto-update cron.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "success": true, "message": "phpMyAdmin installed" }

Errors

Status Code Meaning
500 download_error curl download of the phpMyAdmin tarball failed.
500 extract_error tar extraction of the tarball failed.

Side effects

  • Downloads to /tmp/phpmyadmin.tar.gz via curl, removes/recreates /usr/share/phpmyadmin, extracts with tar.
  • Writes config.inc.php with a generated 32-char blowfish secret; creates tmp/ dir; removes the tarball.
  • Configures nginx/apache (random alias), sets up the sign-on handler, disables PrivateTmp for PHP-FPM (writes systemd overrides, daemon-reload, restarts PHP-FPM).
  • Writes /etc/cron.monthly/phpmyadmin-update (0755) calling starkone-cli update_phpmyadmin.

Example

curl -X POST https://panel.example.com:2087/api/mysql/phpmyadmin/install \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/phpmyadmin/login

Generates a one-time sign-on token for a stored database user and returns a phpMyAdmin auto-login URL.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "db_user": "string (required)" }
Field Type Required Description
db_user string yes The database username to auto-login as.

Response - 200 OK

{ "data": { "url": "https://panel.example.com/abcdef12345678/pma/signon.php?token=..." } }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
400 not_installed phpMyAdmin is not installed.
400 no_password No stored password for the user; reset it first.
500 no_alias phpMyAdmin alias not configured.
500 token_error Failed to write the one-time login token file.

Side effects

  • Looks up the user's stored password from the panel DB.
  • Writes a one-time token JSON file under /tmp/pma_tokens/ (consumed and deleted by signon.php on use).

Example

curl -X POST https://panel.example.com:2087/api/mysql/phpmyadmin/login \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"db_user": "user1"}'

POST /api/mysql/phpmyadmin/update

Downloads the latest phpMyAdmin, backs up and restores the existing config.inc.php, and reports the old and new versions.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "success": true, "old_version": "5.2.0", "new_version": "5.2.1" }

Errors

Status Code Meaning
500 download_error curl download of the tarball failed.
500 extract_error tar extraction failed.

Side effects

  • Downloads to /tmp/phpmyadmin.tar.gz, removes/recreates /usr/share/phpmyadmin, extracts with tar.
  • Backs up and restores config.inc.php, recreates signon.php and tmp/, removes the tarball.

Example

curl -X POST https://panel.example.com:2087/api/mysql/phpmyadmin/update \
  -H "Authorization: Bearer $TOKEN"

GET /api/mysql/processes

Returns the current MySQL process list from information_schema.processlist, ordered by time descending.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": {
    "processes": [
      {
        "id": 42,
        "user": "user1",
        "host": "localhost",
        "db": "appdb",
        "command": "Query",
        "time": 3,
        "state": "Sending data",
        "info": "SELECT ..."
      }
    ]
  }
}

Errors

Status Code Meaning
500 mysql_error The mysql query for the process list failed.

Side effects

  • Read-only mysql query using the stored root password or /etc/starkone/.mysql.conf.

Example

curl -X GET https://panel.example.com:2087/api/mysql/processes \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/processes/:id/kill

Kills a single MySQL connection by issuing KILL <id>.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id string The MySQL process/connection id to kill (passed verbatim to KILL).

Response - 200 OK

{ "data": { "killed": true } }

Errors

Status Code Meaning
500 kill_failed The KILL command failed.

Side effects

  • Executes mysql -e "KILL <id>" against the running server.

Example

curl -X POST https://panel.example.com:2087/api/mysql/processes/42/kill \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/processes/kill-all

Kills all active (non-Sleep, non-system) MySQL connections and returns the count killed.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "data": { "killed": 5 } }

Side effects

  • Selects process ids where command != 'Sleep' and the user is not system user/event_scheduler, then issues KILL <id> for each.

Example

curl -X POST https://panel.example.com:2087/api/mysql/processes/kill-all \
  -H "Authorization: Bearer $TOKEN"

DELETE /api/mysql/replication

Tears down replication: stops the slave thread, resets slave state, and deletes the local replication config row.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "success": true, "message": "Replication removed" }

Side effects

  • Runs STOP SLAVE and RESET SLAVE ALL via mysql.
  • Deletes the mysql_replication row (id = 1) from the panel DB.

Example

curl -X DELETE https://panel.example.com:2087/api/mysql/replication \
  -H "Authorization: Bearer $TOKEN"

GET /api/mysql/replication

Returns the configured replication mode/role plus live status, including slave details (IO/SQL running, lag, relay log) or master details (binlog position, connected slaves).

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": {
    "enabled": true,
    "mode": "master-slave",
    "role": "slave",
    "remote_host": "https://203.0.113.10:2087",
    "remote_status": "connected",
    "lag_seconds": 0,
    "token": "...",
    "ha_proxy_ip": "",
    "replication_status": "syncing",
    "slave_details": {
      "io_running": true,
      "sql_running": true,
      "master_host": "203.0.113.10",
      "io_state": "Waiting for master to send event",
      "relay_log_file": "mysql-relay.000001",
      "relay_log_pos": "456",
      "exec_master_log_pos": "456"
    }
  }
}

When not configured, data is returned with enabled: false and empty mode/role fields.

Side effects

  • Read-only: queries the panel DB row and runs mysql status queries (SHOW SLAVE STATUS, SHOW MASTER STATUS, SHOW SLAVE HOSTS); for masters it may probe the remote host over HTTPS. No writes.

Example

curl -X GET https://panel.example.com:2087/api/mysql/replication \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/replication/accept

Server-to-server endpoint (token-authenticated): the remote peer calls this to register itself, verifying the waiting token and saving the peer's connection info; returns this server's MySQL version.

Request body

{
  "token": "string (required)",
  "remote_host": "string (required)",
  "remote_token": "string (required)",
  "mode": "string (required)",
  "role": "string (required)"
}
Field Type Required Description
token string yes Must match this server's waiting remote_token.
remote_host string yes The calling peer's base URL.
remote_token string yes The peer's token, saved as peer_token.
mode string yes Replication mode (master-slave / master-master).
role string yes Role assigned to this server.

Response - 200 OK

{ "success": true, "mysql_version": "10.11.6-MariaDB" }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match the waiting token.

Side effects

  • Updates the mysql_replication row with remote_host, mode, role, and peer_token.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/accept \
  -H "Content-Type: application/json" \
  -d '{"token":"...","remote_host":"https://203.0.113.10:2087","remote_token":"...","mode":"master-slave","role":"slave"}'

POST /api/mysql/replication/connect

Initiates a connection to a remote peer: calls the peer's accept endpoint, performs a MySQL version compatibility check, records the slave, and triggers the slave to start replication.

Guards: RequirePermission(db, "administrative_operations")

Request body

{
  "remote_host": "string (required)",
  "remote_token": "string (required)",
  "mode": "string (optional)",
  "force_connect": "boolean (optional)"
}
Field Type Required Description
remote_host string yes Base URL of the remote peer.
remote_token string yes Token issued by the remote peer (its waiting token).
mode string no master-slave (default) or master-master.
force_connect boolean no Skip the MySQL version-mismatch check.

Response - 200 OK

{
  "success": true,
  "local_version": "10.11.6-MariaDB",
  "remote_version": "10.11.6-MariaDB",
  "slave_started": true,
  "message": "Slave connected"
}

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
400 remote_error The remote accept endpoint returned an error.
409 version_mismatch Local/remote MySQL major.minor versions differ; use force_connect.
502 connection_failed Could not reach the remote server.

Side effects

  • Auto-initializes local mysql_replication config if missing; updates mode.
  • Calls the remote /api/mysql/replication/accept and /api/mysql/replication/start-slave endpoints over HTTPS.
  • Inserts/updates a row in mysql_replication_slaves.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/connect \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"remote_host":"https://203.0.113.10:2087","remote_token":"...","mode":"master-slave"}'

POST /api/mysql/replication/databases

Server-to-server endpoint (token-authenticated): returns the list of user databases (excluding system schemas) for the slave to sync.

Request body

{ "token": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.

Response - 200 OK

{ "data": { "databases": ["appdb", "shopdb"] } }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.

Side effects

  • Read-only mysql query against information_schema.schemata.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/databases \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}'

POST /api/mysql/replication/db-sizes

Server-to-server endpoint (token-authenticated): returns a map of user database name to human-readable size.

Request body

{ "token": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.

Response - 200 OK

{ "data": { "sizes": { "appdb": "12.0 MB", "shopdb": "<1 MB" } } }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.

Side effects

  • Read-only mysql aggregate query against information_schema.tables.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/db-sizes \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}'

POST /api/mysql/replication/dump

Server-to-server endpoint (token-authenticated): streams a full mysqldump --all-databases --master-data=2 as an octet-stream for the slave's initial sync.

Request body

{ "token": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.

Response - 200 OK

Streamed application/octet-stream body containing the SQL dump (not JSON).

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.
500 dump_failed mysqldump could not be started.

Side effects

  • Spawns mysqldump (with safe args) and streams its stdout to the response.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/dump \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}' --output dump.sql

POST /api/mysql/replication/dump-db

Server-to-server endpoint (token-authenticated): runs mysqldump for a single named database and returns it as an octet-stream.

Request body

{ "token": "string (required)", "database": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.
database string yes Name of the database to dump.

Response - 200 OK

Returns application/octet-stream with the database's SQL dump (not JSON).

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.
500 dump_failed mysqldump failed.

Side effects

  • Runs mysqldump <database> with safe args and returns the output.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/dump-db \
  -H "Content-Type: application/json" \
  -d '{"token":"...","database":"appdb"}' --output appdb.sql

POST /api/mysql/replication/dump-users

Server-to-server endpoint (token-authenticated): returns CREATE USER ... IDENTIFIED BY PASSWORD and GRANT statements for all non-system local users as an octet-stream.

Request body

{ "token": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.

Response - 200 OK

Returns application/octet-stream containing SQL CREATE USER/GRANT statements (not JSON).

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.

Side effects

  • Read-only mysql queries against mysql.user and SHOW GRANTS; excludes root, mysql, mariadb.sys, repl_user, and empty users.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/dump-users \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}' --output users.sql

POST /api/mysql/replication/init

Initializes local replication config, generating a connection token to share with the remote peer.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "mode": "string (required)", "role": "string (required)" }
Field Type Required Description
mode string yes master-slave or master-master.
role string yes master or slave (for master-slave).

Response - 200 OK

{
  "success": true,
  "connection_token": "...",
  "message": "Replication initialized. Share this token with the remote server."
}

Errors

Status Code Meaning
400 bad_request Body failed to bind, or mode is not master-slave/master-master.

Side effects

  • Creates the mysql_replication table if missing; deletes any existing id = 1 row and inserts the new config with a generated 32-char token.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/init \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"master-slave","role":"master"}'

POST /api/mysql/replication/position

Server-to-server endpoint (token-authenticated): returns the current binlog file/position and GTID position from SHOW MASTER STATUS.

Request body

{ "token": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.

Response - 200 OK

{ "data": { "binlog_file": "mysql-bin.000003", "binlog_pos": "456", "gtid_pos": "0-1-100" } }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.
500 no_binlog Binary log not enabled (no master status).

Side effects

  • Read-only mysql queries (SHOW MASTER STATUS, SELECT @@gtid_binlog_pos).

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/position \
  -H "Content-Type: application/json" \
  -d '{"token":"..."}'

POST /api/mysql/replication/setup-master

Server-to-server endpoint (token-authenticated): called by a slave to have this server configure itself as a master for the given slave IP.

Request body

{ "token": "string (required)", "slave_ip": "string (required)" }
Field Type Required Description
token string yes Must match this server's remote_token.
slave_ip string yes IP of the slave to authorize.

Response - 200 OK

{ "data": { "...": "result of master setup (binlog config, repl user, etc.)" } }

The data payload mirrors the result returned by the internal setupMasterReplication helper.

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.
500 setup_failed Master setup failed (e.g. binary logging could not be enabled).

Side effects

  • May edit the MySQL config file to enable log_bin/server-id/GTID and bind-address = 0.0.0.0, then restart MariaDB/MySQL.
  • Adds the slave IP to the CSF firewall (csf -a) and creates the repl_user replication user.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/setup-master \
  -H "Content-Type: application/json" \
  -d '{"token":"...","slave_ip":"203.0.113.20"}'

GET /api/mysql/replication/slaves

Lists configured replication slaves with their live connection status (cross-checked against SHOW SLAVE HOSTS and the process list).

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": [
    {
      "id": 1,
      "remote_host": "https://203.0.113.20:2087",
      "status": "connected",
      "created_at": "2026-06-17 10:00:00"
    }
  ]
}

Side effects

  • Read-only: queries mysql_replication_slaves plus two mysql queries (2s timeout) to determine live connection status. No writes.

Example

curl -X GET https://panel.example.com:2087/api/mysql/replication/slaves \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/replication/slaves

Adds a new slave (in pending state) to this master's slave list.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "remote_host": "string (required)" }
Field Type Required Description
remote_host string yes Base URL of the slave server.

Response - 200 OK

{ "data": { "success": true } }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
400 not_master This server is not configured as a master.
500 db_error Failed to insert the slave row.

Side effects

  • Inserts a row into mysql_replication_slaves with status pending.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/slaves \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"remote_host":"https://203.0.113.20:2087"}'

DELETE /api/mysql/replication/slaves/:id

Removes a slave from this master's slave list.

Guards: RequirePermission(db, "administrative_operations")

Path parameters

Name Type Description
id string The mysql_replication_slaves row id to delete.

Response - 200 OK

{ "data": { "success": true } }

Side effects

  • Deletes the matching row from mysql_replication_slaves.

Example

curl -X DELETE https://panel.example.com:2087/api/mysql/replication/slaves/1 \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/replication/start

Starts replication based on the stored config: configures this server as master (binlog + repl user) or as slave (points at the master) depending on its role.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "data": { "...": "result of master or slave setup" } }

Errors

Status Code Meaning
400 not_configured No replication config row exists.
400 not_connected Config has no remote_host.
500 setup_failed Master or slave setup failed.

Side effects

  • For master role: may edit MySQL config (binlog/GTID/bind-address), restart the service, add the remote IP to CSF, and create repl_user.
  • For slave role: configures and starts the slave pointing at the master.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/start \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/replication/start-slave

Server-to-server endpoint (token-authenticated): called by the master to have this server (a slave) begin replicating from the given master.

Request body

{
  "token": "string (required)",
  "master_host": "string (optional)",
  "master_ip": "string (optional)",
  "master_token": "string (optional)"
}
Field Type Required Description
token string yes Must match this server's remote_token.
master_host string no Master base URL; used to derive the IP if master_ip is empty.
master_ip string no Master IP address.
master_token string no Token to use when calling back to the master; saved as peer_token.

Response - 200 OK

{ "data": { "...": "result of slave setup" } }

Errors

Status Code Meaning
400 bad_request Request body failed to bind.
401 invalid_token Token does not match.
400 not_slave This server is not configured as a slave.
500 setup_failed Slave setup failed.

Side effects

  • Updates mysql_replication with remote_host and peer_token, then configures and starts the MySQL slave thread.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/start-slave \
  -H "Content-Type: application/json" \
  -d '{"token":"...","master_host":"https://203.0.113.10:2087","master_ip":"203.0.113.10","master_token":"..."}'

POST /api/mysql/replication/sync

Performs an initial sync on a slave: pulls a full mysqldump from the master, restores it locally, sets the binlog position, and restarts the slave.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "data": { "success": true, "databases_synced": "3", "message": "Initial sync complete" } }

Errors

Status Code Meaning
400 not_slave This server is not configured as a slave.
500 restore_failed Restoring the dump into local MySQL failed.
502 connection_failed Could not connect to the master.
502 dump_failed Master returned an error for the dump request.

Side effects

  • Runs STOP SLAVE, pipes the master's /api/mysql/replication/dump output into local mysql, issues CHANGE MASTER TO with the new position, then START SLAVE.

Example

curl -X POST https://panel.example.com:2087/api/mysql/replication/sync \
  -H "Authorization: Bearer $TOKEN"

GET /api/mysql/replication/sync-stream

Streams real-time initial-sync progress (per-database) to the slave via Server-Sent Events, syncing each database and the MySQL users, then setting the GTID position.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

Content-Type: text/event-stream. Each event's data is a JSON object:

{ "type": "progress", "message": "📦 Syncing appdb (1/3) - downloading from Master..." }

type is one of progress, success, error, or done.

Errors

Status Code Meaning
400 not_slave This server is not configured as a slave (sent as JSON before the stream starts).

In-stream failures are emitted as {"type":"error","message":"..."} events rather than HTTP error codes.

Side effects

  • For each master database: DROP DATABASE/CREATE DATABASE locally, pull /api/mysql/replication/dump-db, restore via mysql.
  • Pulls /api/mysql/replication/dump-users and applies it; sets gtid_slave_pos, runs CHANGE MASTER TO MASTER_USE_GTID = slave_pos, and START SLAVE.

Example

curl -N -X GET https://panel.example.com:2087/api/mysql/replication/sync-stream \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/restart

Restarts the running MySQL/MariaDB service.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "success": true, "message": "mariadb restarted" }

Errors

Status Code Meaning
400 not_found No active mysql/mysqld/mariadb service found.
500 restart_error systemctl restart failed.

Side effects

  • Finds the active service among mysql, mysqld, mariadb and runs systemctl restart.

Example

curl -X POST https://panel.example.com:2087/api/mysql/restart \
  -H "Authorization: Bearer $TOKEN"

GET /api/mysql/slow-log

Parses the MySQL slow query log and returns up to the 20 most recent slow queries (newest first).

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": {
    "queries": [
      {
        "time": "2026-06-17T10:00:00",
        "query_time": "2.5",
        "user_host": "user1[user1] @ localhost []",
        "query": "SELECT * FROM big_table"
      }
    ]
  }
}

If no slow log file is found, returns { "data": { "queries": [], "message": "Slow query log not found" } }.

Side effects

  • Read-only: locates the slow log among known paths and runs tail -500 on it.

Example

curl -X GET https://panel.example.com:2087/api/mysql/slow-log \
  -H "Authorization: Bearer $TOKEN"

GET /api/mysql/status

Returns MySQL/MariaDB install and runtime status: version, uptime, connections, query counts, buffer pool size, and process memory.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "data": {
    "installed": true,
    "running": true,
    "version": "10.11.6 (MariaDB)",
    "uptime": "1d 2h 3m",
    "connections": 4,
    "max_connections": 151,
    "queries": 0,
    "slow_queries": 0,
    "threads_running": 1,
    "buffer_pool_mb": 128,
    "process_mb": 210
  }
}

Side effects

  • Read-only: mysql -V, systemctl is-active, SHOW GLOBAL STATUS/VARIABLES, and systemctl show ... MemoryCurrent. No writes.

Example

curl -X GET https://panel.example.com:2087/api/mysql/status \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/stop

Stops the running MySQL/MariaDB service.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "success": true, "message": "mariadb stopped" }

Errors

Status Code Meaning
400 not_found No active mysql/mysqld/mariadb service found.
500 stop_error systemctl stop failed.

Side effects

  • Finds the active service among mysql, mysqld, mariadb and runs systemctl stop.

Example

curl -X POST https://panel.example.com:2087/api/mysql/stop \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/sync-users-ha

Recreates local MySQL users (using their stored password hashes) so they can connect from a ProxySQL/HA proxy IP, and syncs them into the ProxySQL admin interface.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "proxy_ip": "string (required)", "admin_pass": "string (optional)" }
Field Type Required Description
proxy_ip string yes IP of the ProxySQL/HA server to authorize and sync to.
admin_pass string no ProxySQL admin password (defaults to admin).

Response - 200 OK

{
  "success": true,
  "synced": 5,
  "proxysql_synced": 5,
  "errors": [],
  "message": "Synced 5 users for 203.0.113.30 (ProxySQL: 5)"
}

Errors

Status Code Meaning
400 bad_request Body failed to bind, or proxy_ip is empty.
500 query_error Failed to query the MySQL user list.

Side effects

  • For each app user: CREATE USER ... IDENTIFIED BY PASSWORD <hash> for 'user'@'<proxy_ip>' and GRANT ALL on its database; creates a monitor@<proxy_ip> user; runs FLUSH PRIVILEGES.
  • Saves proxy_ip to mysql_replication.ha_proxy_ip.
  • If reachable, connects to ProxySQL admin (-P6032 -uradmin) and inserts users into mysql_users, then LOAD/SAVE MYSQL USERS.

Example

curl -X POST https://panel.example.com:2087/api/mysql/sync-users-ha \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"proxy_ip":"203.0.113.30","admin_pass":"secret"}'

GET /api/mysql/tuner

Runs MySQLTuner (if installed) and returns parsed recommendations, suggested variable adjustments, a severity summary, and the detected config file path.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{
  "installed": true,
  "results": {
    "raw": "...full text report...",
    "summary": { "ok": 0, "warnings": 3, "critical": 1, "info": 5 },
    "recommendations": [
      { "type": "warning", "message": "innodb_buffer_pool_size (>= 5.0G)", "variable": "innodb_buffer_pool_size", "suggested_value": "5.0" }
    ],
    "variables": [
      { "name": "innodb_buffer_pool_size", "current": "", "suggested": "5.0" }
    ],
    "config_file": "/etc/mysql/mariadb.conf.d/50-server.cnf"
  }
}

If MySQLTuner is not installed, returns { "installed": false }. Note: this endpoint does not use the { "data": ... } envelope.

Side effects

  • Read-only: runs perl /usr/local/bin/mysqltuner.pl (with and without --json).

Example

curl -X GET https://panel.example.com:2087/api/mysql/tuner \
  -H "Authorization: Bearer $TOKEN"

POST /api/mysql/tuner/apply

Writes a tuning variable into the MySQL config file (under [mysqld]), normalizing the value, and optionally restarts the service.

Guards: RequirePermission(db, "administrative_operations")

Request body

{ "variable": "string (required)", "value": "string (required)", "restart": "boolean (optional)" }
Field Type Required Description
variable string yes Variable name to set (dashes are converted to underscores).
value string yes Value to apply; cleaned of suggestion text and decimal size suffixes.
restart boolean no Whether to restart MySQL after writing (defaults to true when omitted).

Response - 200 OK

{ "success": true }

Errors

Status Code Meaning
400 (no code) Request body failed to bind; message contains the bind error.
500 (no code) Config file not found, or reading/writing the config failed.

Side effects

  • Edits the MySQL config file in place (writes 0644), inserting or updating the variable under [mysqld].
  • If restart is enabled, runs systemctl restart on the active MySQL/MariaDB service.

Example

curl -X POST https://panel.example.com:2087/api/mysql/tuner/apply \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"variable":"innodb_buffer_pool_size","value":"512M","restart":true}'

POST /api/mysql/tuner/install

Downloads the MySQLTuner Perl script and installs the Perl JSON module.

Guards: RequirePermission(db, "administrative_operations")

Response - 200 OK

{ "success": true }

Errors

Status Code Meaning
500 (no code) curl download of mysqltuner.pl failed; message contains the output.

Side effects

  • Downloads mysqltuner.pl to /usr/local/bin/mysqltuner.pl (chmod 0755) via curl.
  • Installs the Perl JSON module via apt-get (libjson-perl), dnf, or yum (perl-JSON).

Example

curl -X POST https://panel.example.com:2087/api/mysql/tuner/install \
  -H "Authorization: Bearer $TOKEN"