feat: WFJ Panel v0.1.0 - 完整功能版
- SSH 服务器连接管理 + 登录页自动预填 - 服务器概览监控(CPU/内存/硬盘/网络/进程,进程卡片支持排序和端口显示) - 远程文件管理(浏览/上传/下载/新建/删除,实时进度条) - 交互式终端(xterm.js + ssh2 PTY) - Docker 容器管理(三连接架构,概览/容器列表/镜像/应用商店/任务中心/配置/网络/卷) - 应用商店一键部署(Redis/MySQL/Halo,在线模板同步,数据驱动 options/showWhen/extraArgs) - Docker 镜像源配置(daemon.json 编辑 + 自动重启) - Docker 强制停止修复(三步强制停止 + 轮询确认) - 系统防火墙管理(firewalld/ufw/iptables 自动检测 + GUI 管理) - 客户端版本检查(/api/version 接口 + 更新弹窗) - 品牌名统一为 WFJ Panel - electron-builder 打包配置(NSIS 安装包,国内镜像) - .npmrc 配置 Electron 国内镜像
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
|
||||
COPY app.py .
|
||||
|
||||
ENV DB_HOST=127.0.0.1
|
||||
ENV DB_PORT=3306
|
||||
ENV DB_USER=root
|
||||
ENV DB_PASSWORD=123456
|
||||
ENV DB_NAME=appstore
|
||||
ENV API_PORT=3001
|
||||
ENV API_TOKEN=123456
|
||||
|
||||
EXPOSE 3001
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -0,0 +1,234 @@
|
||||
"""
|
||||
应用商店 HTTP API 服务
|
||||
从 MySQL 读取应用模板数据,返回 JSON 给 Electron 客户端
|
||||
所有数据库连接参数通过环境变量配置
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
from flask import Flask, jsonify, request
|
||||
from flask_cors import CORS
|
||||
import mysql.connector
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 允许跨域(Electron 渲染进程直接 fetch 时需要)
|
||||
CORS(app)
|
||||
|
||||
# ─── 数据库配置(环境变量) ───
|
||||
DB_HOST = os.environ.get('DB_HOST', '127.0.0.1')
|
||||
DB_PORT = int(os.environ.get('DB_PORT', '3306'))
|
||||
DB_USER = os.environ.get('DB_USER', 'root')
|
||||
DB_PASSWORD = os.environ.get('DB_PASSWORD', '123456')
|
||||
DB_NAME = os.environ.get('DB_NAME', 'appstore')
|
||||
|
||||
# API 监听端口
|
||||
API_PORT = int(os.environ.get('API_PORT', '3001'))
|
||||
|
||||
# API 鉴权 token(可选,客户端通过 Authorization header 传递)
|
||||
API_TOKEN = os.environ.get('API_TOKEN', '')
|
||||
|
||||
|
||||
def get_db():
|
||||
"""获取数据库连接"""
|
||||
return mysql.connector.connect(
|
||||
host=DB_HOST,
|
||||
port=DB_PORT,
|
||||
user=DB_USER,
|
||||
password=DB_PASSWORD,
|
||||
database=DB_NAME,
|
||||
charset='utf8mb4'
|
||||
)
|
||||
|
||||
|
||||
def check_token():
|
||||
"""校验 API token,未配置 token 时跳过"""
|
||||
if not API_TOKEN:
|
||||
return True
|
||||
auth = request.headers.get('Authorization', '')
|
||||
return auth == f'Bearer {API_TOKEN}'
|
||||
|
||||
|
||||
def fetch_template_list():
|
||||
"""查询所有模板(列表摘要)"""
|
||||
conn = get_db()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
'SELECT id, app_key, name, description, icon, category, '
|
||||
'image, container_name, sort_order, is_active '
|
||||
'FROM app_templates WHERE is_active = 1 ORDER BY sort_order, id'
|
||||
)
|
||||
rows = cursor.fetchall()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
def fetch_template_detail(app_key):
|
||||
"""查询单个模板的完整详情(含子表)"""
|
||||
conn = get_db()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 主表
|
||||
cursor.execute('SELECT * FROM app_templates WHERE app_key = %s AND is_active = 1', (app_key,))
|
||||
tpl = cursor.fetchone()
|
||||
if not tpl:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
# 子表批量查询
|
||||
sub_tables = ['app_ports', 'app_volumes', 'app_envs', 'app_mkdirs', 'app_write_files', 'app_params']
|
||||
for table in sub_tables:
|
||||
cursor.execute(f'SELECT * FROM {table} WHERE template_id = %s ORDER BY sort_order', (tpl['id'],))
|
||||
rows = cursor.fetchall()
|
||||
# 序列化:去掉 template_id 和 id,JSON 友好
|
||||
cleaned = []
|
||||
for r in rows:
|
||||
item = {k: v for k, v in r.items() if k not in ('id', 'template_id')}
|
||||
cleaned.append(item)
|
||||
# 子表名映射到 deploy 对应字段
|
||||
field_map = {
|
||||
'app_ports': 'ports',
|
||||
'app_volumes': 'volumes',
|
||||
'app_envs': 'env',
|
||||
'app_mkdirs': 'mkdirs',
|
||||
'app_write_files': 'writeFiles',
|
||||
'app_params': 'params'
|
||||
}
|
||||
tpl[field_map[table]] = cleaned
|
||||
|
||||
# 删除不需要的主表字段
|
||||
tpl.pop('id', None)
|
||||
tpl.pop('is_active', None)
|
||||
tpl.pop('sort_order', None)
|
||||
tpl.pop('created_at', None)
|
||||
tpl.pop('updated_at', None)
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return tpl
|
||||
|
||||
|
||||
def fetch_all_templates_full():
|
||||
"""一次性查询所有模板的完整详情"""
|
||||
conn = get_db()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 所有主表记录
|
||||
cursor.execute(
|
||||
'SELECT * FROM app_templates WHERE is_active = 1 ORDER BY sort_order, id'
|
||||
)
|
||||
templates = cursor.fetchall()
|
||||
|
||||
sub_tables = ['app_ports', 'app_volumes', 'app_envs', 'app_mkdirs', 'app_write_files', 'app_params']
|
||||
field_map = {
|
||||
'app_ports': 'ports',
|
||||
'app_volumes': 'volumes',
|
||||
'app_envs': 'env',
|
||||
'app_mkdirs': 'mkdirs',
|
||||
'app_write_files': 'writeFiles',
|
||||
'app_params': 'params'
|
||||
}
|
||||
|
||||
for tpl in templates:
|
||||
tpl_id = tpl['id']
|
||||
for table in sub_tables:
|
||||
cursor.execute(
|
||||
f'SELECT * FROM {table} WHERE template_id = %s ORDER BY sort_order',
|
||||
(tpl_id,)
|
||||
)
|
||||
rows = cursor.fetchall()
|
||||
cleaned = []
|
||||
for r in rows:
|
||||
item = {k: v for k, v in r.items() if k not in ('id', 'template_id')}
|
||||
cleaned.append(item)
|
||||
tpl[field_map[table]] = cleaned
|
||||
|
||||
# 清理主表多余字段
|
||||
tpl.pop('id', None)
|
||||
tpl.pop('is_active', None)
|
||||
tpl.pop('sort_order', None)
|
||||
tpl.pop('created_at', None)
|
||||
tpl.pop('updated_at', None)
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return templates
|
||||
|
||||
|
||||
# ─── API 路由 ───
|
||||
|
||||
@app.route('/api/apps', methods=['GET'])
|
||||
def list_apps():
|
||||
"""获取所有应用模板列表(完整详情)"""
|
||||
if not check_token():
|
||||
return jsonify({'ok': False, 'error': '未授权'}), 401
|
||||
|
||||
try:
|
||||
templates = fetch_all_templates_full()
|
||||
return jsonify({'ok': True, 'data': templates, 'count': len(templates)})
|
||||
except Exception as e:
|
||||
return jsonify({'ok': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/apps/<app_key>', methods=['GET'])
|
||||
def get_app(app_key):
|
||||
"""获取单个应用模板详情"""
|
||||
if not check_token():
|
||||
return jsonify({'ok': False, 'error': '未授权'}), 401
|
||||
|
||||
try:
|
||||
tpl = fetch_template_detail(app_key)
|
||||
if not tpl:
|
||||
return jsonify({'ok': False, 'error': '模板不存在'}), 404
|
||||
return jsonify({'ok': True, 'data': tpl})
|
||||
except Exception as e:
|
||||
return jsonify({'ok': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/health', methods=['GET'])
|
||||
def health():
|
||||
"""健康检查"""
|
||||
return jsonify({'ok': True, 'status': 'running'})
|
||||
|
||||
|
||||
@app.route('/api/version', methods=['GET'])
|
||||
def get_version():
|
||||
"""获取客户端最新版本信息"""
|
||||
if not check_token():
|
||||
return jsonify({'ok': False, 'error': '未授权'}), 401
|
||||
|
||||
try:
|
||||
conn = get_db()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
'SELECT version, download_url, release_notes, is_latest, '
|
||||
'created_at FROM app_version WHERE is_latest = 1 ORDER BY id DESC LIMIT 1'
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if not row:
|
||||
return jsonify({'ok': False, 'error': '暂无版本信息'}), 404
|
||||
|
||||
return jsonify({
|
||||
'ok': True,
|
||||
'data': {
|
||||
'version': row['version'],
|
||||
'downloadUrl': row['download_url'],
|
||||
'releaseNotes': row['release_notes'] or '',
|
||||
'isLatest': bool(row['is_latest']),
|
||||
'createdAt': row['created_at'].isoformat() if row['created_at'] else None,
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'ok': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(f'[AppStore API] DB: {DB_HOST}:{DB_PORT}/{DB_NAME}')
|
||||
print(f'[AppStore API] Listen on :{API_PORT}')
|
||||
print(f'[AppStore API] Token auth: {"on" if API_TOKEN else "off"}')
|
||||
app.run(host='0.0.0.0', port=API_PORT, debug=False)
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# 应用商店 API 服务一键部署脚本
|
||||
# 在服务器 192.168.153.153 上执行
|
||||
# 功能:建表 → 构建镜像 → 启动容器
|
||||
# 用法:bash deploy.sh
|
||||
# ============================================================
|
||||
|
||||
set -e
|
||||
|
||||
echo "========== 1/4 创建数据库和表 =========="
|
||||
# 注意:MySQL 容器已在运行,3306 端口映射到宿主机
|
||||
mysql -h 127.0.0.1 -P 3306 -u root -p${MYSQL_ROOT_PASSWORD:-123456} < init.sql
|
||||
echo "数据库和表创建完成"
|
||||
|
||||
echo "========== 2/4 构建 API 镜像 =========="
|
||||
docker build -t appstore-api:latest .
|
||||
|
||||
echo "========== 3/4 停止旧容器(如有) =========="
|
||||
docker rm -f appstore-api 2>/dev/null || true
|
||||
|
||||
echo "========== 4/4 启动 API 容器 =========="
|
||||
docker run -d \
|
||||
--name appstore-api \
|
||||
--restart always \
|
||||
-p 3001:3001 \
|
||||
-e DB_HOST=127.0.0.1 \
|
||||
-e DB_PORT=3306 \
|
||||
-e DB_USER=root \
|
||||
-e DB_PASSWORD=${MYSQL_ROOT_PASSWORD:-123456} \
|
||||
-e DB_NAME=appstore \
|
||||
-e API_PORT=3001 \
|
||||
appstore-api:latest
|
||||
|
||||
echo ""
|
||||
echo "========== 部署完成 =========="
|
||||
echo "API 地址: http://$(hostname -I | awk '{print $1}'):3001/api/apps"
|
||||
echo "健康检查: http://$(hostname -I | awk '{print $1}'):3001/api/health"
|
||||
@@ -0,0 +1,290 @@
|
||||
-- ============================================================
|
||||
-- 应用商店数据库 —— 建表 + 初始数据
|
||||
-- 数据库: my_panel
|
||||
-- 字符集: utf8mb4
|
||||
-- ============================================================
|
||||
|
||||
-- ─── 主表 ───
|
||||
CREATE TABLE IF NOT EXISTS app_templates (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
app_key VARCHAR(64) NOT NULL UNIQUE COMMENT '唯一标识 (redis / mysql)',
|
||||
name VARCHAR(128) NOT NULL COMMENT '显示名称',
|
||||
tagline VARCHAR(256) DEFAULT NULL COMMENT '一句话描述',
|
||||
description TEXT DEFAULT NULL COMMENT '详细描述',
|
||||
icon VARCHAR(32) DEFAULT NULL COMMENT '图标标识 (redis / mysql)',
|
||||
category VARCHAR(64) DEFAULT NULL COMMENT '分类',
|
||||
image VARCHAR(256) NOT NULL COMMENT 'Docker 镜像',
|
||||
container_name VARCHAR(128) NOT NULL COMMENT '容器名称',
|
||||
network VARCHAR(128) DEFAULT '' COMMENT 'Docker 网络',
|
||||
restart VARCHAR(32) DEFAULT 'always' COMMENT '重启策略',
|
||||
hostname VARCHAR(128) DEFAULT '' COMMENT '容器主机名',
|
||||
workdir VARCHAR(256) DEFAULT '' COMMENT '工作目录',
|
||||
command TEXT DEFAULT NULL COMMENT '启动命令',
|
||||
extra_args TEXT DEFAULT NULL COMMENT '额外 docker run 参数模板 (含 {{}} 占位符)',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序权重',
|
||||
is_active TINYINT(1) DEFAULT 1 COMMENT '是否上架 1=是 0=否',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用模板主表';
|
||||
|
||||
-- ─── 子表:端口映射 ───
|
||||
CREATE TABLE IF NOT EXISTS app_ports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id INT NOT NULL,
|
||||
host_port VARCHAR(16) NOT NULL COMMENT '宿主机端口',
|
||||
container_port VARCHAR(16) NOT NULL COMMENT '容器端口',
|
||||
protocol VARCHAR(8) DEFAULT 'tcp' COMMENT '协议',
|
||||
sort_order INT DEFAULT 0,
|
||||
FOREIGN KEY (template_id) REFERENCES app_templates(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- ─── 子表:挂载卷 ───
|
||||
CREATE TABLE IF NOT EXISTS app_volumes (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id INT NOT NULL,
|
||||
host_path VARCHAR(512) NOT NULL COMMENT '宿主机路径',
|
||||
container_path VARCHAR(512) NOT NULL COMMENT '容器路径',
|
||||
mode VARCHAR(4) DEFAULT 'rw' COMMENT '读写模式',
|
||||
sort_order INT DEFAULT 0,
|
||||
FOREIGN KEY (template_id) REFERENCES app_templates(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- ─── 子表:环境变量 ───
|
||||
CREATE TABLE IF NOT EXISTS app_envs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id INT NOT NULL,
|
||||
env_key VARCHAR(256) NOT NULL COMMENT '变量名',
|
||||
env_value TEXT DEFAULT NULL COMMENT '变量值 (含 {{}} 占位符)',
|
||||
sort_order INT DEFAULT 0,
|
||||
FOREIGN KEY (template_id) REFERENCES app_templates(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- ─── 子表:自动创建目录 ───
|
||||
CREATE TABLE IF NOT EXISTS app_mkdirs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id INT NOT NULL,
|
||||
dir_path VARCHAR(512) NOT NULL COMMENT '宿主机目录',
|
||||
sort_order INT DEFAULT 0,
|
||||
FOREIGN KEY (template_id) REFERENCES app_templates(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- ─── 子表:自动写入配置文件 ───
|
||||
CREATE TABLE IF NOT EXISTS app_write_files (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id INT NOT NULL,
|
||||
file_path VARCHAR(512) NOT NULL COMMENT '宿主机文件路径',
|
||||
file_content LONGTEXT NOT NULL COMMENT '文件内容 (含 {{}} 占位符)',
|
||||
sort_order INT DEFAULT 0,
|
||||
FOREIGN KEY (template_id) REFERENCES app_templates(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- ─── 子表:用户参数 ───
|
||||
CREATE TABLE IF NOT EXISTS app_params (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
template_id INT NOT NULL,
|
||||
param_key VARCHAR(64) NOT NULL COMMENT '参数键',
|
||||
label VARCHAR(128) NOT NULL COMMENT '显示标签',
|
||||
type VARCHAR(16) DEFAULT 'text' COMMENT 'text/password/number',
|
||||
placeholder VARCHAR(256) DEFAULT NULL,
|
||||
default_value VARCHAR(256) DEFAULT '' COMMENT '默认值',
|
||||
required TINYINT(1) DEFAULT 0 COMMENT '是否必填',
|
||||
options TEXT DEFAULT NULL COMMENT '下拉选项 JSON 数组 [{"value":"","label":""}]',
|
||||
show_when VARCHAR(128) DEFAULT NULL COMMENT '条件显示 param_key:value',
|
||||
sort_order INT DEFAULT 0,
|
||||
FOREIGN KEY (template_id) REFERENCES app_templates(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- 初始数据:Redis 模板
|
||||
-- ============================================================
|
||||
INSERT INTO app_templates (app_key, name, tagline, description, icon, category, image, container_name, network, restart, hostname, workdir, command, sort_order, is_active)
|
||||
VALUES ('redis', 'Redis', '高性能内存数据库 / 缓存',
|
||||
'Redis 是开源的内存数据结构存储,用作数据库、缓存、消息队列和流引擎。支持字符串、哈希、列表、集合等多种数据结构。',
|
||||
'redis', '缓存', 'redis:8.10.0', 'redis', '', 'always', 'redis', '/data',
|
||||
'redis-server /etc/redis/redis.conf', 1, 1);
|
||||
|
||||
SET @redis_id = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO app_ports (template_id, host_port, container_port, protocol, sort_order) VALUES
|
||||
(@redis_id, '6379', '6379', 'tcp', 0);
|
||||
|
||||
INSERT INTO app_volumes (template_id, host_path, container_path, mode, sort_order) VALUES
|
||||
(@redis_id, '/opt/redis/data', '/data', 'rw', 0),
|
||||
(@redis_id, '/opt/redis/conf/redis.conf', '/etc/redis/redis.conf', 'rw', 1),
|
||||
(@redis_id, '/opt/redis/logs', '/logs', 'rw', 2);
|
||||
|
||||
INSERT INTO app_mkdirs (template_id, dir_path, sort_order) VALUES
|
||||
(@redis_id, '/opt/redis/data', 0),
|
||||
(@redis_id, '/opt/redis/conf', 1),
|
||||
(@redis_id, '/opt/redis/logs', 2);
|
||||
|
||||
INSERT INTO app_write_files (template_id, file_path, file_content, sort_order) VALUES
|
||||
(@redis_id, '/opt/redis/conf/redis.conf',
|
||||
'# Redis 配置文件
|
||||
bind 0.0.0.0
|
||||
protected-mode no
|
||||
port 6379
|
||||
tcp-backlog 511
|
||||
timeout 0
|
||||
tcp-keepalive 300
|
||||
daemonize no
|
||||
supervised no
|
||||
pidfile /var/run/redis_6379.pid
|
||||
loglevel notice
|
||||
logfile ""
|
||||
databases 16
|
||||
always-show-logo yes
|
||||
save 900 1
|
||||
save 300 10
|
||||
save 60 10000
|
||||
stop-writes-on-bgsave-error yes
|
||||
rdbcompression yes
|
||||
rdbchecksum yes
|
||||
dbfilename dump.rdb
|
||||
dir /data
|
||||
appendonly no
|
||||
appendfilename "appendonly.aof"
|
||||
appendfsync everysec
|
||||
no-appendfsync-on-rewrite no
|
||||
auto-aof-rewrite-percentage 100
|
||||
auto-aof-rewrite-min-size 64mb
|
||||
# 密码(部署时替换,留空则无密码)
|
||||
{{REDIS_PASSWORD_LINE}}', 0);
|
||||
|
||||
INSERT INTO app_params (template_id, param_key, label, type, placeholder, default_value, required, sort_order) VALUES
|
||||
(@redis_id, 'REDIS_PASSWORD', 'Redis 密码', 'password', '留空则无密码', '', 0, 0),
|
||||
(@redis_id, 'PORT', '宿主机端口', 'number', '6379', '6379', 1, 1);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- 初始数据:MySQL 模板
|
||||
-- ============================================================
|
||||
INSERT INTO app_templates (app_key, name, tagline, description, icon, category, image, container_name, network, restart, hostname, workdir, command, sort_order, is_active)
|
||||
VALUES ('mysql', 'MySQL', '关系型数据库',
|
||||
'MySQL 是最流行的开源关系型数据库管理系统,支持事务、存储过程、触发器、视图等完整 SQL 功能。此模板基于 MySQL 8.4 LTS。',
|
||||
'mysql', '数据库', 'mysql:8.4.11', 'mysql', '', 'always', 'mysql', '/', '', 2, 1);
|
||||
|
||||
SET @mysql_id = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO app_ports (template_id, host_port, container_port, protocol, sort_order) VALUES
|
||||
(@mysql_id, '3306', '3306', 'tcp', 0);
|
||||
|
||||
INSERT INTO app_volumes (template_id, host_path, container_path, mode, sort_order) VALUES
|
||||
(@mysql_id, '/opt/mysql/data', '/var/lib/mysql', 'rw', 0),
|
||||
(@mysql_id, '/opt/mysql/conf/my.cnf', '/etc/my.cnf', 'rw', 1),
|
||||
(@mysql_id, '/opt/mysql/log', '/var/log/mysql', 'rw', 2),
|
||||
(@mysql_id, '/etc/timezone', '/etc/timezone', 'ro', 3),
|
||||
(@mysql_id, '/etc/localtime', '/etc/localtime', 'ro', 4);
|
||||
|
||||
INSERT INTO app_envs (template_id, env_key, env_value, sort_order) VALUES
|
||||
(@mysql_id, 'MYSQL_ROOT_PASSWORD', '{{MYSQL_ROOT_PASSWORD}}', 0);
|
||||
|
||||
INSERT INTO app_mkdirs (template_id, dir_path, sort_order) VALUES
|
||||
(@mysql_id, '/opt/mysql/data', 0),
|
||||
(@mysql_id, '/opt/mysql/conf', 1),
|
||||
(@mysql_id, '/opt/mysql/log', 2);
|
||||
|
||||
INSERT INTO app_write_files (template_id, file_path, file_content, sort_order) VALUES
|
||||
(@mysql_id, '/opt/mysql/conf/my.cnf',
|
||||
'# MySQL 配置文件
|
||||
[mysqld]
|
||||
pid-file = /var/run/mysqld/mysqld.pid
|
||||
socket = /var/run/mysqld/mysqld.sock
|
||||
datadir = /var/lib/mysql
|
||||
secure-file-priv= NULL
|
||||
|
||||
# 字符集
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
|
||||
# 最大连接数
|
||||
max_connections = 200
|
||||
|
||||
# Innodb
|
||||
innodb_buffer_pool_size = 128M
|
||||
innodb_log_file_size = 48M
|
||||
innodb_flush_log_at_trx_commit = 1
|
||||
|
||||
# 日志
|
||||
log-error = /var/log/mysql/error.log
|
||||
slow_query_log = 1
|
||||
slow_query_log_file = /var/log/mysql/slow.log
|
||||
long_query_time = 2
|
||||
|
||||
# 端口
|
||||
port = 3306
|
||||
|
||||
[client]
|
||||
default-character-set = utf8mb4
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4', 0);
|
||||
|
||||
INSERT INTO app_params (template_id, param_key, label, type, placeholder, default_value, required, sort_order) VALUES
|
||||
(@mysql_id, 'MYSQL_ROOT_PASSWORD', 'Root 密码', 'password', '设置 root 用户密码', '', 1, 0),
|
||||
(@mysql_id, 'PORT', '宿主机端口', 'number', '3306', '3306', 1, 1);
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- 初始数据:Halo 模板
|
||||
-- ============================================================
|
||||
INSERT INTO app_templates (app_key, name, tagline, description, icon, category, image, container_name, network, restart, hostname, workdir, command, extra_args, sort_order, is_active)
|
||||
VALUES ('halo', 'Halo', '强大易用的开源建站工具',
|
||||
'Halo 是一款开源的建站工具,支持博客、CMS、企业官网等多种场景。内置主题管理、插件系统、多用户、SEO 等功能。社区版基于开源仓库构建,免费使用。',
|
||||
'halo', '建站', 'registry.fit2cloud.com/halo/halo:2.26', 'halo', '', 'always', 'halo', '', '', '{{HALO_DB_ARGS}}', 3, 1);
|
||||
|
||||
SET @halo_id = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO app_ports (template_id, host_port, container_port, protocol, sort_order) VALUES
|
||||
(@halo_id, '{{PORT}}', '8090', 'tcp', 0);
|
||||
|
||||
INSERT INTO app_volumes (template_id, host_path, container_path, mode, sort_order) VALUES
|
||||
(@halo_id, '/opt/halo/data', '/root/.halo2', 'rw', 0);
|
||||
|
||||
INSERT INTO app_envs (template_id, env_key, env_value, sort_order) VALUES
|
||||
(@halo_id, 'JVM_OPTS', '-Xmx{{JVM_MAX}} -Xms{{JVM_MIN}}', 0);
|
||||
|
||||
INSERT INTO app_mkdirs (template_id, dir_path, sort_order) VALUES
|
||||
(@halo_id, '/opt/halo/data', 0);
|
||||
|
||||
INSERT INTO app_params (template_id, param_key, label, type, placeholder, default_value, required, options, show_when, sort_order) VALUES
|
||||
(@halo_id, 'PORT', '宿主机端口', 'number', '8090', '8090', 1, NULL, NULL, 0),
|
||||
(@halo_id, 'JVM_MAX', 'JVM 最大内存', 'text', '256m', '256m', 1, NULL, NULL, 1),
|
||||
(@halo_id, 'JVM_MIN', 'JVM 初始内存', 'text', '256m', '256m', 1, NULL, NULL, 2),
|
||||
(@halo_id, 'DB_TYPE', '数据库', 'select', 'H2 内置数据库', 'h2', 1, '[{"value":"h2","label":"H2 内置数据库(零配置)"},{"value":"mysql","label":"MySQL 外部数据库"}]', NULL, 3),
|
||||
(@halo_id, 'DB_HOST', 'MySQL 地址', 'text', '127.0.0.1', '127.0.0.1', 0, NULL, 'DB_TYPE:mysql', 4),
|
||||
(@halo_id, 'DB_PORT', 'MySQL 端口', 'number', '3306', '3306', 0, NULL, 'DB_TYPE:mysql', 5),
|
||||
(@halo_id, 'DB_NAME', 'MySQL 库名', 'text', 'halodb', 'halodb', 0, NULL, 'DB_TYPE:mysql', 6),
|
||||
(@halo_id, 'DB_USER', 'MySQL 用户名', 'text', 'root', 'root', 0, NULL, 'DB_TYPE:mysql', 7),
|
||||
(@halo_id, 'DB_PASSWORD', 'MySQL 密码', 'password', '数据库密码', '', 0, NULL, 'DB_TYPE:mysql', 8);
|
||||
|
||||
-- Halo 的 extra_args ({{HALO_DB_ARGS}}) 由前端 tplWithDerived 函数动态解析,
|
||||
-- 根据 DB_TYPE 参数自动生成 MySQL 连接环境变量或留空(H2 模式)
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- 客户端版本管理表
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS app_version (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
version VARCHAR(32) NOT NULL UNIQUE COMMENT '版本号 (如 v0.0.1)',
|
||||
download_url VARCHAR(512) NOT NULL COMMENT '下载地址',
|
||||
release_notes TEXT DEFAULT NULL COMMENT '更新说明',
|
||||
is_latest TINYINT(1) DEFAULT 0 COMMENT '是否为最新版本 1=是 0=否',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户端版本管理';
|
||||
|
||||
-- 初始版本数据:v0.0.1
|
||||
INSERT INTO app_version (version, download_url, release_notes, is_latest, created_at)
|
||||
VALUES ('v0.0.1', 'https://114.66.55.63:3001/download/wfj-panel-v0.0.1.exe',
|
||||
'WFJ Panel 首个版本 v0.0.1
|
||||
|
||||
功能列表:
|
||||
- SSH 远程服务器连接管理
|
||||
- 服务器概览监控(CPU/内存/硬盘/网络/进程)
|
||||
- 远程文件管理(上传/下载/新建/删除)
|
||||
- 交互式终端(xterm.js)
|
||||
- Docker 容器管理(概览/容器列表/镜像/应用商店/任务中心/配置/网络/卷)
|
||||
- 应用商店一键部署(Redis/MySQL/Halo)
|
||||
- 系统防火墙管理(firewalld/ufw/iptables)', 1, NOW());
|
||||
@@ -0,0 +1,3 @@
|
||||
flask==3.0.3
|
||||
flask-cors==4.0.1
|
||||
mysql-connector-python==8.4.0
|
||||
Reference in New Issue
Block a user