admin库

就目前为止, lua生态内唯一的一款通用Web后台管理系统开发库.

起因

开发者在使用各类Lua后端开发框架都时候, 业务的繁杂无意占了中间许多开发时间.

即使是不同的项目也都会遇到一个问题: 需要一个后台管理系统来对相关数据进行统计、修改、展示.

可惜的是: 截止到目前为止的后端框架较多, lua生态内尚没有一款集成的通用后台解决方案可以较为便捷的.

作者在与业内一些开发者沟通后, 发现大多数Lua开发者的后台的解决方法如下:

  • 前、后端分离解决方案, 如: iview-admin/vue-admin、React等.

  • 开发者提供数据接口, 由司内后台开发部门专门对接进行开发.

各类开发模式上的优劣无需讨论, 单单对lua开发者技能领域、技能涉及面、经验等都是比较大的挑战. 也从侧面反映出lua在后台页面开发确实是少有的一块短板, 目前市面上也暂无一个较为可靠的解决方案.

那么, 能否在此基础之上为lua的使用者也提供快速后台开发模板呢?

admin库的优势

admin库由layui + X-admin + lua-resty-template + cf httpd库提供底层支持.

admin库支持双语种支持, 用户在开发的时候建议使用locale的key作为相关名称,

admin基于RBAC进行权限管理, 提供了一套基于角色的菜单权限控制.

admin库为使用者的view内置了权限验证, render开发更加安全、简单.

admin库支持菜单栏与导航栏项目的增加与删除, 方便开发者进行相关功能的关联.

admin库最多支持三级菜单, 菜单的icon可以直接填写layui的unicode.

admin库提供了cdn支持, 用户可以轻松将一些静态文件发布到各类CDN平台.

admin库提供了很多配置参数, 如:设置默认语言、添加语言项、设置home首页、开启模板缓存等等.

开始使用admin库之前

请按照以下步奏初始化数据库:

  • 创建一个数据库(名字任意), 并且保持charset一致;

  • 打开lualib/db/database.sql文件, 复制里面的SQL语句在GUI工具中执行一次创建表;

admin API介绍

1. admin.init_db()

此方法将会初始化超级管理员用户与管理员用户组, 账户、密码都是:admin

2. admin.init_home(url)

此方法将设置dashboard的home页, home的地址就是url.

3. admin.init_page(app, db)

此方法将会构建admin库所用到的所有页面、数据接口.

4. admin.static(domain)

此方法可以用来设置静态文件域名前缀, 默认是:http::/localhost:8080/, 结尾一定要加上'/'.

5. admin.cached()

此方法用来确认是否开启全局模板缓存. 开发阶段不要开启! 这个参数可能会导致html模板缓存需要重启才能更新.

此方法用来设置admin注入的Cookie超时时间, 可以认为这个时间就是用户登录时间. 单位为: sec.

7. admin.set_locale(lang)

此方法用来设置默认显示语言.(用户自行切换语言后, 浏览器将会记住用户选择语言)

8. admin.add_locale_item(lang, lang_locale_itemt)

  1. admin.add_locale_item('ZH-CN', {
  2. {'login.form.title', '这是登录页Title'},
  3. {'dashboard.header.logo', '仪表盘 Logo'},
  4. })
  5. admin.add_locale_item('EN-US', {
  6. {'login.form.title', 'This is Login Page Title'},
  7. {'dashboard.header.logo', 'dashboard Logo'},
  8. })

此方法用于增加/覆盖现有admin/locale内的语言key-value字典表. 通常用于多语言设置

9. admin.display_lang(boolean)

此方法用来设置dashboard页面的语言切换标签是否显示(默认为显示: true or flase).

view介绍

ctx对象

会在被传入一个httpctx对象, 该对象内置了一些方法:

  • ctx:get_method, 用户获取客户端请求方法.

  • ctx:get_args, 获取客户端传参.

  • ctx:get_path 和 ctx:get_raw_path, 获取请求路径与获取原始请求路径(原始请求路径不会去掉'/admin?a=2&b=2'的参数)

  • ctx:get_headers, 获取请求头部. key-value表, value永远为string.

db对象

db对象为DB.init初始化后的对象, 使用方法请参考DB

1. view.use(path, function(ctx, db) … end)

此方法类似httpd:use, 从这里开始, 我们统一称use路由为页面路由.

path为路由路径, function为路由回调, 开发者的相关逻辑代码都将在此函数内编写.

默认情况下, '/admin/'前缀开头的页面路由权限验证. 没有权限的使用者将会被重定向到登录页面.

代码示例:

  1. local view = require "admin.view"
  2. view.use('/admin/test', function (ctx, db)
  3. return "this request method = " .. ctx:get_method()
  4. end)

2. view.api(path, function(ctx, db) … end)

此方法类似httpd:api, 从这里开始, 我们统一称api路由为接口路由.

path为路由路径, function为路由回调, 开发者的相关逻辑代码都将在此函数内编写.

默认情况下, '/api/admin'前缀的数据路由会验证header内是否有token. 没传或无效会导致接口返回401/403.

代码示例:

  1. local json = require "json"
  2. local view = require "admin.view"
  3. view.api('/api/admin/test', function (ctx, db)
  4. return json.encode({
  5. code = 200, method = ctx:get_method()
  6. })
  7. end)

3. view.get_locale()

此方法在页面路由中使用, 获取当前用户使用的语言

4. view.get_cdn()

此方法返回admin.static设置的值. 方便使用者配置静态文件前缀.

5. view.template(path){…}

template即lua-resty-template库的渲染方法. 效果等同于template.compile.

