likeadmin部署

news2024/11/18 13:31:59

以下内容写于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。

文档中示例代码

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

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

{
    "code": 500,
    "msg": "系统错误",
    "data": []
}

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

{
    "code": 200,
    "msg": "成功",
    "data": {
        "status_code": 401,
        "detail": "登录失败",
        "headers": null
    }
}

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

{
    "code": 322,
    "msg": "token参数为空",
    "data": []
}

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

 

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

 

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

 

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

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

 然后使用时

from like.exceptions.base import AppException
from like.http_base import HttpCode, HttpResp
 
def test():
    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()的部分原始源码,这些代码是和解决这个问题有关的。

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

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

async def cache_role_menus_by_role_id(cls, role_id: int):
    """缓存角色菜单"""
    auth_perms = await db.fetch_all(
        system_auth_perm.select().where(system_auth_perm.c.role_id == role_id))
    menu_ids = [i.menu_id for i in auth_perms]
    menus = []
    if menu_ids:
        auth_menus = await db.fetch_all(
            system_auth_menu.select().where(
                system_auth_menu.c.is_disable == 0,
                system_auth_menu.c.id in menu_ids,
                system_auth_menu.c.menu_type in ['C', 'A'])
            .order_by(system_auth_menu.c.menu_sort, system_auth_menu.c.id))
        menus = [i.perms for i in auth_menus if i.perms]
    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语句的方法请告诉我。

auth_menus = system_auth_menu.select().where(
                    system_auth_menu.c.is_disable == 0,
                    system_auth_menu.c.id in menu_ids,
                    system_auth_menu.c.menu_type in ['C', 'A']).__str__()
print(auth_menus)
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
FROM la_system_auth_menu
WHERE false

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

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

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

# menus用列表
menus = []
# 校验角色权限是否存在
for role_id in role_ids:
    if not await RedisUtil.hexists(AdminConfig.backstage_roles_key, role_id):
        await SystemAuthPermService.cache_role_menus_by_role_id(role_id)
    aaa = await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id)
    print(aaa)  # system:admin:list,system:admin:detail,system:admin:add,system:admin:edit等
    print(type(aaa))  # 字符串
    menus.extend(i for i in aaa.split(','))
 
# 验证是否有权限操作
print('-------------')
print(menus)  # ['system:admin:list', 'system:admin:detail', 'system:admin:add', 'system:admin:edit']等
print(auths)  # common:index:console,此时访问的是后台的工作台页面
print('-------------')
if not (menus and auths in menus):
    raise AppException(HttpResp.NO_PERMISSION)
 
 
 
# menus用字符串
menus = ''
# 校验角色权限是否存在
for role_id in role_ids:
    if not await RedisUtil.hexists(AdminConfig.backstage_roles_key, role_id):
        await SystemAuthPermService.cache_role_menus_by_role_id(role_id)
    aaa = await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id)
    print(aaa)  # system:admin:list,system:admin:detail,system:admin:add,system:admin:edit等
    print(type(aaa))  # 字符串
    menus += await RedisUtil.hget(AdminConfig.backstage_roles_key, role_id)
 
# 验证是否有权限操作
print('-------------')
print(menus)  # system:admin:list,system:admin:detail,system:admin:add,system:admin:edit等
print(auths)  # common:index:console,此时访问的是后台的工作台页面
if isinstance(menus, str):
    print(menus.split(','))  # ['system:admin:list', 'system:admin:detail', 'system:admin:add', 'system:admin:edit']等
print('--------------')
if not (menus and auths in menus.split(',')):
    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,所以就看不到这个文件夹中的文件了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1129394.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第八部分:Linux、安全

本心、输入输出、结果 文章目录 系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第八部分:Linux、安全前言Linux 文件系统解释应该知道的 18 个最常用的 Linux 命令HTTPS如何工作?数据是如何加密和解密的?为什么HTTPS在数据传输过程中会…

java通过IO流下载保存文件

我们在开发过程中,可能会遇到需要到远程服务器上下载文件的需求,一般我们的文件可能会有一个url地址,我们拿到这个地址,可以构建URLConnection对象,之后可以根据这个URLConnection来获取InputStream,之后&a…

C++ list 的使用

目录 1. 构造函数 1.1 list () 1.2 list (size_t n, const T& val T()) 1.3 list (InputIterator first, InputIterator last) 2. bool empty() const 3. size_type size() const 4. T& front() 4. T& back() 5. void push_front (const T& val) 6.…

【Java系列】Java 基础

目录 基础1.JDK和JRE的区别2.Java为什么不直接实现lterator接口,而是实现lterable?3.简述什么是值传递和引用传递?4.概括的解释下Java线程的几种可用状态? 中级1.简述Java同步方法和同步代码块的区别 ?2.HashMap和Hashtable有什么区别?3.简述Java堆的结构? 什…

