Jianghu Panel - Plugin Development

12006

1. Jianghu Panel Plugins

The Jianghu Panel provides a series of plugins to facilitate server management for users. Within the Jianghu Panel, you can use the "Software Management" feature to view, install, uninstall, and configure related plugins. You can also try developing a plugin that provides the functionality you need and upload it to the server for use.

This lesson introduces the code structure and usage mechanism of Jianghu Panel plugins and provides a self-developed plugin as a demonstration for your study and research.

2. Understanding the Jianghu Panel Plugin Mechanism

  • Plugin Directory Structure: The code for each Jianghu Panel plugin is stored in the /www/server/jh-panel/plugins directory.

  • Each plugin code directory must contain the following:

├── ico.png		# Plugin icon
├── index.html	# Left sidebar of the plugin management interface
├── index.py	# Main program of the plugin, receives commands sent by the user through the panel interface, performs necessary operations according to the commands, and returns the operation results to the panel interface for user viewing
├── info.json	# Stores basic information about the plugin, such as title, installation script, author, update date, etc.
├── install.sh	# Plugin installation and uninstallation script
└── js			# Directory for storing Javascript scripts that need to be executed on the plugin management interface
  • Frontend and Backend Communication Process:
    • Communication between the panel and backend programs is uniformly conducted through /www/server/jh-panel/class/core/plugins_api.py.
    • Operations executed by the user on the panel interface are packaged into a post request by the javascript script on the frontend and sent to the backend plugins_api.py script.
    • After receiving the post request, plugins_api.py forwards it to the corresponding plugin.
    • The index.py in the plugin finds the corresponding function based on the method parameter in the post request, processes the user request with the corresponding function, and returns the processing result to the user.

3. Plugin Development Case: Command Manager Plugin

In this section, we will take the "Command Manager" plugin in the Jianghu Panel as an example to explain the plugin development of the Jianghu Panel:

Function Settings:
The function of the Command Manager plugin is to save user-defined Shell scripts. Users can execute selected scripts through the Command Manager's plugin page and view the execution results in the logs.

Installation and Uninstallation Scripts:
The installation and uninstallation scripts for the Command Manager are stored in the install.sh under the plugin directory:

# install.sh

#!/bin/bash
// Other configurations omitted

# Installation script
Install_cmd()
{
    echo 'Installing cmd...' > $install_tmp
    mkdir -p $serverPath/cmd
    echo $version > $serverPath/cmd/version.pl
    echo 'Installation complete' > $install_tmp
}

# Uninstallation script
Uninstall_cmd()
{
    echo 'Uninstalling cmd...' > $install_tmp
    rm -rf $serverPath/cmd
    echo "Uninstallation complete" > $install_tmp
}

# Install or uninstall based on user input
action=$1
if [ "${1}" == 'install' ];then
    Install_cmd
else
    Uninstall_cmd
fi

Plugin Management Interface Homepage:
The HTML code for the homepage of the plugin management interface is stored in index.html:

<div class="bt-form">
    <div class="bt-w-main">
        <div class="bt-w-menu">
            <p class="bgw" onclick="refreshTable();">Script List</p>
        </div>
        <div class="bt-w-con pd15">
            <div class="soft-man-con"></div>
        </div>
    </div>
</div>
<script type="text/javascript">

resetPluginWinWidth(760);
$.getScript( "/plugins/file?name=cmd&f=js/cmd.js", function(){
    refreshTable();
});
</script>

The above code defines the left sidebar of the plugin management interface and the Javascript script commands to be executed.

Javascript Script for the Plugin Management Interface:

The Javascript script for the Command Manager plugin is stored in cmd.js under the js directory. A portion of the code is as follows:

# ./js/cmd.js

// Variable definitions omitted...

