Files
YwxAppThink/app/backend/view/console/index.html
T
yangwx 18101cf11a 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(市场 / 开发者中心 / 审核 / 分润)
2026-10-22 00:53:39 +08:00

303 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="layui-fluid">
<!-- 系统概览:与「系统信息」页同源(app\backend\library\SystemInfo),控制台只做展示 -->
<div class="layui-row layui-col-space15">
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card"><div class="layui-card-body">
<div style="color:#666">管理员</div>
<div style="font-size:24px;font-weight:600">{$sys.stat.admin}</div>
</div></div>
</div>
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card"><div class="layui-card-body">
<div style="color:#666">插件</div>
<div style="font-size:24px;font-weight:600">{$sys.stat.addon}</div>
</div></div>
</div>
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card"><div class="layui-card-body">
<div style="color:#666">附件</div>
<div style="font-size:24px;font-weight:600">{$sys.stat.attachment}</div>
<div style="color:#999;font-size:12px">占用 {$sys.stat.attach_size_text}</div>
</div></div>
</div>
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card"><div class="layui-card-body">
<div style="color:#666">数据库</div>
<div style="font-size:24px;font-weight:600">{$sys.stat.db_size_text}</div>
</div></div>
</div>
</div>
<div class="layui-row layui-col-space15">
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-header">运行环境</div>
<div class="layui-card-body">
<table class="layui-table layui-table-sm" lay-skin="nob">
<tbody>
{foreach $sys.env as $row}
<tr>
<td style="width:120px;color:#666">{$row[0]}</td>
<td>{$row[1]}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
<div class="layui-card">
<div class="layui-card-header">磁盘</div>
<div class="layui-card-body">
<div class="layui-progress layui-progress-big" lay-showPercent="true">
<div class="layui-progress-bar" style="width:{$sys.disk.percent}%"></div>
</div>
<div style="margin-top:10px;color:#666">
已用 {$sys.disk.used_text} / 共 {$sys.disk.total_text},剩余 {$sys.disk.free_text}
</div>
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-header">目录权限</div>
<div class="layui-card-body">
<table class="layui-table layui-table-sm" lay-skin="nob">
<thead><tr><th>目录</th><th>存在</th><th>可写</th></tr></thead>
<tbody>
{foreach $sys.dirs as $d}
<tr>
<td>{$d.name}</td>
<td>{if $d.exists}<span class="layui-badge layui-bg-green">是</span>{else}<span class="layui-badge">否</span>{/if}</td>
<td>{if $d.writable}<span class="layui-badge layui-bg-green">可写</span>{else}<span class="layui-badge layui-bg-red">不可写</span>{/if}</td>
</tr>
{/foreach}
</tbody>
</table>
<div style="color:#999;font-size:12px">不可写的目录会导致缓存无法生成、上传失败、插件无法安装。</div>
</div>
</div>
<div class="layui-card">
<div class="layui-card-header">维护</div>
<div class="layui-card-body">
<div class="layui-btn-group">
<button class="layui-btn layui-btn-normal" id="sys-clean-cache" data-perm="console:clean">清理数据缓存</button>
<button class="layui-btn" id="sys-clean-temp" data-perm="console:clean">清理模板缓存</button>
<button class="layui-btn layui-btn-warm" id="sys-clean-all" data-perm="console:clean">全量清理</button>
</div>
<div style="margin-top:10px;color:#999;font-size:12px">
只清理 runtime/cache 与 runtime/temp;<strong>不会删除 runtime/log</strong>(操作日志与错误日志保留)。
</div>
<div style="margin-top:12px;color:#666">
近 7 天操作 <strong>{$sys.stat.log_week}</strong> 条,其中登录失败 <strong style="color:#ff5722">{$sys.stat.login_fail}</strong> 次。
</div>
</div>
</div>
<!-- 框架升级:与「系统管理 → 框架升级」同源,共用 /admin/framework/check|upgrade -->
<div class="layui-card">
<div class="layui-card-header">框架升级</div>
<div class="layui-card-body">
<p style="margin-bottom:8px">当前版本:<span class="layui-badge layui-bg-blue" id="fw-current">{$version}</span></p>
<div id="fw-status" class="layui-text" style="margin:10px 0;min-height:24px;"></div>
<div id="fw-pkg-box" style="display:none;margin:10px 0;padding:10px 12px;background:#fafafa;border:1px solid #eee;border-radius:4px;">
<div style="margin-bottom:8px;color:#555;">升级包类型:</div>
<div class="layui-btn-group" id="fw-pkg-group">
<button type="button" class="layui-btn layui-btn-sm layui-btn-normal fw-pkg" data-type="auto">自动(推荐)</button>
<button type="button" class="layui-btn layui-btn-sm layui-btn-primary fw-pkg" data-type="full">完整包</button>
<button type="button" class="layui-btn layui-btn-sm layui-btn-primary fw-pkg" data-type="patch">增量小包</button>
</div>
<div id="fw-pkg-tip" style="margin-top:8px;color:#e67700;font-size:13px;"></div>
</div>
<div class="layui-btn-group">
<button class="layui-btn layui-btn-normal" id="fw-check" data-perm="framework:check">
<i class="layui-icon layui-icon-refresh"></i> 检查更新
</button>
<button class="layui-btn layui-btn-danger" id="fw-upgrade" data-perm="framework:upgrade" style="display:none;">
<i class="layui-icon layui-icon-release"></i> 立即升级
</button>
</div>
<div style="margin-top:10px;color:#999;font-size:12px">
升级前会自动备份当前核心文件;本卡片与「系统管理 → 框架升级」是同一套接口与权限。
</div>
</div>
</div>
</div>
</div>
<div class="layui-row layui-col-space15">
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
会员总数
<span class="layui-badge layui-bg-blue layuiadmin-badge">总</span>
</div>
<div class="layui-card-body layuiadmin-card-list">
<p class="layuiadmin-big-font">{$stats.total.member}</p>
<p>
近 7 天新增
<span class="layuiadmin-span-color">{$stats.total.member_week} 人 <i class="layui-inline layui-icon layui-icon-user"></i></span>
</p>
</div>
</div>
</div>
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
今日新增会员
<span class="layui-badge layui-bg-green layuiadmin-badge">日</span>
</div>
<div class="layui-card-body layuiadmin-card-list">
<p class="layuiadmin-big-font">{$stats.today.member}</p>
<p>
会员表实时统计
<span class="layuiadmin-span-color">今日 <i class="layui-inline layui-icon layui-icon-friends"></i></span>
</p>
</div>
</div>
</div>
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
今日操作
<span class="layui-badge layui-bg-cyan layuiadmin-badge">日</span>
</div>
<div class="layui-card-body layuiadmin-card-list">
<p class="layuiadmin-big-font">{$stats.today.log}</p>
<p>
后台操作记录
<span class="layuiadmin-span-color">累计 {$stats.total.log} <i class="layui-inline layui-icon layui-icon-log"></i></span>
</p>
</div>
</div>
</div>
<div class="layui-col-sm6 layui-col-md3">
<div class="layui-card">
<div class="layui-card-header">
今日蜘蛛来访
<span class="layui-badge layui-bg-orange layuiadmin-badge">日</span>
</div>
<div class="layui-card-body layuiadmin-card-list">
<p class="layuiadmin-big-font">{$stats.today.spider}</p>
<p>
搜索引擎抓取
<span class="layuiadmin-span-color">累计 {$stats.total.spider} <i class="layui-inline layui-icon layui-icon-chart-screen"></i></span>
</p>
</div>
</div>
</div>
<div class="layui-col-sm12">
<div class="layui-card">
<div class="layui-card-header">
近 7 天趋势
<span class="layui-badge layui-bg-blue layuiadmin-badge">会员注册 / 后台操作</span>
</div>
<div class="layui-card-body">
<div class="layui-carousel layadmin-carousel layadmin-dataview" data-anim="fade" lay-filter="LAY-index-trendview">
<div carousel-item id="LAY-index-trend">
<div><i class="layui-icon layui-icon-loading1 layadmin-loading"></i></div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-sm6">
<div class="layui-card">
<div class="layui-card-header">最新会员</div>
<div class="layui-card-body">
<table class="layui-table layuiadmin-page-table" lay-skin="line">
<thead>
<tr>
<th>账号</th>
<th>昵称</th>
<th>注册时间</th>
</tr>
</thead>
<tbody>
{empty name="stats.members"}
<tr><td colspan="3" style="text-align:center;color:#999;">暂无数据</td></tr>
{/empty}
{foreach $stats.members as $m}
<tr>
<td>{$m.account}</td>
<td>{$m.nickname}</td>
<td>{$m.create_at|date='m-d H:i'}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
</div>
<div class="layui-col-sm6">
<div class="layui-row layui-col-space15">
<div class="layui-col-sm12">
<div class="layui-card">
<div class="layui-card-header">最近操作日志</div>
<div class="layui-card-body">
<table class="layui-table layuiadmin-page-table" lay-skin="line">
<thead>
<tr>
<th>管理员</th>
<th>操作</th>
<th>IP</th>
<th>时间</th>
</tr>
</thead>
<tbody>
{empty name="stats.logs"}
<tr><td colspan="4" style="text-align:center;color:#999;">暂无数据</td></tr>
{/empty}
{foreach $stats.logs as $l}
<tr>
<td>{$l.admin}</td>
<td>{$l.action}</td>
<td>{$l.ip}</td>
<td>{$l.time|date='m-d H:i'}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/assets/layui/layui.js?t=1"></script>
<script type="text/javascript">
// 本页会再次加载 layui,覆盖掉 layout 里注入的 layui.app(含当前登录者权限集 powers)。
// 必须在这里补回来:auth 模块在加载时读 layui.app.powers,拿不到就会把
// 所有带 [data-perm] 的按钮当成无权限移除(控制台的清理按钮会消失)。
layui.app = {
root: "{$site.root|default=''}",
assetUrl: "/assets",
module: "{$site.module}",
app: "{$site.app}",
routeBase: "{$route_base}",
controller: "{$site.controller}",
action: "{$site.action}",
powers: {:json_encode($powers ?? [])}
};
</script>
<script type="text/javascript">
// 首页下半区真实统计(Console::collectStats 产出),给 console.js 画趋势图用。
// json_encode 带 HEX_* 选项:模板 default_filter 会把 <>&'" 转义成实体,
// 先转成 \u003C 这类 unicode 才能保证丢到 JS 里仍是合法 JSON。
// 第二个参数 15 = JSON_HEX_TAG|HEX_AMP|HEX_APOS|HEX_QUOT(这里不能写竖线,模板把它当修饰符分隔符)
layui.stats = {:json_encode($stats ?? [], 15)};
</script>
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
<script>
layui.use('console');
</script>