生命礼赞,带动世界第三次文化复兴——非洲回顾篇

一个民族的复兴需要强大的物质力量,也需要强大的精神力量。大型玉雕群组《生命礼赞》是对中华民族伟大生命的讴歌,是对百姓美好生活的赞美,完美诠释了中华民族的伟大图腾,它象征着中华民族在党的带领下艰苦奋斗,江山稳…

嵌入式软件工程师面试题——2025校招专题(二)

说明: 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要…

美团动态ThreadPoolExecutor底层实现源码实战

开篇:介绍springboot连接nacos实现动态线程池,同时得安装nacos,同时代码将有两个模块,dtp-spring-boot-starter 与 user 模块,前者将是独立的动态线程池,可以引入自己的项目中,后者模块主要用于…

面试官:听说你很了解Java8特性,给我优化一下这段代码吧?

文章目录 前言我的想法面试官 前言 在之前的一次面试过程中,我被问到了一道代码优化题:对于下面的代码,你有什么优化的思路呢? boolean handleStrList(String strList){for (String s :strList){if(s.length()%20){return true;…

测试用例的设计方法(全):等价类划分方法

一.方法简介 1.定义 是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例。该方法是一种重要的,常用的黑盒测试用例设计方法。 2.划分等价类: 等价类是指某个输入域的…

祝各位程序员节日快乐

今天是1024程序员节,小编祝各位Windows开机蓝屏,Linux开机Kernel Panic,macos开机五国,服务器iDRAC/ iLO/IPMI/KVM全部失联,路由器全爆炸,路由表内存全溢出,交换机全环路,防火墙全阻…

Spring Security加密和匹配

一. 密码加密简介 1. 散列加密概述 我们开发时进行密码加密,可用的加密手段有很多,比如对称加密、非对称加密、信息摘要等。在一般的项目里,常用的就是信息摘要算法,也可以被称为散列加密函数,或者称为散列算法、哈希…

清华训练营悟道篇之浅谈操作系统

文章目录 前言系统软件执行环境操作系统的功能操作系统组成 前言 操作系统是一个帮助用户和应用程序使用和管理计算机资源的软件,它控制着嵌入式设备、更通用的系统(如智能手机、台式计算机和服务器)以及巨型机等各种计算机系统 系统软件 …

【模型部署】c++部署yolov5使用openvino

1. 推理方式 CPU推理GPU推理(要求电脑具备核显,即CPU中有嵌入显卡) 2. openvino依赖项下载 https://github.com/openvinotoolkit/openvino/releases 直接解压(随便放到哪个路径) 环境配置 ********(…

Text Classification via Large Language Models

Abstract 表达大模型在文本分类上做的不好。 原因: 1、处理复杂语境时缺少推理能力。(e.g… 类比、讽刺) 2、限制学习的上下文的token数。 提出了自己的策略: ** Clue And Reasoning Prompting (CARP).线索与推理提示** 1、能用prompt找到clue(语境线索…

Mysql进阶-存储引擎篇

MySQL体系结构 其他数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎上,插件式的存储引擎架构,将查询处理和其他的系统任务以及数据的存储提取分离。 这种架构可以根据业务的需…

RK3568平台开发系列讲解(应用篇)串口应用编程之串口的使用步骤

🚀返回专栏总目录 文章目录 一、配置参数1.1、获取当前串口的配置参数1.2、修改和写入串口的配置参数二、模式2.1、输入模式2.2、输出模式2.3、控制模式2.4、本地模式2.5、特殊控制字符沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 串口设备是嵌入式开发中最常用的…

Facebook批量注册如何做?一文教会你

我们都知道单个代理地址注册多个社媒账号,基本第二天都会进入到一个风控状态,要申诉或者封号,批量注册需要用不同的地址进行注册,推荐使用动态代理代理,下面是需要准备到的环境和材料以及注册的全流程。 一、环境需求 1、接码平…

STM32定时器介绍

STM32F103系列单片机定时器主要有:系统定时器SysTick,2个高级定时器TIM1和TIM8,4个通用定时器TIM2/3/4/5,2个基本定时器TIM6和TIM7。下面先简单介绍一下: 基本定时器:基本定时器只能计时,不能产…

MIT-BIH-AF 数据集开发库

目录 1 介绍数据集2 本博客函数库代码地址以及介绍读取dat,qrc,atr文件,获得 ECG_rpeaks,ann_aux_note,ann_sample,ECG0寻找时间点函数----signal_time_sample寻找R_R峰信号以及其位置----find_R_R_peak寻找 nR 峰信号以及位置---…

springboot实现邮箱发送(激活码)功能

第一步&#xff1a;现在邮箱里面开启smtp服务 这里用163邮箱举例&#xff0c;配置一下授权密码&#xff0c;这个要提前记住 第二步&#xff1a;引入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/P…