Jianghu Panel - Plugin Development
120061. 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/pluginsdirectory.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
postrequest by thejavascriptscript on the frontend and sent to the backendplugins_api.pyscript. - After receiving the
postrequest,plugins_api.pyforwards it to the corresponding plugin. - The
index.pyin the plugin finds the corresponding function based on themethodparameter in thepostrequest, processes the user request with the corresponding function, and returns the processing result to the user.
- Communication between the panel and backend programs is uniformly conducted through
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
fiPlugin 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.