Dify 社区版Docker部署-(私有化部署知识库1)

克隆代码

git clone https://github.com/langgenius/dify.git

启动Dify

进入 Dify 源代码的 Docker 目录

cd dify/docker
复制环境配置文件
cp .env.example .env
启动 Docker 容器

根据你系统上的 Docker Compose 版本,选择合适的命令来启动容器。你可以通过 $ docker compose version 命令检查版本,详细说明请参考 Docker 官方文档:

如果版本是 Docker Compose V2,使用以下命令:

docker compose up -d

如果版本是 Docker Compose V1,使用以下命令:

docker-compose up -d

更新 Dify

进入 dify 源代码的 docker 目录,按顺序执行以下命令:

cd dify/docker
docker compose down
git pull origin main
docker compose pull
docker compose up -d

同步环境变量配置 (重要!)
如果 .env.example 文件有更新,请务必同步修改你本地的 .env 文件。

检查 .env 文件中的所有配置项,确保它们与你的实际运行环境相匹配。你可能需要将 .env.example 中的新变量添加到 .env 文件中,并更新已更改的任何值。

修改默认端口

EXPOSE_NGINX_PORT=80
EXPOSE_NGINX_SSL_PORT=443

Rust初学-安装篇

mac下安装

首先通过homebrew安装rustup管理工具:

brew install rustup

安装完后发现并不能够找到rustup指令,通过brew list进行查询,发现实际上安装的为rustup-init,于是再在命令行执行:

rustup-init

顺着流程安装完成之后,重启终端便可以安装好rust环境以及相关的工具链。

可以查看对应rustc以及cargo的版本:

rustc --version
rustc 1.53.0 (53cb7b09b 2021-06-17)
cargo --version
cargo 1.53.0 (4369396ce 2021-04-27)

简单的创建一个rust语言版的Hello World进行测试:

fn main() {
    println!("Hello World!");
}

将其保存为hello.rs。

在命令行使用rustc将其编译为可执行文件:

rustc hello.rs

之后直接执行可执行文件,便可以在终端看到对应的输出了!

./hello
Hello World!

打开本地文档

rustup doc

更新

 rustup update

卸载

rustup self uninstall

关于时序数据库influxdb的基本说明与使用整理

InfluxDB 是一种时序数据库,时序数据库通常被用在监控场景,比如运维和 IOT(物联网)领域。这类数据库旨在存储时序数据并实时处理它们。
可以写一个程序将服务器上 CPU 的使用情况每隔 10 秒钟向 InfluxDB 中写入一条数据。接着,我们写一个查询语句,查询过去 30 秒 CPU 的平均使用情况,然后让这个查询语句也每隔 10 秒钟执行一次。最终,我们配置一条报警规则,如果查询语句的执行结果>xxx,就立刻触发报警。
上述就是一个指标监控的场景,在 IOT 领域中,也有大量的指标需要我们监控。比如, 机械设备的轴承震动频率,农田的湿度温度等等。

为什么不用关系型数据库

  1. 写入性能
    关系型数据库也是支持时间戳的,也能够基于时间戳进行查询。但是,从我们的使用场景出发,需要注意数据库的写入性能。通常,关系型数据库会采用 B+树数据结构,在数据写入时,有可能会触发叶裂变,从而产生了对磁盘的随机读写,降低写入速度。
    数据价值

  2. 时间不可倒流,数据只写不改
    我们之前说,时序数据库一般用于指标监控场景。这个场景的数据有一个非常明显的特点就是冷热差别明显。通常,指标监控只会使用近期一段时间的数据,比如我只查询某个设备最近 10 分钟的记录,10 分钟前的数据我就不再用了。那么这 10 分钟前的数据,对我们来说就是冷数据,应该被压缩放到磁盘里去来节省空间。而热数据因为经常要用,数据库就应该让它留在内存里,等待查询。而市面上的时序数据库大都有类似的设计。

  3. 时间不可倒流,数据只写不改
    时序数据是描述一个实体在不同时间所处的不同状态。
    就像是我们打开任务管理器,查看 CPU 的使用情况。我发现 CPU 占用率太高了,于是杀死了一个进程,但 10 秒前的数据不会因为我关闭进程再发生改变了。这是时序数据的一大特点。与之相应,时序数据库基本上是插入操作较多,而且还没有什么更新需求。

一些概念

