• likeadmin部署


    以下内容写于2023年9月17日,likeadmin版本

    1.登录页404,且无法登录

    参照官方教程部署后,访问登录页,能打开但提示404,点登录也是404,在issues中搜到新搭建的环境,登录管理后台,报request failed with status code 404 · Issue #I79LNI · 码多多AI/likeadmin(Python版) - Gitee.com,里面有篇likeadmin抢险体验_热心的裴同学的博客-CSDN博客,照着配完后,能登录,感谢这两位大佬。

    2.登录页的图片加载不出来,网站ico也加载不出来

    在issues中搜到部署完成之后 图片无法加载,哪位遇到,指点一下,谢谢 · Issue #I7ESYX · 码多多AI/likeadmin(Python版) - Gitee.com,里面说了几点

    看下路径里面有没有图片

    图片端口不对,后台端口应该是8000(具体多少看你的配置),如果是80那没问题

    还有就是协议,本地没有证书,不是https

    解决了,的确是,后端端口是8000,env文件中的端口配置的是80,所以访问不了

    第一点图片文件我看了存在;第二点我看图片请求的80端口,而后端默认8000端口(就是python asgi.py启动后的端口) ,所以要把server文件夹中.env文件中DOMAIN中80改成8000;重启后图片还是不显示,接着第三点把DOMAIN中https改成http,重启后图片可以加载了。感谢这三位大佬。

    3.列表页接口404

    例如后台中点文章管理,提示404,前端请求的http://127.0.0.1:8000/article/api/article/list?pageNo=1&pageSize=15&title=&cid=&isShow=,但后端接口实际是http://127.0.0.1:8000/api/article/list?pageNo=1&pageSize=15&title=&cid=&isShow=,不知道为什么前端请求时端口号后面多了article。

    方法一

    解决方法是前面likeadmin抢险体验_热心的裴同学的博客-CSDN博客中说的改index.ts中baseUrl,但那篇文章作者是改成空字符串了,咨询前端同事后我这里实际应该改成http://127.0.0.1:8000,这个值的作用类似前端上传图片时用全局baseUrl和图片的相对路径拼接得到图片的绝对路径(只是类似,实际不一样),上线时要改成公网ip和端口,可能还有https。

    方法二

    index.ts中urlPrefix改为'/api',因为上面那篇文章中admin/vite.config.ts中设置了proxy代理/api,所以前缀匹配值urlPrefix要修改为/api。上线时修改admin/vite.config.ts中defineConfig()中server中proxy中的target为公网ip和端口,可能还有https。

    4.自定义HTTP异常状态码和HTTP异常返回值

    fastapi中向客户端返回HTTP错误响应,可以使用HTTPException

    文档中示例代码

    1. from fastapi import FastAPI, HTTPException
    2. app = FastAPI()
    3. items = {"foo": "The Foo Wrestlers"}
    4. @app.get("/items/{item_id}")
    5. async def read_item(item_id: str):
    6. if item_id not in items:
    7. raise HTTPException(status_code=404, detail="Item not found")
    8. return {"item": items[item_id]}

    但我在likeadmin中用同样的写法,想返回401登录失败,触发异常时postman显示

    1. {
    2. "code": 500,
    3. "msg": "系统错误",
    4. "data": []
    5. }

    然后把raise改成return,响应变成

    1. {
    2. "code": 200,
    3. "msg": "成功",
    4. "data": {
    5. "status_code": 401,
    6. "detail": "登录失败",
    7. "headers": null
    8. }
    9. }

    想起来likeadmin中不带token访问需要token的接口时提示

    1. {
    2. "code": 322,
    3. "msg": "token参数为空",
    4. "data": []
    5. }

    这是我想要的响应格式,但不知道怎么实现,请教朋友后,他说可以全局搜索"token参数为空"这几个字,看likeadmin怎么实现的。likeadmin的前后端代码中都能搜到,改的话最好同步修改,比如新增一个状态码时,前后端代码中都新增,下面以后端代码为例,后端代码只搜到一处。 

     

     然后搜索TOKEN_EMPTY,进入搜索结果中的第一个

     

    发现它使用了AppException,按住Ctrl键并单击它,进去看看

     

     也就是说,先在likeadmin/server/like/http_base.py中添加自定义的HTTP异常状态码

    1. class HttpResp:
    2. LOGIN_DENY = HttpCode(401, '登录失败') # 客户端面板未在允许登录的时间段内登录

     然后使用时

    1. from like.exceptions.base import AppException
    2. from like.http_base import HttpCode, HttpResp
    3. def test():
    4. raise AppException(HttpResp.LOGIN_DENY) # 可以给AppException传入msg参数,是自定义的报错内容提示,如果不传那提示就是HttpResp中的默认值

     

    不知道为什么fastapi的HTTPException在likeadmin中不行。

    最后补充下前端需要同步修改的文件:likeadmin/admin/src/enums/requestEnums.ts和/likeadmin/admin/src/utils/request/index.ts,还搜到了uniapp的,这里就不列出了。 

    5.新建账号后登录成功,工作台页面提示无相关权限

    新建非超级管理员账号,设置角色,并且角色对应的权限全部勾选,然后登录这个账号,登录成功自动跳转到工作台,页面提示无相关权限。

    全局搜索无相关权限,发现定义在http_base.py,对应NO_PERMISSION,再全局搜索NO_PERMISSION,发现在like/dependencies/verify.py中verify_token()末尾用了,下面只放出verify_token()的部分原始源码,这些代码是和解决这个问题有关的。

    1. async def verify_token(request: Request):
    2. """登录状态及权限校验依赖项"""
    3. role_ids = mapping.get('role_ids')
    4. menus = []
    5. # 校验角色权限是否存在
    6. for role_id in role_ids:
    7. if not await RedisUtil.hexists(AdminConfig.backstage_roles_key, role_id):
    8. await SystemAuthPermService.cache_role_menus_by_role_id(role_id)
    9. menus.extend(await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id))
    10. # 验证是否有权限操作
    11. if not (menus and auths in menus.split(',')):
    12. raise AppException(HttpResp.NO_PERMISSION)

    还有上面代码中调用的cache_role_menus_by_role_id()的原始源码,在like/admin/service/system/auth_perm.py中。

    1. async def cache_role_menus_by_role_id(cls, role_id: int):
    2. """缓存角色菜单"""
    3. auth_perms = await db.fetch_all(
    4. system_auth_perm.select().where(system_auth_perm.c.role_id == role_id))
    5. menu_ids = [i.menu_id for i in auth_perms]
    6. menus = []
    7. if menu_ids:
    8. auth_menus = await db.fetch_all(
    9. system_auth_menu.select().where(
    10. system_auth_menu.c.is_disable == 0,
    11. system_auth_menu.c.id in menu_ids,
    12. system_auth_menu.c.menu_type in ['C', 'A'])
    13. .order_by(system_auth_menu.c.menu_sort, system_auth_menu.c.id))
    14. menus = [i.perms for i in auth_menus if i.perms]
    15. await RedisUtil.hset(AdminConfig.backstage_roles_key, str(role_id), ','.join(menus))

    后来才知道,上面这两处原始代码都有错误。

    我先在verify_token()的for循环结束后打印menus,写文章时忘了它是空列表还是空字符串,反正是空的。然后打印RedisUtil.hexists(AdminConfig.backstage_roles_key, role_id)和RedisUtil.hget(AdminConfig.backstage_roles_key, role_id),分别是True和空字符串。还打印了auths,写文章时忘了当时是index:console还是common:index,反正这俩都不对,是后来才知道它不对。

    我新建账号时已经设了角色和权限,menus中竟然没有,咨询朋友后得知问题可能出在verify_token()中"校验角色权限是否存在"这个for循环中。那就退出登录,清空redis,再登一次新建的账号,看看redis中是什么。由于写文章时这个问题已修复,所以这张图没法复现提供了,就是redis中有一条数据key是2,value是空字符串,2是我当前用户的id,value的内容就是我这个号拥有的权限。

    for循环中有个判断,hexists不存在时会向redis写入数据,于是手动从redis中删除前面说的key为2的那条数据,再刷新网页,再刷新redis,发现有了key为2的数据,value仍然是空字符串。那说明是向redis写入数据时有问题,所以看cache_role_menus_by_role_id()。

    我打印了cache_role_menus_by_role_id()末尾的menus,写文章时忘了当时它是什么值,可能是空列表或空字符串,反正有问题。然后打印auth_menus,这是执行SQL语句的结果,没查到数据。然后修改auth_menus成下面的样子(省略了order_by,排序不重要),这样可以打印出它对应的SQL语句,有其他打印SQL语句的方法请告诉我。

    1. auth_menus = system_auth_menu.select().where(
    2. system_auth_menu.c.is_disable == 0,
    3. system_auth_menu.c.id in menu_ids,
    4. system_auth_menu.c.menu_type in ['C', 'A']).__str__()
    5. print(auth_menus)
    1. SELECT la_system_auth_menu.id, la_system_auth_menu.pid, la_system_auth_menu.menu_type, la_system_auth_menu.menu_name, la_system_auth_menu.menu_icon, la_system_auth_menu.menu_sort, la_system_auth_menu.perms, la_system_auth_menu.paths, la_system_auth_menu.component, la_system_auth_menu.selected, la_system_auth_menu.params, la_system_auth_menu.is_cache, la_system_auth_menu.is_show, la_system_auth_menu.is_disable, la_system_auth_menu.create_time, la_system_auth_menu.update_time
    2. FROM la_system_auth_menu
    3. WHERE false

    发现where false,那当然查不到数据了。然后仔细看它的代码,发现用的in,而likeadmin用的SQLAlchemy(增删改查参考链接1),它的in应该写成.in_(),注意in前有一个点,括号里传入列表或元组,其他支持的类型可以点进去看详情。所以将auth_menus改成下面的样子就可以了,可以打印下SQL语句并手动执行SQL语句去数据库中查询,然后和代码的查询结果对比。

    1. auth_menus = await db.fetch_all(
    2. system_auth_menu.select().where(
    3. system_auth_menu.c.is_disable == 0,
    4. system_auth_menu.c.id.in_(menu_ids),
    5. system_auth_menu.c.menu_type.in_(['C', 'A']))
    6. .order_by(system_auth_menu.c.menu_sort, system_auth_menu.c.id))

    修改后verify_token()中menus有数据了,但原始源码对它的处理方式不对。它首先把menus定义成空列表,然后对列表进行了错误的处理,导致处理结果不对,可以打印看看,我就不放图了。根据打印结果,以及它代码中用了extend和split,猜测menus定义时可以是空列表或空字符串,对应两种不同的处理方法。

    1. # menus用列表
    2. menus = []
    3. # 校验角色权限是否存在
    4. for role_id in role_ids:
    5. if not await RedisUtil.hexists(AdminConfig.backstage_roles_key, role_id):
    6. await SystemAuthPermService.cache_role_menus_by_role_id(role_id)
    7. aaa = await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id)
    8. print(aaa) # system:admin:list,system:admin:detail,system:admin:add,system:admin:edit等
    9. print(type(aaa)) # 字符串
    10. menus.extend(i for i in aaa.split(','))
    11. # 验证是否有权限操作
    12. print('-------------')
    13. print(menus) # ['system:admin:list', 'system:admin:detail', 'system:admin:add', 'system:admin:edit']等
    14. print(auths) # common:index:console,此时访问的是后台的工作台页面
    15. print('-------------')
    16. if not (menus and auths in menus):
    17. raise AppException(HttpResp.NO_PERMISSION)
    18. # menus用字符串
    19. menus = ''
    20. # 校验角色权限是否存在
    21. for role_id in role_ids:
    22. if not await RedisUtil.hexists(AdminConfig.backstage_roles_key, role_id):
    23. await SystemAuthPermService.cache_role_menus_by_role_id(role_id)
    24. aaa = await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id)
    25. print(aaa) # system:admin:list,system:admin:detail,system:admin:add,system:admin:edit等
    26. print(type(aaa)) # 字符串
    27. menus += await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id)
    28. # 验证是否有权限操作
    29. print('-------------')
    30. print(menus) # system:admin:list,system:admin:detail,system:admin:add,system:admin:edit等
    31. print(auths) # common:index:console,此时访问的是后台的工作台页面
    32. if isinstance(menus, str):
    33. print(menus.split(',')) # ['system:admin:list', 'system:admin:detail', 'system:admin:add', 'system:admin:edit']等
    34. print('--------------')
    35. if not (menus and auths in menus.split(',')):
    36. raise AppException(HttpResp.NO_PERMISSION)

    可能你的auths的打印结果和我的不一样,上面注释中我写的打印的值,是我又修改了一处地方后的,也可能是修改前一直都是,记不清了。因为改完上面后发现还是提示无相关权限,朋友说可能是菜单管理中权限标识配错了。

     

    打印的auths是common:index:console,打印的menus中也是类似system:admin:list这种,三个值中间用冒号连接,而出现问题时,是登录后自动跳转到工作台页面,访问的工作台,菜单管理里配的index:console,配成common:index:console就可以了,这个对应工作台页面的路由。

    配置时也有提示,要写什么格式。

     

    感谢朋友的帮助和指导。

    6.Nginx HTTP文件服务器无法访问

    一开始,整个项目都在/root中,配置了Nginx HTTP服务器后,浏览器通过url访问不了文件夹/文件,403。

    然后在/home中新建一个文件夹,再将项目放在这个文件夹中,再用chmod -R 666给需要通过url访问的文件夹设置权限,浏览器访问某些文件夹时会跳转到其他url,浏览器标签页中显示的网页title也变了,如变成"首页",而有的文件夹能正常访问。

    不能访问的文件夹的例子:public文件夹,里面有html文件;public/uploads文件夹,里面有html文件。

    然后单独创建一个空文件夹也能访问。发现有的文件夹中含有html文件,打开了某一个后,内容里有"首页"字样,这个文件夹无法通过浏览器访问,删除这个html文件或改扩展名后,这个文件夹就能访问了。应该是文件夹中有html文件时,访问这个文件夹会渲染这个html,所以就看不到这个文件夹中的文件了。

  • 相关阅读:
    Linux编译安装libmodbus库
    逆波兰算法、中缀表达式转后缀表达式
    代码应该怎么写?
    51单片机项目(9)——基于51单片机的电子琴设计
    3.1 C++高级编程_抽象类_概念
    初识compileflow工作流
    Oracle database 21c 新特性:密码逐步切换策略
    java 调用合约使用nonce 可能会出现的问题
    Java学习笔记------常用API(二)
    攀爬倒影发光方块
  • 原文地址:https://blog.csdn.net/fj_changing/article/details/134022930