feat: 上线前安全加固与发布工程化;appmall 由插件迁移为独立应用

安全(P0)
- 发布包签名:新增 scripts/package_keygen.php、scripts/package_verify.php、
  ywxapp\service\PackageVerifier;FrameworkService::apply() 解压前强制验签
  (ywxapp.framework_package_verify,默认开启)
- JWT 收紧:config/jwt.php 移除公开默认密钥,JwtService::assertStrongSecret()
  校验非空 / 长度≥32 / 已知弱值;.env.production.example 键名由 JWT_SECRET_KEY 修正为 JWT_SECRET
- /api/v1/Article:免登录白名单收窄为只读接口,写操作字段白名单 + assertOwner() 越权校验
- 开发者令牌:查找接口不再回显明文、统一失败文案 + 限流
- TemplateInstaller:文件名白名单 + realpath 归一化 + 目录内断言,修复 Zip Slip;补 use ZipArchive
- 后台登录:接入验证码、失败锁定与登录限流(config/ratelimit.php)
- 缓存 / Session 驱动可由 .env 覆盖(CACHE_DRIVER / SESSION_TYPE),集群部署改 redis
- paycenter 支付日志开关改为 PAY_LOG,不再跟随 APP_DEBUG

工程化 / 交付
- 新增 CHANGELOG.md 与 .env.production.example;composer.lock 入库锁定依赖版本
- 插件离线安装 CLI 增加 --uid,后台上传安装透传 uid,付费插件授权校验可通过
- 授权拒绝文案区分「未携带 uid」与「未找到有效授权」,便于排障
- .gitignore 放行 .env.production.example / .env.addon.example / .gitattributes / public/.htaccess

结构
- appmall 由 addon/appmall 迁移为独立应用 app/appmall(市场 / 开发者中心 / 审核 / 分润)
This commit is contained in:
yangwx committed 2026-10-22 00:53:39 +08:00
1 parent 1d49e6f5ee
commit 18101cf11a
636 files changed
+58517 -9519

No files matched your search