Bucket桶
是influxDb中基本存储单元 用于组织和存储时间序列数据 他是数据的逻辑容器,用于区分和隔离不同类型或来源的数据
oraganization 组织
用于隔离和管理用户 应用程序或者项目 每个用户可以属于一个或者多个组织 Bucket 也属于一个组织
Measurement 测量值
是influxDb中的数据表的概念 他类似关系型数据库中的数据表 用于存储时间序列数据 每个Measurement 包含一组字段和标签
Field 字段
field 是 Measurement 中的一种数据类型 用于存储实际的数值数据 例如温度 湿度 等是字段的例子
Tag标签
Tag 是 Measurement 中的一种索引类型 用于标识和过滤数据 标签通常用于存储维度信息 例如设备ID 地理位置 等是字段的例子
Point数据点
Point 是influxDb中时间序列数据的基本单位 它包含了时间戳 字段 标签 每个数据点都关联到一个Measurement
API密钥和访问令牌
为了进行安全的访问和操作
2.x 引入了Api 密钥和访问令牌 API密钥用于身份验证 而访问令牌用于授权对数据的访问
Flux查询语言
Flux是2.x 中的查询语言 用于执行强大的数据处理和分析操作 它支持灵活的 查询 过滤 聚合和转换操作
仪表盘和可视化
2.x 提供了仪表盘 和可视化工具 允许用户创建 定制 和共享图表 和仪表盘 以直观的展示时间序列数据

概念是oraganization,所有的数据都是属于org的。数据的存储都是在bucket里面。可以理解为数据库,2.x版本中没有database这个概念了。measurement可以理解成为一张表,但是实际上不是一个表。数据中point表示一条数据,每条数据都有一个_time表示时间戳,series表示point的集合,field是固定字段_field,key和value都是值,没有索引,tag是自定义字段名和值,索引的结合

复盘Casbin权限框架

Casbin的工作原理

在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) [策略,效果,请求,匹配器]的一个文件。

Policy:定义权限的规则
Effect:定义组合了多个Policy之后的结果
Request:访问请求
Matcher:判断Request是否满足Policy

首先会定义一堆Policy,让后通过Matcher来判断Request和Policy是否匹配,然后通过Effect来判断匹配结果是Allow还是Deny。

Policy
Policy 主要表示访问控制关于角色、资源、行为的具体映射关系。

Casbin的核心概念

Model

Model是Casbin的具体访问模型,其主要以文件的形式出现,该文件常常以.conf最为后缀。

Model CONF 至少应包含四个部分:
[request_definition],
[policy_definition],
[policy_effect],
[matchers]。
如果 model 使用 RBAC, 还需要添加[role_definition]部分。
Model CONF 文件可以包含注释。注释以 # 开头, # 会注释该行剩余部分。

比如:

# 请求定义 
# 用于request的定义,它明确了e.Enforce(...)函数中参数的定义,sub, obj, act 表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)。
[request_definition]
r = sub, obj, act

# 策略定义
# 用于policy的定义,每条规则通常以形如p的policy type开头,比如p,joker,data1,read就是一条joker具有data1读权限的规则。
[policy_definition]
p = sub, obj, act

# 角色定义
# 是RBAC角色继承关系的定义。g 是一个 RBAC系统,_, _表示角色继承关系的前项和后项,即前项继承后项角色的权限。
[role_definition]
g = _, _

# policy_effect:是对policy生效范围的定义,它对request的决策结果进行统一的决策,比如e = some(where (p.eft == allow))就表示如果存在任意一个决策结果为allow的匹配规则,则最终决策结果为allow。p.eft 表示策略规则的决策结果,可以为allow 或者deny,当不指定规则的决策结果时,取默认值allow 。
[policy_effect]
e = some(where (p.eft == allow))

# 匹配器定义
# 定义了策略匹配者。匹配者是一组表达式,它定义了如何根据请求来匹配策略规则
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

在Casbin中,访问控制模型被抽象为基于PERM元模型(策略 = Policy ,效果 = Effect,请求 = Request,匹配器 = Matcher)的CONF文件。

为项目切换或升级授权机制就像修改配置一样简单。 您可以通过组合可用模型来定制自己的访问控制模型。

例如,您可以在一个模型内部将RBAC角色和ABAC属性结合在一起,并共享一套策略规则。

策略 一般存储到数据库 因为会有很多!!!!

