Dfiy社区1.9.0数据库创建用户实现多租户多工作区
我对此做了些改造,dify初次启动后pg数据库的格式并不是原博主说的那样而是UUIDWORKCODE要确认 Dify 数据库中的accounts表是否包含custom_id字段,你可以通过以下任一方法操作:accountsWORKCODE必须是 UUID(由数据库或代码生成)WORKCODEnameemail。
参考:Dify 社区版手动开启多租户并批量导入用户实战指南_dify 多租户-CSDN博客
我对此做了些改造,dify初次启动后pg数据库的格式并不是原博主说的那样
而是
Dify 的 accounts.id 字段是 UUID 类型,但你的 Excel 中的 WORKCODE 列是字符串,不是合法的 UUID 格式
要确认 Dify 数据库中的 accounts 表是否包含 custom_id 字段,你可以通过以下任一方法操作:
psql -h localhost -p 5432 -U postgres -d dify
查看 accounts 表结构:
\d accounts
Table "public.accounts"
Column | Type | ...
--------------------+-----------------------------+-----
id | uuid | ...
name | character varying(255) | ...
email | character varying(255) | ...
...(其他字段)...
放弃用 WORKCODE 作为系统 ID,改用 UUID(推荐)
accounts.id 必须是 UUID(由数据库或代码生成)
- 不再使用
WORKCODE - Excel 列最少化
- 所有用户密码统一设为
CnAI@2025 accounts.id使用 UUID(合法)- 保留
name和email
修改后 Excel 所需列(仅需 2 列):
|
张三 |
|
|
李四 |
修改后的代码:
import pandas as pd
import psycopg2
from psycopg2 import sql
import uuid
from datetime import datetime
import secrets
import hashlib
import binascii
import base64
import os
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# --- 配置 ---
DB_CONFIG = {
'dbname': 'dify',
'user': 'postgres',
'password': 'difyai123456',
'host': 'localhost',
'port': '5432'
}
XLS_FILE_PATH = r'E:\dify\dify相关\users.xls'
OUTPUT_KEYS_DIR = 'private_keys_to_upload'
# 固定密码
FIXED_PASSWORD = "CnAI@2025"
# --- Dify 密码加密函数 ---
def generate_dify_password_hash(plain_password: str):
salt_bytes = secrets.token_bytes(16)
dk_bytes = hashlib.pbkdf2_hmac("sha256", plain_password.encode("utf-8"), salt_bytes, 10000)
hex_hash = binascii.hexlify(dk_bytes)
final_hash = base64.b64encode(hex_hash).decode('utf-8')
final_salt = base64.b64encode(salt_bytes).decode('utf-8')
return final_hash, final_salt
# --- 密钥对生成 ---
def generate_key_pair():
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_pem = private_key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
return public_pem.decode('utf-8'), private_pem
# --- 主函数 ---
def main():
conn = None
successful = 0
failed = 0
passwords = {}
os.makedirs(OUTPUT_KEYS_DIR, exist_ok=True)
try:
print(f"[*] 读取 Excel: {XLS_FILE_PATH}")
df = pd.read_excel(XLS_FILE_PATH)
print(f"[+] 成功读取 {len(df)} 条记录。")
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
print("[+] 数据库连接成功!\n--- 开始导入 ---")
for idx, row in df.iterrows():
name = str(row['LOGINID']).strip()
email = str(row['EMAIL']).strip()
if not name or not email:
print(f"[!] 跳过第 {idx + 2} 行:LOGINID 或 EMAIL 为空")
failed += 1
continue
# 检查邮箱是否已存在
cur.execute("SELECT 1 FROM accounts WHERE email = %s;", (email,))
if cur.fetchone():
print(f"[!] 邮箱已存在,跳过: {email}")
failed += 1
continue
try:
# 使用固定密码
hashed_password, salt = generate_dify_password_hash(FIXED_PASSWORD)
public_key, private_key = generate_key_pair()
account_id = str(uuid.uuid4())
tenant_id = str(uuid.uuid4())
workspace_name = f"{name}'s Workspace"
ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 插入 accounts
cur.execute(
sql.SQL("INSERT INTO accounts (id, name, email, password, password_salt, status, created_at, updated_at, timezone) VALUES (%s, %s, %s, %s, %s, 'active', %s, %s, 'Asia/Shanghai')"),
(account_id, name, email, hashed_password, salt, ts, ts)
)
# 插入 tenants
cur.execute(
sql.SQL("INSERT INTO tenants (id, name, plan, status, created_at, updated_at, encrypt_public_key) VALUES (%s, %s, 'basic', 'normal', %s, %s, %s)"),
(tenant_id, workspace_name, ts, ts, public_key)
)
# 插入关联
cur.execute(
sql.SQL("INSERT INTO tenant_account_joins (tenant_id, account_id, role, current) VALUES (%s, %s, 'owner', TRUE)"),
(tenant_id, account_id)
)
# 保存私钥
key_dir = os.path.join(OUTPUT_KEYS_DIR, tenant_id)
os.makedirs(key_dir)
with open(os.path.join(key_dir, 'private.pem'), 'wb') as f:
f.write(private_key)
conn.commit()
successful += 1
passwords[email] = FIXED_PASSWORD
print(f" -> 成功: {name} ({email})")
except Exception as e:
conn.rollback()
print(f"[!] 导入失败 {name} ({email}): {e}")
failed += 1
print("\n--- 完成 ---")
except Exception as e:
print(f"\n[!] 致命错误: {e}")
if conn:
conn.rollback()
finally:
if conn:
cur.close()
conn.close()
print("\n[*] 数据库连接已关闭。")
# 输出结果
print(f"\n成功: {successful} | 失败/跳过: {failed}")
if passwords:
print("\n============ 用户密码清单 ============")
print("所有用户密码均为: CnAI@2025")
print("-" * 50)
for email in passwords:
print(email)
print("-" * 50)
if __name__ == "__main__":
print("批量导入用户到 Dify(固定密码 CnAI@2025)")
input("按 Enter 开始...")
main()
Dify 默认的 PostgreSQL 密码在 .env 文件中,通常是:
POSTGRES_PASSWORD=difyai123456
所以你的 DB_CONFIG 应为:
DB_CONFIG = {
'dbname': 'dify',
'user': 'postgres',
'password': 'difyai123456', # ⚠️ 不是 'postgres'
'host': 'localhost',
'port': '5432'
}
请检查
E:\dify\dify\docker\.env中的POSTGRES_PASSWORD值。
另外有博主提出
创建的空间,访问知识库报错呢Application error: a client-side exception has occurred while loading xx.xx.xx.xx (see the browser console for more information). 创建工作流也报错;Internal Server Error
上述是因为时区为空导致的,你可以登录账号以后,先去设置时区就可以避免这个报错,或者在创建账号的时候,带上时区字段,把字段设置为'Asia/Shanghai'。具体代码路径:
# 1. 插入到 accounts 表
account_sql = sql.SQL("""
INSERT INTO accounts (
id, name, email, password, password_salt, status, created_at, updated_at, timezone
) VALUES (%s, %s, %s, %s, %s, 'active', %s, %s, 'Asia/Shanghai');
""")添加时区字段。
第二个问题内部服务报错是没有设置默认语言,同理可以在数据库建账号的时候修改,或者在数据库里面修改:ALTER TABLE sites ALTER COLUMN default_language DROP NOT NULL;
其实反思回去,我们在社区版注册时,也是需要设置时区和语言的,新建账号这2个字段为空,所以报错了。
更多推荐
所有评论(0)