+213
View File
@@ -0,0 +1,213 @@
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
// MQTT Broker 配置项(后台"服务设置"表单数据源)
return [
[
'name' => 'port',
'title' => '监听端口',
'type' => 'number',
'value' => '1883',
'tip' => 'Broker 监听的 TCP 端口,默认 1883',
],
[
'name' => 'host',
'title' => '监听地址',
'type' => 'string',
'value' => '0.0.0.0',
'tip' => '绑定地址,0.0.0.0 表示全部网卡',
],
[
'name' => 'ws_port',
'title' => 'WebSocket端口',
'type' => 'number',
'value' => '8083',
'tip' => 'MQTT over WebSocket 端口(App/浏览器/小程序用),0 表示关闭',
],
[
'name' => 'ws_force',
'title' => 'Windows强制WebSocket',
'type' => 'select',
'options' => ['0' => '关闭', '1' => '开启'],
'value' => '1',
'tip' => 'Windows 开发机需同时启用 TCP+WebSocket 时设为「开启」(Workerman 一个进程只能监听一个端口,需开两个窗口分别运行 iot start --tcp-only 与 iot start --ws-only,并开启「启用集群桥接」互通)。生产 Linux 恒启用,无需配置',
],
[
'name' => 'ssl_enabled',
'title' => '启用TLS',
'type' => 'select',
'options' => ['0' => '关闭', '1' => '开启'],
'value' => '0',
'tip' => '对 TCP 监听启用 TLS(mqtts),需配置证书',
],
[
'name' => 'ssl_cert',
'title' => 'TLS证书路径',
'type' => 'string',
'value' => '',
'tip' => 'PEM 证书文件绝对路径(local_cert)',
],
[
'name' => 'ssl_key',
'title' => 'TLS私钥路径',
'type' => 'string',
'value' => '',
'tip' => 'PEM 私钥文件绝对路径(local_pk)',
],
[
'name' => 'allow_anonymous',
'title' => '允许匿名连接',
'type' => 'select',
'options' => ['1' => '允许', '0' => '禁止'],
'value' => '1',
'tip' => '建议保持「允许」:设备首次入网(system/device/register)不带用户名密码,禁止后无法注册。权限由 ACL 与「设备主题硬归属校验」兜住,匿名客户端拿不到任何设备数据',
],
[
'name' => 'acl_enabled',
'title' => '启用ACL',
'type' => 'select',
'options' => ['0' => '关闭', '1' => '开启'],
'value' => '1',
'tip' => '开启后按"ACL规则"表校验发布/订阅权限(建议开启)。注意:/devices/{uuid}/... 与 /notify/{account}/... 另有「硬归属校验」,只看设备绑定与共享关系,与本开关无关——未绑定的会员始终无法订阅他人设备',
],
[
'name' => 'acl_default',
'title' => 'ACL默认策略',
'type' => 'select',
'options' => ['allow' => '默认允许', 'deny' => '默认拒绝'],
'value' => 'deny',
'tip' => '所有规则都未命中时的兜底策略。建议「默认拒绝」:仅显式放行的主题可通行($SYS 只读、设备注册主题、设备私有主题的归属校验均不受此处影响)',
],
[
'name' => 'allow_member_login',
'title' => '允许框架会员登录',
'type' => 'select',
'options' => ['0' => '关闭', '1' => '开启'],
'value' => '0',
'tip' => '开启后,框架 member_user 表的会员可用其账号密码直接连接 Broker(密码算法一致),无需在"认证账号"中重复建号',
],
[
'name' => 'member_is_superuser',
'title' => '会员默认超管',
'type' => 'select',
'options' => ['0' => '否', '1' => '是'],
'value' => '0',
'tip' => '会员直通连接是否视为超级用户(跳过 ACL);建议保持"否",用 ACL 规则按 %u 隔离',
],
[
'name' => 'max_keepalive',
'title' => '最大保活间隔(秒)',
'type' => 'number',
'value' => '60',
'tip' => '超过该间隔无心跳则断开',
],
[
'name' => 'max_connections',
'title' => '最大连接数',
'type' => 'number',
'value' => '5000',
'tip' => '单进程并发连接硬上限,超出拒绝新连接防止内存雪崩(0=不限)',
],
[
'name' => 'publish_rate_limit',
'title' => '单连接发布限流(条/秒)',
'type' => 'number',
'value' => '50',
'tip' => '令牌桶限流,单连接每秒最多发布数,超出静默丢弃(0=不限)',
],
[
'name' => 'sys_enabled',
'title' => '启用$SYS主题',
'type' => 'select',
'options' => ['1' => '开启', '0' => '关闭'],
'value' => '1',
'tip' => '周期发布 $SYS/broker/# 运行指标,可被客户端订阅',
],
[
'name' => 'sys_interval',
'title' => '$SYS发布间隔(秒)',
'type' => 'number',
'value' => '10',
'tip' => '系统指标发布周期',
],
[
'name' => 'persist_retain',
'title' => '持久化保留消息',
'type' => 'select',
'options' => ['1' => '开启', '0' => '关闭'],
'value' => '1',
'tip' => '重启后从数据库恢复保留消息',
],
[
'name' => 'persist_offline',
'title' => '持久化离线消息',
'type' => 'select',
'options' => ['1' => '开启', '0' => '关闭'],
'value' => '1',
'tip' => 'clean=false 会话离线期间的 QoS1/2 消息入库,重连补投',
],
[
'name' => 'offline_limit',
'title' => '离线队列上限',
'type' => 'number',
'value' => '1000',
'tip' => '单客户端离线消息最大条数,超出丢弃最旧',
],
[
'name' => 'cluster_enabled',
'title' => '启用集群桥接',
'type' => 'select',
'options' => ['0' => '关闭', '1' => '开启'],
'value' => '0',
'tip' => '开启后经 Redis Stream 打通多进程/WebSocket 与 TCP 消息(Linux 多进程、Windows 双进程联调都必须开启)',
],
[
'name' => 'rule_enabled',
'title' => '启用转发规则',
'type' => 'select',
'options' => ['1' => '开启', '0' => '关闭'],
'value' => '1',
'tip' => '开启后按"转发规则"匹配源主题并桥接到外部 Broker(如 EMQX),需 workerman/mqtt',
],
[
'name' => 'redis_host',
'title' => 'Redis地址',
'type' => 'string',
'value' => '127.0.0.1',
'tip' => '集群桥接用 Redis 主机',
],
[
'name' => 'redis_port',
'title' => 'Redis端口',
'type' => 'number',
'value' => '6379',
'tip' => 'Redis 端口',
],
[
'name' => 'redis_db',
'title' => 'Redis库',
'type' => 'number',
'value' => '0',
'tip' => 'Redis 数据库序号',
],
[
'name' => 'redis_password',
'title' => 'Redis密码',
'type' => 'string',
'value' => '',
'tip' => '无密码留空',
],
[
'name' => 'log_level',
'title' => '日志级别',
'type' => 'select',
'options' => ['0' => '关闭', '1' => '仅错误', '2' => '详细'],
'value' => '1',
'tip' => '写入 runtime 日志的详细程度',
],
];