安爸-超级家庭

commands.py中的函数解析1:reset_password等

安爸 发布于

本文使用Dify v1.4.0版本,主要解析了commands.py中的reset_passwordreset_emailreset_encrypt_key_pairvdb_migrate等函数的执行逻辑。源码位置:dify\api\commands.py

函数名 功能描述 参数列表 参数默认值 参数解释
reset_password 重置账户密码 email, new_password, password_confirm email: 账户邮箱new_password: 新密码password_confirm: 确认新密码
reset_email 重置账户邮箱 email, new_email, email_confirm email: 当前邮箱new_email: 新邮箱email_confirm: 确认新邮箱
reset_encrypt_key_pair 重置工作区加密密钥对
vdb_migrate 迁移向量数据库 scope “all” scope: 迁移范围(knowledge/annotation/all)
migrate_annotation_vector_database 迁移注释数据到目标向量数据库
migrate_knowledge_vector_database 迁移知识库向量数据库数据
convert_to_agent_apps 转换Agent Assistant为Agent App
add_qdrant_index 添加Qdrant索引 field “metadata.doc_id” field: 索引字段
old_metadata_migration 旧元数据迁移
create_tenant 创建租户账户 email, language, name language: Nonename: None email: 邮箱language: 语言name: 工作区名
upgrade_db 升级数据库
fix_app_site_missing 修复应用相关站点缺失问题
migrate_data_for_plugin 插件数据迁移
extract_plugins 提取插件 output_file, workers output_file: “plugins.jsonl”workers: 10 output_file: 输出文件workers: 并发数
extract_unique_plugins 提取唯一插件标识符 output_file, input_file output_file: “unique_identifiers.json”input_file: “plugins.jsonl” output_file: 输出文件input_file: 输入文件
install_plugins 安装插件 input_file, output_file, workers input_file: “plugins.jsonl”output_file: “installed_plugins.jsonl”workers: 100 input_file: 输入文件output_file: 输出文件workers: 并发数
clear_free_plan_tenant_expired_logs 清理免费计划租户过期日志 days, batch, tenant_ids days: 30batch: 100 days: 天数batch: 批量大小tenant_ids: 租户ID列表
clear_orphaned_file_records 清理数据库中孤立文件记录 force False force: 跳过确认强制执行
remove_orphaned_files_on_storage 清理存储中孤立文件 force False force: 跳过确认强制执行

一.reset_password()函数

完整的执行命令reset-password示例,如下所示:

flask reset-password --email your@email.com --new-password 新密码 --password-confirm 新密码

如果不加参数,会依次提示输入邮箱、新密码和确认新密码。执行后会根据逻辑重置对应账号的密码。

`@click.command("reset-password", help="Reset the account password.")
@click.option("--email", prompt=True, help="Account email to reset password for")
@click.option("--new-password", prompt=True, help="New password")
@click.option("--password-confirm", prompt=True, help="Confirm new password")
defreset_password(email, new_password, password_confirm):
"""
    Reset password of owner account
    Only available in SELF_HOSTED mode
    """
if str(new_password).strip() != str(password_confirm).strip():
        click.echo(click.style("Passwords do not match.", fg="red"))
return

    account = db.session.query(Account).filter(Account.email == email).one_or_none()

ifnot account:
        click.echo(click.style("Account not found for email: {}".format(email), fg="red"))
return

try:
        valid_password(new_password)
except:
        click.echo(click.style("Invalid password. Must match {}".format(password_pattern), fg="red"))
return

generate password salt

    salt = secrets.token_bytes(16)
    base64_salt = base64.b64encode(salt).decode()

encrypt password with salt

    password_hashed = hash_password(new_password, salt)
    base64_password_hashed = base64.b64encode(password_hashed).decode()
    account.password = base64_password_hashed
    account.password_salt = base64_salt
    db.session.commit()
    click.echo(click.style("Password reset successfully.", fg="green"))
`

该段代码实现了一个命令行工具命令 reset-password,用于重置账户密码,具体流程如下:

1.命令定义与参数

使用 click 库定义命令 reset-password,并要求输入三个参数:

  • --email:要重置密码的账户邮箱,命令行会提示输入。
  • --new-password:新密码,命令行会提示输入。
  • --password-confirm:确认新密码,命令行会提示输入。

2.密码一致性校验

检查两次输入的新密码是否一致(去除首尾空格后比较)。如果不一致,输出红色提示”Passwords do not match.”,并终止命令。

3.账户查找

通过 SQLAlchemy 查询数据库,查找邮箱为输入值的账户。如果找不到,输出红色提示”Account not found for email: …”,并终止命令。

4.密码格式校验

调用 valid_password(new_password) 校验新密码格式(如长度、复杂度等)。如果校验失败,捕获异常,输出红色提示”Invalid password. Must match …”,并终止命令。

5.生成password salt(密码盐)

使用 secrets.token_bytes(16) 生成16字节的随机盐,并用 base64 编码为字符串,便于存储。

6.加密新密码

调用 hash_password(new_password, salt) 用盐加密新密码,得到加密后的字节串,再用 base64 编码为字符串。