定义Policy

定义Matcher

Request 与 Policy 进行匹配,获得一个Effect

拿到 Effect 的结果到 Effect 的表达式 返回一个布尔值

Request == 请求

定义请求参数。 基本请求是一个元组对象,至少需要一个主体(被访问实体),对象(被访问资源)和动作(访问方法)。

例如,请求定义可能看起来像这样:

r={sub,obj,act}
# r={被访问主体,被访问资源,动作或方法}

此定义指定了访问控制匹配函数所需的参数名称和顺序。

Policy == 策略

定义访问策略的模型。 它指定了策略规则文档中字段的名称和顺序。

例如:

p={sub, obj, act} 或 p={sub, obj, act, eft}

注意:如果未定义eft(策略结果),则不会读取策略文件中的结果字段,匹配策略结果将默认允许。

Matcher == 匹配

定义请求和策略的匹配规则。

例如:

m = r.sub == p.sub && r.act == p.act && r.obj == p.obj 

这个简单而常见的匹配规则意味着,如果请求的参数(被访问主体,被访问资源,动作或方法)等于策略中找到的那些,那么返回策略结果(p.eft)。 策略的结果将保存在p.eft中。

Effect == 效果

对匹配器的匹配结果进行逻辑组合判断。

例如:

e = some(where(p.eft == allow))

这个语句意味着,如果匹配策略结果p.eft有(一些)允许的结果,那么最终结果为真。

让我们看另一个例子:

e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

这个例子组合的逻辑意义是:如果有一个策略匹配到允许的结果,并且没有策略匹配到拒绝的结果,结果为真。 换句话说,当匹配策略都是允许时,结果为真。 如果有任何拒绝,两者都为假(更简单地说,当允许和拒绝同时存在时,拒绝优先)。

ACL

Casbin中最基本和最简单的模型是ACL。
ACL的模型CONF如下:

# 请求
[request_definition]
r = sub, obj, act

# 策略
[policy_definition]
p = sub, obj, act

# 效果
[policy_effect]
e = some(where (p.eft == allow))

# 匹配
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

ACL模型的一个示例策略为:

p, 张三, data1, read
p, 李四, data2, write

这意味着:

张三 可以读取data1
李四 可以写入data2
我们还支持通过在末尾添加 '\' 来进行多行模式:

# 匹配
[matchers]
m = r.sub == p.sub && r.obj == p.obj \
&& r.act == p.act

此外,如果您正在使用ABAC,您可以尝试如下例子中的'in'操作符,该例子是Casbin golang版本的(jCasbin和Node-Casbin还不支持):

# 匹配
[matchers]
m = r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3')

但是你必须确保数组的长度超过 1,否则会引发难以恢复的错误(panic)。

参考教程

国高办公系统
https://www.bilibili.com/video/BV1qz4y167XP/
https://blog.csdn.net/weixin_51991615/article/details/123746169
https://darjun.github.io/2020/06/12/godailylib/casbin/
https://casbin.org/zh/docs/how-it-works
https://casbin.org/zh/editor
https://github.com/casbin/gorm-adapter
https://casbin.org/zh/docs/adapters/#%E6%94%AF%E6%8C%81%E7%9A%84%E9%80%82%E9%85%8D%E5%99%A8
https://juejin.cn/post/7133456877605617678
https://medium.com/@mehul25/casbin-rbac-with-domains-92680bd28d2e
https://medium.com/@mehul25/casbin-groups-and-roles-6c279aa918b

复盘orm库gorm框架

gorm是一个使用Go语言编写的ORM框架。它文档齐全,对开发者友好,支持主流数据库。
基础配置

