Implement Dify account creation with tenant association: 1. Create tenant named 'username's Workspace' 2. Set owner role for new account 3. Add normal role in ucas's Workspace 4. Fix UUID handling in tenant info retrieval
This commit is contained in:
parent
97ff5ffd24
commit
671886d57b
@ -7,6 +7,7 @@ import logging
|
||||
from datetime import datetime, timezone
|
||||
import psycopg2.extras
|
||||
from database import get_db_cursor, execute_query, execute_update
|
||||
from tenant_manager import TenantManager # Import TenantManager for tenant operations
|
||||
|
||||
# 配置日志
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -65,6 +66,26 @@ class AccountManager:
|
||||
cursor.connection.commit()
|
||||
|
||||
logger.info(f"用户 {username} 邮箱 {email} 注册成功!")
|
||||
|
||||
# 创建租户并关联角色
|
||||
try:
|
||||
# 创建租户(租户名为"账号名's Workspace")
|
||||
tenant_name = f"{username}'s Workspace"
|
||||
tenant_id = TenantManager.create_tenant(tenant_name)
|
||||
|
||||
# 关联新账号为自建租户的owner
|
||||
AccountManager.associate_with_tenant(result[0], tenant_id, "owner")
|
||||
|
||||
# 关联新账号为ucas's Workspace的normal角色
|
||||
ucas_tenant = TenantManager.get_tenant_by_name("ucas's Workspace")
|
||||
if ucas_tenant:
|
||||
AccountManager.associate_with_tenant(result[0], ucas_tenant['id'], "normal")
|
||||
else:
|
||||
logger.warning("ucas's Workspace not found - skipping normal role association")
|
||||
except Exception as e:
|
||||
logger.error(f"租户创建或关联失败: {e}")
|
||||
# Continue even if tenant operations fail since account creation succeeded
|
||||
|
||||
return {
|
||||
"id": result[0],
|
||||
"username": result[1],
|
||||
@ -289,11 +310,14 @@ class AccountManager:
|
||||
def get_account_tenants(account_id):
|
||||
"""获取账户关联的所有租户"""
|
||||
try:
|
||||
# 处理account_id可能是UUID对象或字符串的情况
|
||||
account_id_str = str(account_id) if hasattr(account_id, 'hex') else account_id
|
||||
|
||||
# 验证account_id是否为有效UUID
|
||||
try:
|
||||
uuid.UUID(account_id)
|
||||
uuid.UUID(account_id_str)
|
||||
except ValueError:
|
||||
logger.error(f"无效的account_id格式: {account_id}")
|
||||
logger.error(f"无效的account_id格式: {account_id_str}")
|
||||
return []
|
||||
|
||||
query = """
|
||||
@ -302,7 +326,7 @@ class AccountManager:
|
||||
JOIN tenant_account_joins j ON t.id = j.tenant_id
|
||||
WHERE j.account_id = %s::uuid;
|
||||
"""
|
||||
tenants = execute_query(query, (account_id,))
|
||||
tenants = execute_query(query, (account_id_str,))
|
||||
if not tenants:
|
||||
logger.info(f"账号 {account_id} 未关联任何租户")
|
||||
return []
|
||||
|
||||
15
api/app.py
15
api/app.py
@ -256,6 +256,21 @@ async def refresh_token(current_user: dict = Depends(get_current_user)):
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
# 账户管理路由组
|
||||
@app.post("/api/accounts/")
|
||||
async def create_account(account: AccountCreate):
|
||||
"""创建后台管理账户"""
|
||||
try:
|
||||
user = backend_account_manager.create_account(account.username, account.email, account.password)
|
||||
return {
|
||||
"user_id": str(user["id"]),
|
||||
"username": user["username"],
|
||||
"email": user["email"],
|
||||
"created_at": user["created_at"]
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"创建后台账户失败: {e}")
|
||||
raise HTTPException(status_code=400, detail="创建后台账户失败")
|
||||
|
||||
@app.post("/api/dify_accounts/")
|
||||
async def create_dify_account(account: AccountCreate):
|
||||
"""创建Dify账户"""
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAvRzEAE/Cbm+QKTqGQ5Xry+W60kTJeIeoQMb8G374PcbTxdPe
|
||||
0hURUaIH/h7jQ1rhDkNvkaBXC26v0q6ZkCBt89i4TopWCxewVzXb18DAcUswW2z2
|
||||
qQs70ssZqpSIn8sQbKfdZn20CD8C1Exkt18uI01VquWqMB30iwZvK8Wly94jm3WC
|
||||
6xiqnItOz64Y+prOQFhLr0trvoYEf0aKOAnrJr5qeOP6/XnxdF/bHJwOXCU0uFHA
|
||||
8RXHp58O60fDEZctdSTp9iUQeZidSXS0p3B85NRj/Y2rd/UOEiQ9Sj+DSXg0knXk
|
||||
CnYiLCeMBpV88bWyCwopRn3Im0haxzVrp9UYVQIDAQABAoIBAAuiuHXjHRhvFCn6
|
||||
ktuXtDFlyiU5MZg76ItLyE6SH7NVXMmq6+Z11gygG+kefLyJMUMPvSPy/0gRyw77
|
||||
H9xb/R09dAxJELA6szvlHB4Imw6dd1/jXW3bO2wQg7IY1NXYHa5BydU43hnfyUxy
|
||||
Atm8KSTnk4zMorhhL3RaX1QMeNNelXRzddzm82u1A/mItIMwyu3b3RXrDjIB/VsS
|
||||
VtY9C1hiXhcWvZdVcRMTRSQt4nbh7ap1QQG1oCHQKBDwBH4rryZHXioKBdKdJOvJ
|
||||
/1rNtvwWmUtsUwxVNDQ0GnQQlRhg4QoWoEvL9PzMqEEx/Oix2OPEZMa9ePJjUWoj
|
||||
j0udMkUCgYEA7YrwB/7ASDrNNC2x6dZvNHFAz5Ng9IDqjdBu+u6V61Aq5whoJFDh
|
||||
ysmywbSaz5NiVtTltr1wggkmWZJKvCLq3CJ+eRm0UfSAsWzeyeOii1+7eazW5zzp
|
||||
0PpIGdhNBoQf9jo6hn84iyCnxaehyTPDH+0HecFmYu2xyqGwPvFCqfMCgYEAy856
|
||||
tbXM/TSeO5GVX/giJ6OzebXcyannbyqeTOgSZ3wlm9pTtsyRhcIfjV4/VjnDkpby
|
||||
WTPsQqDjCQnWD6kPJXSwRjxQlhoT84FnDf3TtogxOkPUQgp7TRZRFPAy9WtisTyY
|
||||
EvZefWllw1U5G5WQclBLk4jtWLF8gPG3cya+PpcCgYAGkkJjpjuBhAbrO5xhYKpA
|
||||
5CUr2OLkwLA8bhiz3mfNU7FsJMJkxswIRR4p16o0tdoRluAn9wOmxUQHVFG29KxU
|
||||
nKutA/YGEmKBBgb/xMlZW7OfARYM3ZWGT3YxKNewp3UO5bt6vwg8aRHbGPET1NfA
|
||||
CVmcl6oScIrrlR+ADkKO2wKBgFE7HeuY53gwZbTmZLwYgTx9YcZr9vEl2AmV2fxb
|
||||
LyKx96dz0JP2WzYN1leZLfNnGI0vvq5RJKS1zWfH1NrvDgtmw+9qkNAS2sreU73e
|
||||
4mhJRh2r9UjRNq3V7EFFL3pj77UmqpI8QhMIVDgTmqxk42q8mDLYi4kjTLV0IiMy
|
||||
WPdHAoGBAJkJAhQ1JGSUAY+3QAKBWre8QwabKZdZIzDFq9DdQfnwiHkluueVUI63
|
||||
YY5U+wbff0dIAPBpeFGap56XYN8qy3/i6UkRNTTYji5kj6XqVWbyn2xU1IX8/KcC
|
||||
7AqeEsg04ArqGP0eDAw3gfSe4fgJ5Zoc5ZOkARcb/ajMJi+zA2+W
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA6GsVZmXAF+XkytRj9c+5tJP2YlWdYLQMo0HXfprVKobLUI5N
|
||||
+VMrCSreTCARTpsr2KtPV21zSgwVsRzA3zx9RgnAvHzDkJZtkSS1OHGKkA2CAU2T
|
||||
bJmpZWDEw5L/wOxx0QKA0tWBZzlWvRtXQjGqte/yNYF18TuxCOty6cIPUEQlVcYq
|
||||
QCwIeDfxGS94GI5ibwynvp7A8QfocR7DxYUZQdo8RSOlBz7d5ra/KVpIRb7I6fsV
|
||||
4U4qEYsx2GFFXZeZ6HWH7a+deC7Selv9Eycw9cvERWLQVE+FtGyDygbTHpwG7lPl
|
||||
8D9Yaf2NkXgibsxqHroK6OKyHIr0CkjlxfX1bQIDAQABAoIBACBSVAWLfO1V4T17
|
||||
z/LtUmwUb505Psul/mPt0iQfMCnb2QZ448saj8+UkJmRlBaUacKWAFCuPew8Rf/9
|
||||
ea0R4nINBSqxRxkiclZO/oy87YIe/sdjnG7r56zbikGQfdS8jQ9+A6BOZNKQHMnr
|
||||
tXTVJ2Jp3gsm19oWbQZKiA6OGEcXDyikRVipDoavDPBGoTBniJzUNwqD+JyasqdX
|
||||
QoUIqyJPLLTvXNN6pij8MnK+N86RccEVHdd/NdCDUGyh9ej4vs0dkg8BLTT+3fuw
|
||||
GR6WSwxZ7qFWl7PD0NhcrJfVQo2X1JNHm4BZFo5kNTJiWkKX+uqEp5QNAZmcXXXK
|
||||
N6owt9kCgYEA9vBZAE6JKvKBj+wNWeAvUGI/W5KTbYuk8NbPFoYzsqFaseewr0ei
|
||||
Iq4yzp31fmbIBbyx3iYEIuW2sdxMTEg9ZLi1yqh4qxn3aOhN0/dLkAFZlnBNsJVA
|
||||
izNsdFDtwy9EIT8vg4Tl1byC+ZzvT2cpP5PYVF4auD1DVUP3Zxet6wMCgYEA8PJV
|
||||
y18O6smWJPkgDfS2rbUHnP2dBJ6JznrauDGTzDlnd9LNL/sQBG4ZN/nDbo+M2++w
|
||||
84PeaWB+IQvpweQfZYC/CEA8gJqpQZzlL0TiRJTOAFlm45IEkY9IY9ZM2M0QfbmW
|
||||
Alt9uCxOQ/+O0TSg7s/hJgjFO7HVU1L+0TGc+s8CgYEA1vm6obe9VnQ0MvVPUjUH
|
||||
O7Wsbq4WRsHRYBHBy1wd2Z768/I+MPntvr9kX9fIBri33ba+KiVyaGcPO5wLmEUz
|
||||
nWHocXtlQZ+jceL7DlM1pzJlqcAvAlGNFSI5grGJq+iUKVV2qcqixAYPB/uFotyu
|
||||
Xjj5lcku/cHnwm0fDtLgh70CgYBeIg8quSSWzdL+9uVzDlwgWU10JBEoMNgd9xlk
|
||||
RNo9O2AVDCJl0GxdjrYDjGyx0RtzAZ3cRXSrLquYNZNJ0NoDSd1YV+BLTiE6JRXU
|
||||
9dCAgrJIqKTt26o1RAlAGAVF3jfHJq5YlW30ejrJjho52ZnWMxj/RwXnYH67Yv1y
|
||||
tTNjOQKBgH57/cRMpvuQ/kuLNYu1IS+EEZJ5WlDjEC0lmM2E54+Lb4D1UBMj+Y7V
|
||||
EZa9r+UQYm5DnsD4BQjiI1PWY/Iu39Cq3hlkxitOcTOds307xHKbivXancdwlwwx
|
||||
G+K1nGpNnAHSYCf+qaGdQgfG1LU9REKeLiPkIyt2zOtk6tZ4hmNq
|
||||
-----END RSA PRIVATE KEY-----
|
||||
54
docs/api/README.md
Normal file
54
docs/api/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Dify Admin API Documentation
|
||||
|
||||
## Overview
|
||||
This documentation covers the core API modules of the Dify Admin system. Each module provides specific functionality for managing the administration platform.
|
||||
|
||||
## Module Documentation
|
||||
|
||||
### [Account Management](account_manager.md)
|
||||
- User account creation and management
|
||||
- Password hashing and verification
|
||||
- Tenant association
|
||||
|
||||
### [Tenant Management](tenant_manager.md)
|
||||
- Workspace/tenant creation
|
||||
- RSA key pair generation
|
||||
- Tenant search and retrieval
|
||||
|
||||
### [Model Management](model_manager.md)
|
||||
- AI model configuration
|
||||
- Model encryption and storage
|
||||
- Bulk operations
|
||||
|
||||
### [Provider Management](provider_manager.md)
|
||||
- AI service provider management
|
||||
- API key encryption
|
||||
- Provider configuration
|
||||
|
||||
### [Database Operations](database.md)
|
||||
- Connection pooling
|
||||
- SQL execution helpers
|
||||
- Database initialization
|
||||
|
||||
### [Operation Logging](operation_logger.md)
|
||||
- Audit trail recording
|
||||
- Log querying
|
||||
- Log maintenance
|
||||
|
||||
### [Encryption Services](encryption.md)
|
||||
- Hybrid encryption (RSA + AES)
|
||||
- Key management
|
||||
- API key protection
|
||||
|
||||
## Getting Started
|
||||
To use these APIs, import the required module and instantiate the appropriate class:
|
||||
|
||||
```python
|
||||
from api.account_manager import AccountManager
|
||||
account_mgr = AccountManager()
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
- All sensitive operations use encryption
|
||||
- API keys are never stored in plaintext
|
||||
- Detailed logging for audit purposes
|
||||
97
docs/api/account_manager.md
Normal file
97
docs/api/account_manager.md
Normal file
@ -0,0 +1,97 @@
|
||||
# Account Manager API Documentation
|
||||
|
||||
## Overview
|
||||
The `AccountManager` class provides functionality for managing user accounts, including:
|
||||
- Account creation and authentication
|
||||
- Password management (hashing, verification, reset)
|
||||
- Account search and retrieval
|
||||
- Tenant association management
|
||||
|
||||
## Class Methods
|
||||
|
||||
### `hash_password(password, salt=None)`
|
||||
Generates a hashed password and salt for secure storage.
|
||||
|
||||
**Parameters:**
|
||||
- `password` (str): Plain text password to hash
|
||||
- `salt` (bytes, optional): Optional salt value. If None, generates new salt.
|
||||
|
||||
**Returns:**
|
||||
- Tuple of (base64_password_hashed, base64_salt)
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
hashed_pw, salt = AccountManager.hash_password("mysecurepassword")
|
||||
```
|
||||
|
||||
### `create_account(username, email, password)`
|
||||
Creates a new user account.
|
||||
|
||||
**Parameters:**
|
||||
- `username` (str): Unique username
|
||||
- `email` (str): User email address
|
||||
- `password` (str): Plain text password
|
||||
|
||||
**Returns:**
|
||||
- Dictionary with created account details:
|
||||
```python
|
||||
{
|
||||
"id": UUID,
|
||||
"username": str,
|
||||
"email": str,
|
||||
"created_at": datetime
|
||||
}
|
||||
```
|
||||
|
||||
### `get_user_by_username(username)`
|
||||
Retrieves user information by username.
|
||||
|
||||
**Parameters:**
|
||||
- `username` (str): Username to search for
|
||||
|
||||
**Returns:**
|
||||
- User dictionary or None if not found
|
||||
|
||||
### `search_accounts(search=None, page=1, page_size=10)`
|
||||
Searches accounts with pagination.
|
||||
|
||||
**Parameters:**
|
||||
- `search` (str): Optional search term
|
||||
- `page` (int): Page number (1-based)
|
||||
- `page_size` (int): Items per page
|
||||
|
||||
**Returns:**
|
||||
```python
|
||||
{
|
||||
"data": [user_dicts],
|
||||
"total": int
|
||||
}
|
||||
```
|
||||
|
||||
### `verify_password(plain_password, hashed_password, salt)`
|
||||
Verifies a password against stored hash.
|
||||
|
||||
**Parameters:**
|
||||
- `plain_password` (str): Password to verify
|
||||
- `hashed_password` (str): Stored password hash
|
||||
- `salt` (str): Password salt
|
||||
|
||||
**Returns:**
|
||||
- bool: True if password matches
|
||||
|
||||
### Password Management
|
||||
- `update_password(username, email, new_password)`
|
||||
- `reset_password(account_id)`
|
||||
|
||||
### Tenant Association
|
||||
- `associate_with_tenant(account_id, tenant_id, role, invited_by, current)`
|
||||
- `get_tenant_accounts(tenant_id)`
|
||||
- `get_account_tenants(account_id)`
|
||||
|
||||
## Error Handling
|
||||
All methods raise exceptions on failure and log errors using the module logger.
|
||||
|
||||
## Security Notes
|
||||
- Uses PBKDF2 with SHA-256 for password hashing
|
||||
- Generates random salts for each password
|
||||
- All sensitive operations are logged
|
||||
81
docs/api/database.md
Normal file
81
docs/api/database.md
Normal file
@ -0,0 +1,81 @@
|
||||
# Database API Documentation
|
||||
|
||||
## Overview
|
||||
The database module provides database connection management and basic operations for both PostgreSQL and SQLite databases. Key features include:
|
||||
- Connection pooling for PostgreSQL
|
||||
- Context managers for safe connection handling
|
||||
- Unified interface for both database types
|
||||
- SQL execution helpers
|
||||
|
||||
## Connection Management
|
||||
|
||||
### `get_db_connection(db_type='postgres')`
|
||||
Context manager for database connections.
|
||||
|
||||
**Parameters:**
|
||||
- `db_type`: 'postgres' or 'sqlite' (default: 'postgres')
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
with get_db_connection() as conn:
|
||||
# Use connection
|
||||
```
|
||||
|
||||
### `get_db_cursor(cursor_factory=None, db_type='postgres')`
|
||||
Context manager for database cursors with automatic commit/rollback.
|
||||
|
||||
**Parameters:**
|
||||
- `cursor_factory`: Optional cursor factory (e.g., psycopg2.extras.DictCursor)
|
||||
- `db_type`: 'postgres' or 'sqlite' (default: 'postgres')
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
with get_db_cursor() as cursor:
|
||||
cursor.execute("SELECT * FROM table")
|
||||
```
|
||||
|
||||
## SQL Execution Helpers
|
||||
|
||||
### `execute_query(query, params=None, cursor_factory=None, fetch_one=False, db_type='postgres')`
|
||||
Executes a query and returns results.
|
||||
|
||||
**Parameters:**
|
||||
- `query`: SQL query string
|
||||
- `params`: Query parameters (tuple/dict)
|
||||
- `cursor_factory`: Optional cursor factory
|
||||
- `fetch_one`: Return single row if True
|
||||
- `db_type`: Database type
|
||||
|
||||
**Returns:**
|
||||
- Query results (list or single row)
|
||||
|
||||
### `execute_update(query, params=None, db_type='postgres')`
|
||||
Executes an update/insert/delete query.
|
||||
|
||||
**Parameters:**
|
||||
- `query`: SQL query string
|
||||
- `params`: Query parameters (tuple/dict)
|
||||
- `db_type`: Database type
|
||||
|
||||
**Returns:**
|
||||
- Number of affected rows
|
||||
|
||||
## Initialization
|
||||
|
||||
### `init_sqlite_db()`
|
||||
Initializes SQLite database tables (api_endpoints, api_logs, api_users, api_operations).
|
||||
|
||||
## Configuration
|
||||
Module uses configuration from:
|
||||
- `DB_CONFIG` for PostgreSQL
|
||||
- `SQLITE_CONFIG` for SQLite
|
||||
|
||||
## Error Handling
|
||||
- All database operations are wrapped in try/except blocks
|
||||
- Errors are logged with detailed context
|
||||
- Connections are automatically returned to pool (PostgreSQL) or closed (SQLite)
|
||||
|
||||
## Best Practices
|
||||
- Always use context managers (`with` statements) for connections/cursors
|
||||
- For PostgreSQL, prefer connection pooling for better performance
|
||||
- For complex transactions, use explicit commit/rollback
|
||||
51
docs/api/encryption.md
Normal file
51
docs/api/encryption.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Encryption API Documentation
|
||||
|
||||
## Overview
|
||||
The `Encryption` class provides cryptographic functions including:
|
||||
- RSA key pair generation and management
|
||||
- Hybrid encryption (RSA + AES)
|
||||
- API key encryption
|
||||
- Secure key storage
|
||||
|
||||
## Class Methods
|
||||
|
||||
### Key Management
|
||||
- `load_public_key(public_key_path_or_content)`
|
||||
- Loads public key from file or content
|
||||
- `load_private_key(private_key_path)`
|
||||
- Loads private key from file
|
||||
|
||||
### Encryption/Decryption
|
||||
- `encrypt(text, public_key)`
|
||||
- Encrypts text using hybrid RSA+AES approach
|
||||
- Returns: Encrypted data with "HYBRID:" prefix
|
||||
- `decrypt(encrypted_text, private_key)`
|
||||
- Decrypts hybrid encrypted data
|
||||
- Returns: Original plaintext
|
||||
|
||||
### Specialized Methods
|
||||
- `encrypt_api_key(public_key_pem, api_key)`
|
||||
- Encrypts API keys with base64 encoding
|
||||
- Returns: base64 encoded encrypted key
|
||||
|
||||
## Security Features
|
||||
- Uses 2048-bit RSA keys
|
||||
- AES-256 for symmetric encryption
|
||||
- Random key generation for each operation
|
||||
- Secure key storage practices
|
||||
- Detailed error logging
|
||||
|
||||
## Error Handling
|
||||
- Validates all inputs
|
||||
- Raises exceptions for invalid operations
|
||||
- Logs all errors with context
|
||||
|
||||
## Example Usage
|
||||
```python
|
||||
# Encrypt data
|
||||
public_key = Encryption.load_public_key("public.pem")
|
||||
encrypted = Encryption.encrypt("secret data", public_key)
|
||||
|
||||
# Decrypt data
|
||||
private_key = Encryption.load_private_key("private.pem")
|
||||
decrypted = Encryption.decrypt(encrypted, private_key)
|
||||
75
docs/api/model_manager.md
Normal file
75
docs/api/model_manager.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Model Manager API Documentation
|
||||
|
||||
## Overview
|
||||
The `ModelManager` class provides functionality for managing AI models, including:
|
||||
- Adding models to tenants (both standard and Volc models)
|
||||
- Removing models
|
||||
- Querying model information
|
||||
- Bulk operations for all tenants
|
||||
|
||||
## Class Methods
|
||||
|
||||
### Model Addition
|
||||
- `add_model_for_tenant(tenant_id, public_key_pem, model_config)`
|
||||
- Adds a standard model to a tenant
|
||||
- Encrypts API keys using tenant's public key
|
||||
- Supports different model types (text-generation, embeddings, etc.)
|
||||
|
||||
- `add_volc_model_for_tenant(tenant_id, public_key_pem, model_config)`
|
||||
- Adds a Volc engine model to a tenant
|
||||
- Special handling for Volc-specific parameters
|
||||
|
||||
**Common Parameters:**
|
||||
- `tenant_id`: Target tenant UUID
|
||||
- `public_key_pem`: Tenant's public key for encryption
|
||||
- `model_config`: Dictionary containing model configuration
|
||||
|
||||
**Required Fields in model_config:**
|
||||
```python
|
||||
{
|
||||
"model_name": str, # Unique model name
|
||||
"provider_name": str, # Provider identifier
|
||||
"model_type": str, # Type of model
|
||||
"api_key": str # API key to encrypt (or volc_api_key for Volc models)
|
||||
}
|
||||
```
|
||||
|
||||
### Bulk Operations
|
||||
- `add_models_for_all_tenants(config_path)`
|
||||
- `add_volc_models_for_all_tenants(config_path)`
|
||||
- `add_models_for_tenant(tenant_name, config_path)`
|
||||
- `add_volc_models_for_tenant(tenant_name, config_path)`
|
||||
|
||||
### Model Removal
|
||||
- `delete_model_for_tenant(tenant_id, model_name)`
|
||||
- `delete_models_for_tenant(tenant_id)`
|
||||
- `delete_specific_model_for_all_tenants(model_name)`
|
||||
|
||||
### Query Methods
|
||||
- `get_tenant_models(tenant_id)`
|
||||
- Returns all models for a tenant
|
||||
- Format:
|
||||
```python
|
||||
{
|
||||
"tenant_id": str,
|
||||
"models": [{
|
||||
"id": str,
|
||||
"provider_name": str,
|
||||
"model_name": str,
|
||||
"model_type": str,
|
||||
"encrypted_config": dict,
|
||||
"is_valid": bool,
|
||||
"created_at": datetime,
|
||||
"updated_at": datetime
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
- All API keys are encrypted using tenant's public key
|
||||
- Supports different encryption configurations per model type
|
||||
- Detailed logging of all operations
|
||||
|
||||
## Error Handling
|
||||
- Raises exceptions for invalid configurations
|
||||
- Logs all errors with detailed context
|
||||
65
docs/api/operation_logger.md
Normal file
65
docs/api/operation_logger.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Operation Logger API Documentation
|
||||
|
||||
## Overview
|
||||
The `OperationLogger` class provides functionality for recording and managing operation logs, including:
|
||||
- Logging API operations
|
||||
- Querying operation logs with filters
|
||||
- Cleaning up old logs
|
||||
|
||||
## Class Methods
|
||||
|
||||
### `log_operation(user_id, operation_type, endpoint, parameters=None, status="SUCCESS")`
|
||||
Records an API operation in the database.
|
||||
|
||||
**Parameters:**
|
||||
- `user_id` (int): ID of the user performing the operation
|
||||
- `operation_type` (str): Type of operation (e.g., "CREATE", "UPDATE")
|
||||
- `endpoint` (str): API endpoint accessed
|
||||
- `parameters` (str, optional): Operation parameters in string format
|
||||
- `status` (str): Operation status (default: "SUCCESS")
|
||||
|
||||
**Returns:**
|
||||
- int: ID of the created log record
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
logger.log_operation(123, "LOGIN", "/api/auth/login", "username=test", "SUCCESS")
|
||||
```
|
||||
|
||||
### `get_operations(user_id=None, start_time=None, end_time=None, limit=100)`
|
||||
Queries operation logs with optional filters.
|
||||
|
||||
**Parameters:**
|
||||
- `user_id` (int, optional): Filter by user ID
|
||||
- `start_time` (datetime, optional): Earliest log time
|
||||
- `end_time` (datetime, optional): Latest log time
|
||||
- `limit` (int): Maximum number of logs to return (default: 100)
|
||||
|
||||
**Returns:**
|
||||
- List of log records with fields:
|
||||
- id
|
||||
- user_id
|
||||
- operation_type
|
||||
- endpoint
|
||||
- parameters
|
||||
- status
|
||||
- created_at
|
||||
|
||||
### `clean_old_logs(days=30)`
|
||||
Deletes logs older than specified number of days.
|
||||
|
||||
**Parameters:**
|
||||
- `days` (int): Delete logs older than this many days (default: 30)
|
||||
|
||||
**Returns:**
|
||||
- int: Number of logs deleted
|
||||
|
||||
## Error Handling
|
||||
- Raises `OperationLogError` for all failures
|
||||
- Detailed error logging through module logger
|
||||
|
||||
## Best Practices
|
||||
- Log all critical operations for audit trail
|
||||
- Regularly clean old logs to maintain performance
|
||||
- Use appropriate status values ("SUCCESS", "FAILED", etc.)
|
||||
- Include relevant parameters for debugging
|
||||
61
docs/api/provider_manager.md
Normal file
61
docs/api/provider_manager.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Provider Manager API Documentation
|
||||
|
||||
## Overview
|
||||
The `ProviderManager` class provides functionality for managing AI service providers, including:
|
||||
- Adding providers to tenants
|
||||
- Removing providers
|
||||
- Querying provider information
|
||||
- Bulk operations for all tenants
|
||||
|
||||
## Class Methods
|
||||
|
||||
### Provider Addition
|
||||
- `add_provider_for_tenant(tenant_id, public_key_pem, provider_config)`
|
||||
- Adds a provider to a tenant
|
||||
- Encrypts API keys using tenant's public key
|
||||
- Supports different provider types
|
||||
|
||||
**Parameters:**
|
||||
- `tenant_id`: Target tenant UUID
|
||||
- `public_key_pem`: Tenant's public key for encryption
|
||||
- `provider_config`: Dictionary containing provider configuration
|
||||
|
||||
**Required Fields in provider_config:**
|
||||
```python
|
||||
{
|
||||
"provider_name": str, # Unique provider name
|
||||
"config": dict # Provider configuration including API keys
|
||||
}
|
||||
```
|
||||
|
||||
### Bulk Operations
|
||||
- `add_providers_for_all_tenants(config_path)`
|
||||
- `add_providers_for_tenant(tenant_name, config_path)`
|
||||
|
||||
### Provider Removal
|
||||
- `delete_provider_for_tenant(tenant_id, provider_name)`
|
||||
- `delete_providers_for_tenant(tenant_id)`
|
||||
|
||||
### Query Methods
|
||||
- `get_providers_for_tenant(tenant_id)`
|
||||
- Returns all providers for a tenant
|
||||
- Format:
|
||||
```python
|
||||
[
|
||||
{
|
||||
"id": str,
|
||||
"provider_name": str,
|
||||
"provider_type": str,
|
||||
"is_valid": bool
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
- API keys are encrypted using tenant's public key
|
||||
- Detailed logging of all operations
|
||||
- Validation of required fields
|
||||
|
||||
## Error Handling
|
||||
- Raises exceptions for invalid configurations
|
||||
- Logs all errors with detailed context
|
||||
70
docs/api/tenant_manager.md
Normal file
70
docs/api/tenant_manager.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Tenant Manager API Documentation
|
||||
|
||||
## Overview
|
||||
The `TenantManager` class provides functionality for managing tenants (workspaces), including:
|
||||
- Tenant creation with RSA key pair generation
|
||||
- Key management (generation, storage)
|
||||
- Tenant search and retrieval
|
||||
|
||||
## Class Methods
|
||||
|
||||
### `generate_rsa_key_pair()`
|
||||
Generates a new RSA key pair for tenant encryption.
|
||||
|
||||
**Returns:**
|
||||
- Tuple of (public_key_pem, private_key)
|
||||
- public_key_pem: PEM formatted public key string
|
||||
- private_key: RSA private key object
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
public_key, private_key = TenantManager.generate_rsa_key_pair()
|
||||
```
|
||||
|
||||
### `save_private_key(tenant_id, private_key)`
|
||||
Securely stores a private key for a tenant.
|
||||
|
||||
**Parameters:**
|
||||
- `tenant_id` (UUID): Tenant identifier
|
||||
- `private_key`: RSA private key object
|
||||
|
||||
**Returns:**
|
||||
- Path to stored private key file
|
||||
|
||||
### `create_tenant(workspace_name)`
|
||||
Creates a new tenant with cryptographic keys.
|
||||
|
||||
**Parameters:**
|
||||
- `workspace_name` (str): Name for the new tenant/workspace
|
||||
|
||||
**Returns:**
|
||||
- UUID of created tenant
|
||||
|
||||
**Process:**
|
||||
1. Generates RSA key pair
|
||||
2. Stores private key securely
|
||||
3. Creates tenant record with public key
|
||||
|
||||
### Query Methods
|
||||
- `get_tenant_by_name(workspace_name)`: Retrieves tenant by name
|
||||
- `get_all_tenants()`: Lists all tenants
|
||||
- `search_tenants(search_term)`: Searches tenants by name
|
||||
|
||||
**Return Format:**
|
||||
```python
|
||||
{
|
||||
"id": str/UUID,
|
||||
"name": str,
|
||||
"encrypt_public_key": str,
|
||||
"created_at": datetime
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
- Uses 2048-bit RSA keys for encryption
|
||||
- Private keys stored in secure directory structure
|
||||
- Public keys stored in database for encryption
|
||||
- All operations are logged
|
||||
|
||||
## Error Handling
|
||||
Methods raise exceptions on failure and log errors using the module logger.
|
||||
48
docs/architecture/overview.md
Normal file
48
docs/architecture/overview.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Dify Admin System Architecture
|
||||
|
||||
## Overall Architecture
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Frontend] -->|API Calls| B[Backend]
|
||||
B --> C[Database]
|
||||
B --> D[External Services]
|
||||
C --> E[(PostgreSQL)]
|
||||
C --> F[(SQLite)]
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
### Frontend
|
||||
- **Framework**: Vue 3 + TypeScript
|
||||
- **State Management**: Pinia
|
||||
- **UI Library**: Element Plus
|
||||
- **Routing**: Vue Router
|
||||
|
||||
### Backend
|
||||
- **API Services**:
|
||||
- Account Management
|
||||
- Tenant Management
|
||||
- Model Management
|
||||
- Provider Management
|
||||
- **Core Modules**:
|
||||
- Database Access
|
||||
- Encryption
|
||||
- Operation Logging
|
||||
|
||||
## Data Flow
|
||||
1. User actions trigger API calls
|
||||
2. Backend processes requests with business logic
|
||||
3. Data persisted to database
|
||||
4. Responses returned to frontend
|
||||
5. Frontend updates state and UI
|
||||
|
||||
## Security Architecture
|
||||
- JWT Authentication
|
||||
- RSA Encryption for sensitive data
|
||||
- Role-based Access Control
|
||||
- Audit logging
|
||||
|
||||
## Deployment
|
||||
- Frontend: Static hosting (Vite)
|
||||
- Backend: Containerized microservices
|
||||
- Database: PostgreSQL (primary), SQLite (logging)
|
||||
31
docs/web/README.md
Normal file
31
docs/web/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Web Frontend Documentation
|
||||
|
||||
## Technology Stack
|
||||
- **Framework**: Vue 3 (Composition API)
|
||||
- **UI Library**: Element Plus
|
||||
- **State Management**: Pinia
|
||||
- **Routing**: Vue Router
|
||||
- **HTTP Client**: Axios
|
||||
|
||||
## Core Architecture
|
||||
|
||||
### Application Initialization
|
||||
1. Creates Vue app instance
|
||||
2. Configures router and store
|
||||
3. Initializes Element Plus components
|
||||
4. Checks for existing auth token
|
||||
5. Mounts to DOM element (#app)
|
||||
|
||||
### Key Files
|
||||
- `main.ts`: Application entry point
|
||||
- `App.vue`: Root component
|
||||
- `router/index.ts`: Route configuration
|
||||
- `store/index.ts`: State management setup
|
||||
|
||||
## Module Structure
|
||||
- `api/`: API service layer
|
||||
- `axios/`: HTTP client configuration
|
||||
- `router/`: Navigation logic
|
||||
- `store/`: Global state management
|
||||
- `utils/`: Helper functions
|
||||
- `views/`: Page components
|
||||
48
docs/web/api.md
Normal file
48
docs/web/api.md
Normal file
@ -0,0 +1,48 @@
|
||||
# API Service Documentation
|
||||
|
||||
## Architecture
|
||||
- **HTTP Client**: Axios with custom wrapper
|
||||
- **Modular Structure**: Feature-based organization
|
||||
- **Type Safety**: TypeScript interfaces
|
||||
|
||||
## Auth Module
|
||||
|
||||
### Methods
|
||||
- `login(data)`: User authentication
|
||||
- Parameters: `{username, password}`
|
||||
- Returns: `{access_token}`
|
||||
- Content-Type: `application/json`
|
||||
|
||||
- `register(formData)`: User registration
|
||||
- Parameters: FormData
|
||||
- Returns: `{user_id}`
|
||||
- Content-Type: `multipart/form-data`
|
||||
|
||||
- `getPublicKey()`: Gets RSA public key
|
||||
- Returns: Public key string
|
||||
- Error Handling: Custom error messages
|
||||
|
||||
- `refreshToken()`: Refreshes access token
|
||||
- Returns: New `{access_token}`
|
||||
|
||||
## Request Wrapper
|
||||
Located in `axios/service.ts`:
|
||||
- Base URL configuration
|
||||
- Request/response interceptors
|
||||
- Error handling
|
||||
- Type definitions
|
||||
|
||||
## Type Definitions
|
||||
Each module has corresponding type definitions:
|
||||
```typescript
|
||||
// Example from auth/types.ts
|
||||
interface LoginParams {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
- Custom error messages
|
||||
- Type-safe error responses
|
||||
- Console logging for debugging
|
||||
37
docs/web/router.md
Normal file
37
docs/web/router.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Router Documentation
|
||||
|
||||
## Route Configuration
|
||||
|
||||
### Public Routes
|
||||
- `/login`: Login page
|
||||
- `/register`: Registration page
|
||||
|
||||
### Authenticated Routes (Nested under Layout)
|
||||
- `/dashboard`: Main dashboard
|
||||
- `/user`: User management
|
||||
- `/account`: Account settings
|
||||
- `/model`: Model management
|
||||
|
||||
## Route Meta Fields
|
||||
- `requiresAuth`: Boolean indicating if authentication is required
|
||||
|
||||
## Route Guards
|
||||
### Authentication Check
|
||||
1. Checks for `access_token` in localStorage
|
||||
2. Redirects to `/login` if:
|
||||
- Route requires auth (`meta.requiresAuth`)
|
||||
- No valid token found
|
||||
|
||||
## Technical Details
|
||||
- **Mode**: HTML5 History Mode
|
||||
- **Component Loading**: Dynamic imports (code splitting)
|
||||
- **Base Path**: Project root (`/`)
|
||||
|
||||
## Example Usage
|
||||
```javascript
|
||||
// Adding a new route
|
||||
{
|
||||
path: 'new-route',
|
||||
component: () => import('../views/NewRoute.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
}
|
||||
36
docs/web/store.md
Normal file
36
docs/web/store.md
Normal file
@ -0,0 +1,36 @@
|
||||
# State Management Documentation
|
||||
|
||||
## Architecture
|
||||
- **Library**: Pinia (Vue 3 recommended state management)
|
||||
- **Structure**: Modular stores
|
||||
- **Reactivity**: Composition API style
|
||||
|
||||
## User Store
|
||||
|
||||
### State Properties
|
||||
- `token`: Authentication token
|
||||
- `userInfo`: User profile data
|
||||
|
||||
### Methods
|
||||
- `setToken(token)`: Updates token in store and localStorage
|
||||
- `setUserInfo(info)`: Updates user profile
|
||||
- `login(params)`: Handles login flow
|
||||
- Parameters: `{username, password}`
|
||||
- Returns: `{access_token}`
|
||||
- `getUserInfo()`: Returns current user info
|
||||
|
||||
### Persistence
|
||||
- Token is automatically synced with localStorage
|
||||
- Uses `utils/storage` helper methods
|
||||
|
||||
## Usage Example
|
||||
```javascript
|
||||
import { useUserStore } from '@/store'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
// Login
|
||||
await userStore.login({username: 'test', password: '123'})
|
||||
|
||||
// Get user info
|
||||
const user = userStore.getUserInfo()
|
||||
@ -46,14 +46,19 @@ export const toggleAccountStatus = (id: string) =>
|
||||
url: `/api/accounts/${id}/toggle-status`
|
||||
})
|
||||
|
||||
export const createAccount = (data: { username: string }) =>
|
||||
export const createAccount = (data: { username: string; email: string; password?: string }) =>
|
||||
request<{
|
||||
message: string
|
||||
account: AccountItem
|
||||
user_id: string
|
||||
username: string
|
||||
email: string
|
||||
created_at: string
|
||||
}>({
|
||||
method: 'POST',
|
||||
url: '/api/accounts',
|
||||
data
|
||||
url: '/api/accounts/',
|
||||
data: {
|
||||
...data,
|
||||
password: data.password || 'TempPassword123!' // Default password
|
||||
}
|
||||
})
|
||||
|
||||
// Dify账号相关API
|
||||
|
||||
58
web/src/views/Account/components/CreateAccountDialog.vue
Normal file
58
web/src/views/Account/components/CreateAccountDialog.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="创建账号" width="500px">
|
||||
<el-form ref="form" label-width="100px">
|
||||
<el-form-item label="用户名" prop="username" required>
|
||||
<el-input
|
||||
:model-value="formData.username"
|
||||
@update:model-value="val => formData.username = val"
|
||||
placeholder="4-20位字母数字或下划线"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email" required>
|
||||
<el-input
|
||||
:model-value="formData.email"
|
||||
@update:model-value="val => formData.email = val"
|
||||
placeholder="请输入有效邮箱地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const visible = ref(false)
|
||||
const formData = reactive({
|
||||
username: '',
|
||||
email: ''
|
||||
})
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const show = () => {
|
||||
visible.value = true
|
||||
formData.username = ''
|
||||
formData.email = ''
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!/^[a-zA-Z0-9_]{4,20}$/.test(formData.username)) {
|
||||
ElMessage.error('用户名必须为4-20位字母数字或下划线')
|
||||
return
|
||||
}
|
||||
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||||
ElMessage.error('请输入有效的邮箱地址')
|
||||
return
|
||||
}
|
||||
emit('success', { ...formData })
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
defineExpose({ show })
|
||||
</script>
|
||||
29
web/src/views/Account/components/CreateAccountForm.vue
Normal file
29
web/src/views/Account/components/CreateAccountForm.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="form" label-width="80px">
|
||||
<el-form-item label="用户名" prop="username" required>
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
placeholder="4-20位字母数字或下划线"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email" required>
|
||||
<el-input
|
||||
v-model="form.email"
|
||||
placeholder="请输入有效邮箱地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
defineProps({
|
||||
form: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -91,17 +91,20 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<CreateAccountDialog ref="createDialog" @success="handleCreateSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import CreateAccountDialog from './components/CreateAccountDialog.vue'
|
||||
import {
|
||||
fetchAccounts,
|
||||
toggleAccountStatus,
|
||||
createAccount,
|
||||
resetPassword,
|
||||
getDifyAccount
|
||||
getDifyAccount,
|
||||
createDifyAccount
|
||||
} from '@/api/account'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
@ -117,6 +120,10 @@ interface Account {
|
||||
const accountList = ref<Account[]>([])
|
||||
const filteredAccountList = ref<Account[]>([])
|
||||
const tenantList = ref<any[]>([])
|
||||
const createForm = ref({
|
||||
username: '',
|
||||
email: ''
|
||||
})
|
||||
const searchQuery = ref('')
|
||||
const loading = ref(false)
|
||||
const tenantDialogVisible = ref(false)
|
||||
@ -155,21 +162,27 @@ const handleToggleStatus = async (account: Account) => {
|
||||
}
|
||||
}
|
||||
|
||||
const createDialog = ref()
|
||||
|
||||
const handleCreate = () => {
|
||||
ElMessageBox.prompt('请输入新账号用户名', '创建账号', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: /^[a-zA-Z0-9_]{4,20}$/,
|
||||
inputErrorMessage: '用户名必须为4-20位字母数字或下划线'
|
||||
}).then(async ({ value }) => {
|
||||
try {
|
||||
await createAccount({ username: value })
|
||||
ElMessage.success('账号创建成功')
|
||||
await fetchAccountData()
|
||||
} catch (error) {
|
||||
ElMessage.error('账号创建失败')
|
||||
}
|
||||
})
|
||||
createDialog.value?.show()
|
||||
}
|
||||
|
||||
const handleCreateSuccess = async (formData) => {
|
||||
try {
|
||||
// Create Dify account only
|
||||
await createDifyAccount({
|
||||
...formData,
|
||||
password: 'Welcome123!'
|
||||
})
|
||||
|
||||
ElMessage.success('Dify账号创建成功')
|
||||
await fetchAccountData()
|
||||
} catch (error) {
|
||||
console.error('创建Dify账号失败:', error)
|
||||
const errorMsg = error.response?.data?.message || error.message
|
||||
ElMessage.error(`创建Dify账号失败: ${errorMsg}`)
|
||||
}
|
||||
}
|
||||
|
||||
const handleResetPassword = async (account: Account) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user