7.保存到数据库

将加密后的password和password salt分别赋值给账户对象的 passwordpassword_salt 字段,然后提交数据库事务。

8.成功提示

输出绿色提示”Password reset successfully.”,表示密码重置成功。

二.reset_email()函数

reset-email 命令的完整执行示例如下:

python -m flask reset-email --email old@example.com --new-email new@example.com --email-confirm new@example.com

执行后会根据输入的参数重置账户邮箱。如果新邮箱和确认邮箱不一致,或邮箱格式不正确,会有相应的错误提示。

`@click.command("reset-email", help="Reset the account email.")
@click.option("--email", prompt=True, help="Current account email")
@click.option("--new-email", prompt=True, help="New email")
@click.option("--email-confirm", prompt=True, help="Confirm new email")
defreset_email(email, new_email, email_confirm):
"""
    Replace account email
    :return:
    """
if str(new_email).strip() != str(email_confirm).strip():
        click.echo(click.style("New emails do not match.", fg="red"))
return

    account = db.session.query(Account).filter(Account.email == email).one_or_none()

ifnot account:
        click.echo(click.style("Account not found for email: {}".format(email), fg="red"))
return

try:
        email_validate(new_email)
except:
        click.echo(click.style("Invalid email: {}".format(new_email), fg="red"))
return

    account.email = new_email
    db.session.commit()
    click.echo(click.style("Email updated successfully.", fg="green"))
`

该命令行工具 reset-email 用于重置账户邮箱,主要流程如下:

1.命令定义与参数获取

使用 click 定义命令和参数,要求输入当前邮箱、新邮箱和确认新邮箱。

@click.command("reset-email", help="Reset the account email.") @click.option("--email", prompt=True, help="Current account email") @click.option("--new-email", prompt=True, help="New email") @click.option("--email-confirm", prompt=True, help="Confirm new email") defreset_email(email, new_email, email_confirm):

2.新邮箱一致性校验

检查两次输入的新邮箱是否一致,不一致则提示错误并终止。

if str(new_email).strip() != str(email_confirm).strip():     click.echo(click.style("New emails do not match.", fg="red")) return

3.账户查找

在数据库中查找当前邮箱对应的账户,找不到则提示错误并终止。

`account = db.session.query(Account).filter(Account.email == email).one_or_none()

ifnot account:
    click.echo(click.style("Account not found for email: {}".format(email), fg="red"))
return
`

4.新邮箱格式校验

调用 email_validate 校验新邮箱格式,失败则提示错误并终止。

try:     email_validate(new_email) except:     click.echo(click.style("Invalid email: {}".format(new_email), fg="red")) return

5.邮箱更新并提交

更新账户邮箱,提交数据库事务,提示成功。

account.email = new_email db.session.commit() click.echo(click.style("Email updated successfully.", fg="green"))

三.reset_encrypt_key_pair()函数

使用如下命令执行重置密钥对操作:

flask reset-encrypt-key-pair

执行后会有危险操作确认提示,输入 y 并回车即可继续。此命令仅适用于自托管(SELF_HOSTED)模式。

`@click.command(
"reset-encrypt-key-pair",
    help="Reset the asymmetric key pair of workspace for encrypt LLM credentials. "
"After the reset, all LLM credentials will become invalid, "
"requiring re-entry."
"Only support SELF_HOSTED mode.",
)
@click.confirmation_option(
    prompt=click.style(
"Are you sure you want to reset encrypt key pair? This operation cannot be rolled back!", fg="red"
    )
)
defreset_encrypt_key_pair():
"""
    Reset the encrypted key pair of workspace for encrypt LLM credentials.
    After the reset, all LLM credentials will become invalid, requiring re-entry.
    Only support SELF_HOSTED mode.
    """
if dify_config.EDITION != "SELF_HOSTED":
        click.echo(click.style("This command is only for SELF_HOSTED installations.", fg="red"))
return

    tenants = db.session.query(Tenant).all()
for tenant in tenants:
ifnot tenant:
            click.echo(click.style("No workspaces found. Run /install first.", fg="red"))
return

        tenant.encrypt_public_key = generate_key_pair(tenant.id)

        db.session.query(Provider).filter(Provider.provider_type == "custom", Provider.tenant_id == tenant.id).delete()
        db.session.query(ProviderModel).filter(ProviderModel.tenant_id == tenant.id).delete()
        db.session.commit()

        click.echo(
            click.style(
"Congratulations! The asymmetric key pair of workspace {} has been reset.".format(tenant.id),
                fg="green",
            )
        )
`

该段代码实现了一个用于重置工作区(租户)的加密密钥对的命令行工具,适用于”自托管”模式。主要流程如下:

1.命令注册与确认

  • 使用 @click.command 注册命令名为 reset-encrypt-key-pair
  • 使用 @click.confirmation_option 增加危险操作的二次确认提示。

@click.command( "reset-encrypt-key-pair",     help="Reset the asymmetric key pair of workspace for encrypt LLM credentials. " "After the reset, all LLM credentials will become invalid, " "requiring re-entry." "Only support SELF_HOSTED mode.", ) @click.confirmation_option(     prompt=click.style( "Are you sure you want to reset encrypt key pair? This operation cannot be rolled back!", fg="red"     ) )