// Get table content
function refreshTable() {
    // Other steps omitted...
    
    requestApi('script_list',{showLoading: firstLoad}, function(data){
        // Other steps omitted...

        var tbody = '';
        var tmp = rdata['data'];
        tableData = tmp;
        for(var i=0;i<tmp.length;i++){
            var opt = '';
            
            var statusHtml = '';
            var loadingStatus = tmp[i].loadingStatus || '';
            var loadingStatusColor = {'Execution Successful': 'green', 'Execution Failed': 'red'}[loadingStatus.trim()]
            
            statusHtml = '<span style="color:' + (loadingStatusColor || '#cecece') + ';">' + (loadingStatus || 'Not Executed') + '</span>';

            if(!loadingStatus || loadingStatus != 'Executing...') {
                opt += '<a href="javascript:scriptExcute(\''+tmp[i].id+'\')" class="btlink">Execute</a> | ';    
            }

            tbody += '<tr>\
                        <td style="width: 180px;">'+tmp[i].name+'</td>\
                        <td style="width: 100px;">'+statusHtml+'</td>\
                        <td style="text-align: right;width: 280px;">\
                            '+opt+
                            '<a href="javascript:openScriptLogs(\''+tmp[i].id+'\')" class="btlink">Logs</a> | ' + 
                            '<a href="javascript:openEditItem(\''+tmp[i].id+'\')" class="btlink">Edit</a> | ' + 
                            '<a href="javascript:deleteItem(\''+tmp[i].id+'\', \''+tmp[i].name+'\')" class="btlink">Delete</a>\
                        </td>\
                    </tr>';
        }
        $(".plugin-table-body").html(tbody);
    });
}
// Open the add script popup
function openCreateItem() {
    addLayer = layer.open({
        type: 1,
        skin: 'demo-class',
        area: '640px',
        title: 'Add Script',
        closeBtn: 1,
        shift: 0,
        shadeClose: false,
        content: "\
        <form class='bt-form pd20 pb70' id='addForm'>\
            <div class='line'>\
                <span class='tname'>Script Name</span>\
                <div class='info-r c4'>\
                    <input id='scriptName' class='bt-input-text' type='text' name='name' placeholder='Script Name' style='width:458px' />\
                </div>\
            </div>\
            <div class='line'>\
                <span class='tname'>Script Content</span>\
                <div class='info-r c4'>\
                    <textarea id='scriptContent' class='bt-input-text' name='script' style='width:458px;height:100px;line-height:22px' /></textarea>\
                </div>\
            </div>\
            <div class='bt-form-submit-btn'>\
                <button type='button' class='btn btn-danger btn-sm btn-title' onclick='layer.close(addLayer)'>Cancel</button>\
                <button type='button' class='btn btn-success btn-sm btn-title' onclick=\"submitCreateItem()\">Submit</button>\
            </div>\
        </form>",
    });
}

// Submit the added script
function submitCreateItem(){
    requestApi('script_add', {
        name: $('#addForm #scriptName').val(),
        script: $('#addForm #scriptContent').val()
    }, function(data){
        // Callback function omitted...
    });
}

// Send post request to the backend
function requestApi(method,args,callback){
    return new Promise(function(resolve, reject) {
        // Other steps omitted...
        $.post('/plugins/run', {name:'cmd', func:method, args:_args}, function(data) {
            // Callback function omitted...
        },'json');
    });
}

// Other functions omitted

In the above code, you can clearly see the functions called for each user operation, the requests sent to the backend, and the interface and script content displayed on the frontend.

Main Program of the Plugin:

The main program of the Command Manager plugin is stored in index.py. A portion of the code is as follows:

# coding:utf-8

# Import, configuration, and other functions omitted

# Script list
def scriptList():
    data = getAll('script')
    for item in data:
        echo = item.get('echo', '')

        # loadingStatus
        loadingStatusCmd = "ls -R %s/script | grep %s_status" % (getServerDir() , echo) 
        loadingStatusExec = mw.execShell(loadingStatusCmd)
        if loadingStatusExec[0] != '':
            item['loadingStatus'] = mw.readFile(getServerDir() + '/script/' + echo + '_status')

    return mw.returnJson(True, 'ok', data)

# Add script
def scriptAdd():
    args = getArgs()
    data = checkArgs(args, ['name', 'script'])
    if not data[0]:
        return data[1]
    name = args['name']
    script = getScriptArg('script')
    echo =  mw.md5(str(time.time()) + '_docker')
    id = int(time.time())
    saveOne('script', id, {
        'name': name,
        'script': script,
        'create_time': int(time.time()),
        'echo': echo
    })
    statusFile = '%s/script/%s_status' % (getServerDir(), echo)
    finalScript = """
{ 
    touch %(statusFile)s\n
    echo "Executing..." >> %(statusFile)s\n
    cat /dev/null > %(statusFile)s\n
    {
        %(script)s\n
    } && {
        echo "Execution Successful" >> %(statusFile)s\n
    }
} || { 
    echo "Execution Failed" >> %(statusFile)s\n
}
    """ % {"statusFile": statusFile, "script": script}
    makeScriptFile(echo + '.sh', finalScript)
    return mw.returnJson(True, 'Added successfully!')

if __name__ == "__main__":
    func = sys.argv[1]
    elif func == 'script_list':
        print(scriptList())
    elif func == 'script_add':
        print(scriptAdd())
        # Other options omitted
    else:
        print('error')

In the above code, you can see how the functionalities of retrieving the script list and adding scripts are implemented in the backend. You can also see how the main program finds the function to be executed based on the requests sent from the frontend.

4. Using Your Developed Plugin

In the Jianghu Panel, you can develop your own plugin according to the plugin mechanism introduced above and upload it for use in the Jianghu Panel.

How to Use Your Developed Plugin in the Jianghu Panel:

This lesson provides a sample plugin. You can download it and then upload it through the Jianghu Panel's "Software Management" --> "Add Plugin" feature. After unzipping, you will see this newly added plugin in the software list. Click install, and once completed, you can start using it.

Your developed plugin can also be added to the Jianghu Panel using the packaging and uploading method.