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
114 changes: 114 additions & 0 deletions spk/pelican_panel/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
SPK_NAME = pelican_panel
SPK_VERS = 1.0.0
SPK_REV = 12
SPK_ICON = src/PACKAGE_ICON.PNG
DSM_UI_DIR = app

# No cross-compilation needed - Docker-based package
DEPENDS =

# Require DSM 7.0+ for Container Manager
REQUIRED_MIN_DSM = 7.0
# Container Manager (Docker) is required
SPK_DEPENDS = "ContainerManager"

# Only x86_64 supported (Docker images)
UNSUPPORTED_ARCHS = $(PPC_ARCHS) $(ARMv5_ARCHS) $(ARMv7_ARCHS) $(ARMv7L_ARCHS) $(i686_ARCHS)

MAINTAINER = SynoCommunity
MAINTAINER_URL = https://github.com/SynoCommunity
DESCRIPTION = Pelican Panel is a free, open-source game server management panel built with PHP, React, and Go. It includes Wings daemon for server management.
DESCRIPTION_FRE = Pelican Panel est un panneau de gestion de serveurs de jeux gratuit et open-source construit avec PHP, React et Go. Il inclut le daemon Wings pour la gestion des serveurs.
DISPLAY_NAME = Pelican Panel
CHANGELOG = "1. Initial release with Panel and Wings support."

HOMEPAGE = https://pelican.dev
LICENSE = MIT

# Service configuration
SERVICE_USER = auto
SERVICE_SETUP = src/service-setup.sh
SSS_SCRIPT = src/dsm-control.sh
STARTABLE = yes

# Wizard for installation
WIZARDS_DIR = src/wizard/

# Ports (SERVICE_PORT for firewall, no ADMIN_PORT - handled by .url in config)
SERVICE_PORT = 8080
SERVICE_PORT_TITLE = Pelican Panel (HTTP)

# Additional ports for Wings
SERVICE_PORT_01 = 8445
SERVICE_PORT_TITLE_01 = Wings API (HTTPS)
SERVICE_PORT_02 = 2022
SERVICE_PORT_TITLE_02 = Wings SFTP

# Configuration directory
CONF_DIR = src/conf/

# DSM UI integration
DSM_UI_CONFIG = src/app/config

# Override copy target since we have no cross dependencies
COPY_TARGET = pelican_copy_target

# Post-strip target for installing files
POST_STRIP_TARGET = pelican_extra_install

# noarch since we just deploy Docker config files
override ARCH = noarch

include ../../mk/spksrc.spk.mk

# ===========================================
# Auto-increment version management
# ===========================================
# Usage:
# make bump - Increment SPK_REV and build
# make bump-only - Just increment SPK_REV without building

.PHONY: bump bump-only

bump-only:
@./scripts/bump-revision.sh

bump: bump-only
@$(MAKE) noarch-7.0

# Custom copy target - create empty staging directory
.PHONY: pelican_copy_target
pelican_copy_target:
@$(MSG) "Creating staging directory for pelican_panel"
@mkdir -p $(STAGING_DIR)