2.入口函数定义

  • 定义 reset_encrypt_key_pair 函数,包含详细注释说明用途。

defreset_encrypt_key_pair(): """     Reset the encrypted key pair of workspace for encrypt LLM credentials.     After the reset, all LLM credentials will become invalid, requiring re-entry.     Only support SELF_HOSTED mode.     """

3. 检查运行环境

  • 判断当前系统是否为 SELF_HOSTED 版本,否则直接退出。

if dify_config.EDITION != "SELF_HOSTED":         click.echo(click.style("This command is only for SELF_HOSTED installations.", fg="red")) return

4. 查询所有租户

  • 查询数据库中所有租户(Tenant)。

    tenants = db.session.query(Tenant).all()

5. 遍历租户并重置密钥

  • 遍历每个租户,若租户不存在则提示并退出。
  • 为每个租户生成新的加密公钥。
  • 删除该租户下所有自定义 Provider 和 ProviderModel 记录(密钥重置后原有凭据失效)。
  • 提交数据库更改。

`for tenant in tenants:
ifnot tenant:
            click.echo(click.style("No workspaces found. Run /install first.", fg="red"))
return

        tenant.encrypt_public_key = generate_key_pair(tenant.id)

        db.session.query(Provider).filter(Provider.provider_type == "custom", Provider.tenant_id == tenant.id).delete()
        db.session.query(ProviderModel).filter(ProviderModel.tenant_id == tenant.id).delete()
        db.session.commit()
`

6. 操作完成提示

  • 每个租户重置完成后输出提示信息。

        click.echo(             click.style( "Congratulations! The asymmetric key pair of workspace {} has been reset.".format(tenant.id),                 fg="green",             )         )

总结:该命令用于重置所有租户的加密密钥对,并清除相关凭据,确保密钥重置后所有 LLM 凭据都需重新录入,仅限自托管环境使用。

四.vdb_migrate()函数

在命令行中执行 vdb-migrate 命令的完整示例:

python api/commands.py vdb-migrate --scope=all

也可以指定 scope 参数为 knowledgeannotation,例如:

python api/commands.py vdb-migrate --scope=knowledge

python api/commands.py vdb-migrate --scope=annotation

该命令会根据 scope 参数迁移对应的向量数据库。

@click.command("vdb-migrate", help="Migrate vector db.") @click.option("--scope", default="all", prompt=False, help="The scope of vector database to migrate, Default is All.") defvdb_migrate(scope: str): if scope in {"knowledge", "all"}:         migrate_knowledge_vector_database() if scope in {"annotation", "all"}:         migrate_annotation_vector_database()

该段代码定义了一个名为 vdb_migrate 的 Click 命令,用于迁移向量数据库。

1.命令定义与参数说明

  • 使用 @click.command 装饰器定义命令名为 vdb-migrate,帮助信息为”Migrate vector db.”
  • 使用 @click.option 定义命令行参数 --scope,默认值为 all,不需要交互式输入,帮助信息为”迁移向量数据库的范围,默认是全部”。

@click.command("vdb-migrate", help="Migrate vector db.") @click.option("--scope", default="all", prompt=False, help="The scope of vector database to migrate, Default is All.")

2.命令实现

  • 定义函数 vdb_migrate(scope: str),接收 scope 参数。
  • 判断 scope 是否为 "knowledge""all",如果是,则调用migrate_knowledge_vector_database() 进行知识库向量数据库迁移。
  • 判断 scope 是否为 "annotation""all",如果是,则调用 migrate_annotation_vector_database() 进行标注向量数据库迁移。

defvdb_migrate(scope: str): if scope in {"knowledge", "all"}:         migrate_knowledge_vector_database() if scope in {"annotation", "all"}:         migrate_annotation_vector_database()

3.代码流程总结

  • 用户在命令行输入 vdb-migrate 命令时,可以通过 --scope 参数指定迁移范围(knowledgeannotationall)。
  • 根据参数,分别调用知识库或标注数据的向量数据库迁移函数。
  • 这两个迁移函数分别负责不同类型数据的向量数据库迁移,具体实现见 migrate_knowledge_vector_databasemigrate_annotation_vector_database

参考文献

[0] commands.py中的函数解析1:reset_password等:https://z0yrmerhgi8.feishu.cn/wiki/R35Pwv1fliyP9rkCMYgcjRZknve

[1] click github:https://github.com/pallets/click

[2] click官方文档:https://click.palletsprojects.com/en/stable/

[3] click-extra github:https://github.com/kdeldycke/click-extra

[4] click-extra官方文档:https://kdeldycke.github.io/click-extra/


知识星球服务内容:Dify源码剖析及答疑,Dify对话系统源码,NLP电子书籍报告下载,公众号所有付费资料。加微信buxingtianxia21进NLP工程化资料群

(文:NLP工程化)

commands.py中的函数解析1:reset_password等最先出现在每时AI


扫描二维码,在手机上阅读