安全(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(市场 / 开发者中心 / 审核 / 分润)
96 lines
3.8 KiB
JavaScript
96 lines
3.8 KiB
JavaScript
layui.define(['http', 'auth', 'table', 'layer'], function (exports) {
|
||
var table = layui.table,
|
||
layer = layui.layer,
|
||
auth = layui.auth,
|
||
http = layui.http;
|
||
|
||
// 权限控制器前缀:与后端 BackendPower.name(articles:backend:category:add 等)对齐
|
||
auth.setController('articles:backend:category');
|
||
auth.render();
|
||
|
||
// URL 由浏览器按当前页面所在目录解析(页面即 /articles/backend/category/index.html),
|
||
// 所以只写动作名;带前缀会拼成 .../category/articles/backend.category/index(404)。
|
||
var FUNC = {
|
||
index: 'index',
|
||
delete: 'delete'
|
||
};
|
||
|
||
var obj = {
|
||
init: function () {
|
||
this.render();
|
||
this.events();
|
||
},
|
||
|
||
render: function () {
|
||
table.render({
|
||
elem: '#table',
|
||
url: FUNC.index,
|
||
toolbar: '#toolbar',
|
||
defaultToolbar: ['filter', 'print', 'exports'],
|
||
method: 'get',
|
||
cols: [[
|
||
{field: 'id', title: 'ID', width: 70, sort: true},
|
||
{field: 'name', title: '名称', width: 150},
|
||
{field: 'alias', title: '别名', width: 150},
|
||
{field: 'description', title: '描述'},
|
||
{field: 'sort', title: '排序', width: 80, sort: true},
|
||
{field: 'status', title: '状态', width: 90, templet: '#statusTpl'},
|
||
{field: 'create_at', title: '创建时间', width: 170, templet: function (d) {
|
||
return d.create_at ? layui.util.toDateString(d.create_at * 1000, 'yyyy-MM-dd HH:mm:ss') : '';
|
||
}},
|
||
{title: '操作', width: 130, templet: '#action', fixed: 'right'}
|
||
]],
|
||
page: true,
|
||
limit: 10,
|
||
limits: [10, 20, 50, 100],
|
||
response: {statusCode: 0},
|
||
parseData: function (res) {
|
||
return {
|
||
code: res.code,
|
||
msg: res.msg,
|
||
count: res.count,
|
||
data: res.data
|
||
};
|
||
}
|
||
});
|
||
},
|
||
|
||
events: function () {
|
||
// 顶部工具条
|
||
table.on('toolbar(table)', function (obj) {
|
||
var event = obj.event;
|
||
if (event === 'add') {
|
||
if (!auth.has('add')) return layer.msg('无权限', {icon: 2});
|
||
window.location.href = '/articles/backend.category/edit';
|
||
}
|
||
});
|
||
|
||
// 行内操作
|
||
table.on('tool(table)', function (obj) {
|
||
var data = obj.data,
|
||
event = obj.event;
|
||
if (event === 'edit') {
|
||
if (!auth.has('edit')) return layer.msg('无权限', {icon: 2});
|
||
window.location.href = '/articles/backend.category/edit/id/' + data.id;
|
||
} else if (event === 'del') {
|
||
if (!auth.has('delete')) return layer.msg('无权限', {icon: 2});
|
||
layer.confirm('确定删除该分类吗?', {title: '提示'}, function (index) {
|
||
http.delete(FUNC.delete, {id: data.id}, function (res) {
|
||
if (res.code === 0) {
|
||
layer.msg(res.msg || '删除成功', {icon: 1});
|
||
table.reload('table');
|
||
} else {
|
||
layer.msg(res.msg || '删除失败', {icon: 2});
|
||
}
|
||
});
|
||
layer.close(index);
|
||
});
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
obj.init();
|
||
exports('category', obj);
|
||
});
|