path参数即可以作为html模板路径字符串, 如:"view/tp.html". 也可以作为模板字符串如法, 如:"<h1>{{title}}</h1>"

此方法返回一个函数, 函数的参数一个table作为渲染参数. 函数返回值为path渲染后的html内容.

您可以写成这样:

  1. local view = require "admin.view"
  2. view.use('/admin/test', function (ctx, db)
  3. return view.template([[<h1>{*title*}</h1>]]){
  4. title = '这是title!'
  5. }
  6. end)

但是, 这样每次修改模板需要重启cfadmin. 为了每次仅修改html模板时能即时生效, 作者推荐使用者将模板与渲染代码分离.

例如, 您可以写成这样:

  1. -- main.lua
  2. local view = require "admin.view"
  3. view.use('/admin/test', function (ctx, db)
  4. return view.template('view/tp.html'){
  5. title = '这是title!'
  6. }
  7. end)
  1. <!-- view/tp.html -->
  2. <h1>{*title*}</h1>

上述2种写法无任何差异, 唯一的区别仅仅是模块化设计的区别

浏览器打开http://localhost:8080/admin/test, 即可查看渲染效果.

更多template模板语法, 请在这里查看.

utils介绍

utils内置一些比较常见的方法以供大家使用.

utils导入方式

  1. local utils = require "admin.utils"

1. utils.redirect(location, args)

此方法返回一个重定向的html页面, location为地址字符串(必填), args参数是一个参数数组表(可选).

由于cf框架的httpd实现未提供内置请求重定向方法, 所以为使用者提供一个页面路由请求重定向传参的途径.

假设location为"/admin", args为{{a, 1}, {b, 2}}. 实际重定向到地址为: /admin?a=1&b=2.

2. utils.error_404(location)

此方法返回一个404页面, 参数location为404页面跳转路径(不设置则跳转到cf框架首页).

如何使用cf搭建最简单的lua后台开发环境?

作者为开发者提供了一份代码示例在script/test_cfadmin.lua, 使用者可参考此处进行搭建开发环境.

如何快速对cfadmin后台模板进行二次开发?

为了统一大家习惯. 所有对cfadmin后台的功能增加、修改、删除我们统称为:"二次开发".

1. 快速添加一级菜单

开发者在登录成功后将会看到系统管理, 展开后将会看到"菜单管理"项, 点击"菜单管理"后可以进行即可按照实际情况新建一个菜单.

在新建菜单之前, 我们需要使用view.use建立一个页面路由:

  1. local view = require "admin.view"
  2. view.use('/test', function (ctx, db)
  3. return "<h1>Hello World!</h1>"
  4. end)

建立完成之后, 我们可以开始新建菜单了. 新建菜单可不是那么简单, 它至少需要你确认做以下三件事:

  1. 1. 菜单名称(不能重复);
  2. 2. 菜单url(不能为空);
  3. 3. 菜单icon(需要参考layui图标unicode);

请注意. 菜单url必须对应到您的view.use创建的路由path. 否则将会出现404错误.

在菜单与路由在新建完成之后请刷新当前页面, 如果一切没有问题的话您就可以在左侧菜单栏内看到新建的菜单了.

这时候我们点击新建的菜单项, 就可以看到一个大大多"Hello World"了. 怎么样是不是非常简单?非常Cool?

2. 添加子菜单

如果您参照上述步骤添加完菜单之后, 就会开始思考对菜单进行分级. 没错! 一级菜单一般会作为一个总得分类项(例如系统管理)陈列.

当您项为某项菜单增加子菜单项的时候, 点击该菜单后面的增加"增加菜单". 其它内容按照上一章的内容规则新建菜单与路由即可.

注意:

  1. 1. 最多添加三级菜单, 过多的(多级)菜单项对管理还是整合都是无意义的.
  2. 2. 菜单管理项目内当你添加到三级菜单后, 将不会出现增加子菜单按钮.

3. 菜单对应权限

当您的菜单功能开发、测试完毕之后, 可能就会开始考虑"菜单权限分配".

admin库是基于RBAC建立的用户角色权限管理, 分离了用户(实体)与菜单(menu)、权限(permissions)之间的管理.

admin库对/api/admin/+的接口路由醉了一层header token验证, 如http request header无token字段则会返回401/403.

admin库对/admin/+的页面路由醉了一层cookie验证, 如cookie无效则会重定向到登录页面.

现在假设我们有如下菜单列表:

  1. |-分析菜单
  2. |——分析用户
  3. |——分析数据
  4. |——分析金额

完成上述列表功能后, 进入"角色管理"开始新建一个"分析角色". 并展开菜单列表在对应菜单前面打'√'. 这样就建立了一个角色. (修改同理)

新建完角色之后, 到'用户管理'页面新建一个用户. 角色选择为"分析角色", 其它内容根据实际情况填写然后提交. (修改同理)

尝试使用新创建的用户登录后台, 这样就可以在页面上看到分析菜单并且进行操作.

流程图

请求流程图

权限图

最后

  • 新建菜单、导航项成功之后手动刷新当前页面即可生效.

  • 修改用户信息与密码会导致该用户重新登录.

  • 如果使用admin库, 需要开启httpd的cookie解析功能并且推荐手动设置一下Cookie的secure值, 以防止Cookie被破解.

  • 请根据当前的环境合理的设置admin.cached与httpd:static.

  • 如果在使用期间遇到读取静态文件缓慢, 请考虑分离静态文件(使用nginx做静态文件服务器或上传到CDN).

  • 其它待补充.