type DbConfig struct {
    DbHost        string `mapstructure:"db_host" json:"db_host" yaml:"db_host"`
    DbPort        int    `mapstructure:"db_port" json:"db_port" yaml:"db_port"`
    DbName        string `mapstructure:"db_name" json:"db_name" yaml:"db_name"`
    DbUser        string `mapstructure:"db_user" json:"db_user" yaml:"db_user"`
    DbPass        string `mapstructure:"db_pass" json:"db_pass" yaml:"db_pass"`
    TablePrefix   string `mapstructure:"table_prefix" json:"table_prefix" yaml:"table_prefix"`
    TimeZone      string `mapstructure:"time_zone" json:"time_zone" yaml:"time_zone"`
    LogLevel      int    `mapstructure:"log_level" json:"log_level" yaml:"log_level"`                // LogLevel SQL日志级别 (1-静音 2-错误 3-警告 4-信息)
    SlowThreshold int    `mapstructure:"slow_threshold" json:"slow_threshold" yaml:"slow_threshold"` // SlowThreshold 慢SQL阈值(毫秒)。慢SQL会在log_level大于等于3时输出。
    IdleConns     int    `mapstructure:"idle_conns" json:"idle_conns" yaml:"idle_conns"`             // 空闲连接池中的最大连接数,建议为open_conns的百分之5-20之间
    OpenConns     int    `mapstructure:"open_conns" json:"open_conns" yaml:"open_conns"`             // 最大打开连接数,建议这里设置为50
}

func InitGorm(conf *config.Config) *gorm.DB {
    dsn := fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s sslmode=disable TimeZone=%s",
        conf.Db.DbHost, conf.Db.DbPort, conf.Db.DbName, conf.Db.DbUser, conf.Db.DbPass, conf.Db.TimeZone)
    postgresConfig := postgres.Config{
        DSN:                  dsn,  // DSN data source name
        PreferSimpleProtocol: true, // 禁用隐式 prepared statement
    }

    // 使用标准日志库的New方法创建日志输出
    //newLogger := logger.New(
    //  log.New(os.Stdout, "\r\n", log.LstdFlags),
    //  logger.Config{
    //      SlowThreshold:             time.Duration(dbConf.SlowThreshold) * time.Millisecond, // 慢SQL阈值
    //      LogLevel:                  logger.LogLevel(dbConf.LogLevel),                       // 日志级别
    //      IgnoreRecordNotFoundError: true,
    //      Colorful:                  true,
    //  })

    // zap 自定义
    newLogger := NewGormLogger(x_logger.GormZapLog.GetZapLogger(), logger.LogLevel(conf.Db.LogLevel))

    PGDB, err := gorm.Open(postgres.New(postgresConfig), &gorm.Config{
        //Logger: newLogger,
        //Logger: logger.Default.LogMode(logger.Info),
        Logger: newLogger,
        NamingStrategy: schema.NamingStrategy{
            SingularTable: false, // 使用单数表名,在启用此选项的情况下,“user”的表将是“user”
            TablePrefix:   "te_", // 表前缀
        },
    })

    DB := PGDB

    if err != nil {
        fmt.Println("数据库链接失败")
        os.Exit(0)
    }
    AutoMigrate(DB)
    sqlDB, _ := DB.DB()
    sqlDB.SetMaxIdleConns(conf.Db.IdleConns)
    sqlDB.SetMaxOpenConns(conf.Db.OpenConns)
    // 设置数据库连接池中连接的最大生命周期
    sqlDB.SetConnMaxLifetime(time.Hour)
    fmt.Println("[+]PG连接成功!")
    return DB
}

// AutoMigrate 自动迁移
func AutoMigrate(db *gorm.DB) {
    err := db.AutoMigrate(
        new(model.User),
    )
    if err != nil {
        fmt.Println("[-] 迁移数据表失败:", err.Error())
        os.Exit(0)
    }
}

基础的数据操作

    // 获取表名
    m := &model.User{}
    modelType := reflect.TypeOf(m).Elem()
    fmt.Println(modelType)
    tableName := global.Db.NamingStrategy.TableName("User")
    fmt.Println(tableName)
    //
    // 插入一条记录
    user := model.User{Name: "刘建超"}
    result := global.Db.Create(&user) // 在数据库中插入一条新记录
    fmt.Println(result.Error)         // 如果有错误,打印错误
    fmt.Println(user.ID)              // 打印插入后生成的 ID
    fmt.Printf("%#v", user)           // 打印插入后生成的 ID

    // Save 修改记录
    user = model.User{Name: "刘建超iuu"}
    user.ID = 1
    // 排除指定字段
    global.Db.Omit("Created").Save(&user)

    // 软删除
    global.Db.Delete(&user)

    // 如果主键为空,插入新记录
    user = model.User{Name: "iuu"}
    global.Db.Save(&user)

    user = model.User{Name: "刘建超iuuxxx"}
    user.ID = 2
    global.Db.Select("Name").Save(&user)

    //永久删除
    global.Db.Unscoped().Delete(&user)
1 2 3 9