.PHONY: pelican_extra_install
pelican_extra_install:
@$(MSG) "Installing Pelican Panel files"
# Create bin directory for executables
install -m 755 -d $(STAGING_DIR)/bin
install -m 755 src/bin/loading-proxy.py $(STAGING_DIR)/bin/
install -m 755 src/wings-config-watcher.sh $(STAGING_DIR)/bin/
# Create share directory for config templates
install -m 755 -d $(STAGING_DIR)/share
install -m 755 -d $(STAGING_DIR)/share/docker
install -m 755 -d $(STAGING_DIR)/share/patches
# Install Docker compose file
install -m 644 src/docker/compose.yaml $(STAGING_DIR)/share/docker/
# Install environment template
install -m 644 src/panel.env.example $(STAGING_DIR)/share/
# Install Wings config template
install -m 644 src/wings.config.example.yml $(STAGING_DIR)/share/
# Install patches for Panel
install -m 644 src/patches/*.php $(STAGING_DIR)/share/patches/ 2>/dev/null || true
# Install loading page
install -m 644 src/loading.html $(STAGING_DIR)/share/
# Create app directory for DSM UI
install -m 755 -d $(STAGING_DIR)/app
install -m 755 -d $(STAGING_DIR)/app/images
install -m 644 src/app/config $(STAGING_DIR)/app/
install -m 644 src/app/*.html $(STAGING_DIR)/app/ 2>/dev/null || true
install -m 644 src/app/*.js $(STAGING_DIR)/app/ 2>/dev/null || true
install -m 755 src/app/*.cgi $(STAGING_DIR)/app/ 2>/dev/null || true
install -m 644 src/app/*.sc $(STAGING_DIR)/app/ 2>/dev/null || true
install -m 644 src/app/images/*.png $(STAGING_DIR)/app/images/ 2>/dev/null || true
28 changes: 28 additions & 0 deletions spk/pelican_panel/scripts/bump-revision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash
#
# Auto-increment SPK_REV in Makefile
# Usage: ./scripts/bump-revision.sh
#

MAKEFILE="$(dirname "$0")/../Makefile"

if [ ! -f "$MAKEFILE" ]; then
echo "ERROR: Makefile not found at $MAKEFILE"
exit 1
fi

# Get current revision
CURRENT_REV=$(grep -E "^SPK_REV\s*=" "$MAKEFILE" | sed 's/SPK_REV\s*=\s*//')

if [ -z "$CURRENT_REV" ]; then
echo "ERROR: Could not find SPK_REV in Makefile"
exit 1
fi

# Increment revision
NEW_REV=$((CURRENT_REV + 1))

# Update Makefile
sed -i "s/^SPK_REV\s*=\s*[0-9]*/SPK_REV = ${NEW_REV}/" "$MAKEFILE"

echo "Bumped SPK_REV: $CURRENT_REV -> $NEW_REV"
Binary file added spk/pelican_panel/src/PACKAGE_ICON.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions spk/pelican_panel/src/app/PelicanWings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Namespace definition
Ext.ns("PelicanWings");

// Application definition
Ext.define("PelicanWings.AppInstance", {
extend: "SYNO.SDS.AppInstance",
appWindowName: "PelicanWings.AppWindow"
});

// Window definition
Ext.define("PelicanWings.AppWindow", {
extend: "SYNO.SDS.AppWindow",

constructor: function(config) {
this.appInstance = config.appInstance;

// Use the 3rdparty path which is allowed by DSM's Content Security Policy
var iframeSrc = '/webman/3rdparty/pelican_panel/wings-config.html';

config = Ext.apply({
resizable: true,
maximizable: true,
minimizable: true,
width: 900,
height: 700,
minWidth: 600,
minHeight: 400,
items: [{
xtype: 'box',
autoEl: {
tag: 'iframe',
src: iframeSrc,
width: '100%',
height: '100%',
frameborder: '0',
style: 'border: none; background: #0a1628;'
}
}],
tools: [{
id: 'help',
qtip: 'Ouvrir dans un nouvel onglet',
handler: function(event, element, panel) {
window.open(iframeSrc, '_blank');
}
}]
}, config);

this.callParent([config]);
}
});
53 changes: 53 additions & 0 deletions spk/pelican_panel/src/app/WingsConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Ext.namespace("SYNO.SDS.PelicanWings.Utils");

Ext.apply(SYNO.SDS.PelicanWings.Utils, function(){
return {
getMainHtml: function(){
var host = window.location.hostname;
return '<iframe src="http://' + host + ':8080/wings-config?_ts=' + new Date().getTime() + '" title="Wings Configuration" style="width: 100%; height: 100%; border: none; margin: 0; background: #0a1628;"/>';
}
};
}());

Ext.define("SYNO.SDS.PelicanWings.Application", {
extend: "SYNO.SDS.AppInstance",
appWindowName: "SYNO.SDS.PelicanWings.MainWindow",
constructor: function(){
this.callParent(arguments);
}
});

Ext.define("SYNO.SDS.PelicanWings.MainWindow", {
extend: "SYNO.SDS.AppWindow",
constructor: function(a){
var MY = SYNO.SDS.PelicanWings;
this.appInstance = a.appInstance;
MY.MainWindow.superclass.constructor.call(this, Ext.apply({
layout: "fit",
resizable: true,
cls: "syno-pelican-wings-win",
maximizable: true,
minimizable: true,
width: 900,
height: 700,
html: MY.Utils.getMainHtml()
}, a));
MY.Utils.ApplicationWindow = this;
},

onOpen: function(){
SYNO.SDS.PelicanWings.MainWindow.superclass.onOpen.apply(this, arguments);
},

onRequest: function(a){
SYNO.SDS.PelicanWings.MainWindow.superclass.onRequest.call(this, a);
},

onClose: function(){
clearTimeout(SYNO.SDS.PelicanWings.TimeOutID);
SYNO.SDS.PelicanWings.TimeOutID = undefined;
SYNO.SDS.PelicanWings.MainWindow.superclass.onClose.apply(this, arguments);
this.doClose();
return true;
}
});
45 changes: 45 additions & 0 deletions spk/pelican_panel/src/app/api.cgi
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh
# CGI proxy for Wings API - forwards requests to the loading-proxy on port 8080

# Handle CORS preflight
if [ "$REQUEST_METHOD" = "OPTIONS" ]; then
echo "Content-Type: application/json"
echo "Access-Control-Allow-Origin: *"
echo "Access-Control-Allow-Methods: GET, POST, OPTIONS"
echo "Access-Control-Allow-Headers: Content-Type"
echo ""
exit 0
fi

# Get the action from query string
ACTION=$(echo "$QUERY_STRING" | sed -n 's/.*action=\([^&]*\).*/\1/p')

# Set content type and CORS headers
echo "Content-Type: application/json"
echo "Access-Control-Allow-Origin: *"
echo ""

case "$ACTION" in
status)
curl -s --connect-timeout 5 --max-time 10 "http://127.0.0.1:8080/api/wings/status" 2>/dev/null || echo '{"success":false,"error":"Service unavailable"}'
;;
get-config)
curl -s --connect-timeout 5 --max-time 10 "http://127.0.0.1:8080/api/wings/config" 2>/dev/null || echo '{"success":false,"error":"Service unavailable"}'
;;
save-config)
# Read POST data from stdin
if [ "$REQUEST_METHOD" = "POST" ]; then
if [ -n "$CONTENT_LENGTH" ] && [ "$CONTENT_LENGTH" -gt 0 ] 2>/dev/null; then
POST_DATA=$(dd bs=1 count="$CONTENT_LENGTH" 2>/dev/null)
else
POST_DATA=$(cat)
fi
curl -s --connect-timeout 5 --max-time 30 -X POST -H "Content-Type: application/json" -d "$POST_DATA" "http://127.0.0.1:8080/api/wings/config" 2>/dev/null || echo '{"success":false,"error":"Service unavailable"}'
else
echo '{"success":false,"error":"POST method required"}'
fi
;;
*)
echo '{"success":false,"error":"Unknown action"}'
;;
esac
31 changes: 31 additions & 0 deletions spk/pelican_panel/src/app/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
".url": {
"com.synocommunity.packages.pelican_panel": {
"title": "Pelican Panel",
"desc": "Ouvrir l'interface web du Panel Pelican",
"icon": "images/pelican_panel-{0}.png",
"type": "url",
"protocol": "http",
"port": "8080",
"url": "/",
"allUsers": true
}
},
"PelicanWings.js": {
"PelicanWings.AppInstance": {
"type": "app",
"title": "Configurer Wings",
"desc": "Configurer le daemon Wings pour ce NAS",
"icon": "images/pelican_panel-{0}.png",
"allowMultiInstance": false,
"allUsers": false,
"appWindow": "PelicanWings.AppWindow",
"depend": ["PelicanWings.AppWindow"]
},
"PelicanWings.AppWindow": {
"type": "lib",
"title": "Configurer Wings",
"icon": "images/pelican_panel-{0}.png"
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading