update docs
This commit is contained in:
parent
c55d17bd0f
commit
cc4953da50
4 changed files with 2181 additions and 31 deletions
1450
implementation/implementation_doc_userauth_process_concept.md
Normal file
1450
implementation/implementation_doc_userauth_process_concept.md
Normal file
File diff suppressed because it is too large
Load diff
400
implementation/implementation_doc_userauth_ui_adaptations.md
Normal file
400
implementation/implementation_doc_userauth_ui_adaptations.md
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
# User Authentication UI Adaptations
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the necessary UI changes and adaptations required to implement the magic link-based user authentication process described in `doc_userauth_process_concept.md`.
|
||||
|
||||
**Last Updated**: Based on codebase analysis of current frontend implementation.
|
||||
|
||||
## Current Frontend State
|
||||
|
||||
### Existing Pages
|
||||
1. **`frontend_agents/public/login.html`**
|
||||
- Contains login form with username/password fields
|
||||
- Has buttons for Microsoft and Google authentication
|
||||
- Has registration link
|
||||
- **Missing**: Password reset button
|
||||
|
||||
2. **`frontend_agents/public/register.html`**
|
||||
- Contains registration form with:
|
||||
- Username field
|
||||
- Password field (required)
|
||||
- Confirm password field (required)
|
||||
- Email field
|
||||
- Full name field
|
||||
- Language selector
|
||||
- **Needs modification**: Remove password fields, add email-only registration
|
||||
|
||||
3. **`frontend_agents/public/js/security/auth.js`**
|
||||
- Contains `setupRegisterPage()` function
|
||||
- Contains `validateRegistrationForm()` function that requires password
|
||||
- Contains email validation logic
|
||||
- **Needs modification**: Remove password validation, add shared email validation function
|
||||
|
||||
4. **`frontend_agents/public/js/shared/apiCalls.js`**
|
||||
- Contains `register()` function that sends password
|
||||
- **Missing**: Password reset request and reset password API calls
|
||||
|
||||
## Required UI Changes
|
||||
|
||||
### 1. Login Page (`frontend_agents/public/login.html`)
|
||||
|
||||
#### Changes Required:
|
||||
- Add "Password Reset" button/link below the login form
|
||||
- Button should link to `/password-reset-request.html`
|
||||
- Style should match existing button styles (use `btn` class with appropriate variant)
|
||||
|
||||
#### Implementation:
|
||||
```html
|
||||
<!-- Add after login form, before register-options div -->
|
||||
<div class="password-reset-link">
|
||||
<a href="password-reset-request.html" class="btn btn-link">
|
||||
<i class="fas fa-key"></i> Passwort zurücksetzen
|
||||
</a>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Styling Considerations:
|
||||
- Use existing CSS classes from `htmlparts/styles.css`
|
||||
- Match styling with registration link
|
||||
- Ensure responsive design matches login page layout
|
||||
|
||||
### 2. Registration Page (`frontend_agents/public/register.html`)
|
||||
|
||||
#### Changes Required:
|
||||
- **Remove** password and confirm password fields
|
||||
- **Keep** username, email, fullName, language fields
|
||||
- Update form validation to not require password
|
||||
- Update success message to indicate email will be sent
|
||||
- Add spam folder reminder message
|
||||
|
||||
#### Implementation:
|
||||
```html
|
||||
<!-- Remove these fields: -->
|
||||
<!-- <div class="login-form-group">
|
||||
<label for="password">Passwort*</label>
|
||||
<input type="password" id="password" name="password" required minlength="8">
|
||||
<div class="field-error"></div>
|
||||
</div>
|
||||
<div class="login-form-group">
|
||||
<label for="confirm-password">Passwort bestätigen*</label>
|
||||
<input type="password" id="confirm-password" name="confirm-password" required>
|
||||
<div class="field-error"></div>
|
||||
</div> -->
|
||||
|
||||
<!-- Add info message after form: -->
|
||||
<div class="registration-info">
|
||||
<p>Nach der Registrierung erhalten Sie eine E-Mail mit einem Link zum Setzen Ihres Passworts.</p>
|
||||
<p class="spam-reminder">Bitte prüfen Sie auch Ihren Spam-Ordner, falls Sie keine E-Mail erhalten.</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### JavaScript Changes (`frontend_agents/public/js/security/auth.js`):
|
||||
- Update `validateRegistrationForm()` to remove password validation
|
||||
- Update `setupRegisterPage()` to handle no-password registration
|
||||
- Create shared `validateEmailFormat()` function for reuse
|
||||
|
||||
### 3. New Page: Password Reset Request (`frontend_agents/public/password-reset-request.html`)
|
||||
|
||||
#### Purpose:
|
||||
Allow users to request a password reset by entering their email address.
|
||||
|
||||
#### Structure:
|
||||
- Similar layout to `register.html`
|
||||
- Single email input field
|
||||
- Submit button
|
||||
- Link back to login page
|
||||
- Success/error message area
|
||||
|
||||
#### Implementation:
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PowerOn - Passwort zurücksetzen</title>
|
||||
<link rel="stylesheet" href="./htmlparts/styles_variables.css">
|
||||
<link rel="stylesheet" href="./htmlparts/styles_base.css">
|
||||
<link rel="stylesheet" href="./htmlparts/styles.css">
|
||||
<link rel="stylesheet" href="./htmlparts/styles_icons.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<h1>Passwort zurücksetzen</h1>
|
||||
<div id="reset-request-error" class="login-error-message"></div>
|
||||
<form id="password-reset-request-form">
|
||||
<div class="login-form-group">
|
||||
<label for="email">E-Mail-Adresse*</label>
|
||||
<input type="email" id="email" name="email" required autocomplete="email">
|
||||
<div class="field-error"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success login-btn">Reset-Link anfordern</button>
|
||||
</form>
|
||||
<div class="register-link">
|
||||
<p>Zurück zum <a href="login.html">Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Benötigte globale Utilities -->
|
||||
<script type="module" src="js/shared/utils.js"></script>
|
||||
|
||||
<!-- Module -->
|
||||
<script type="module" src="js/security/passwordResetRequest.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### JavaScript Module (`frontend_agents/public/js/security/passwordResetRequest.js`):
|
||||
- Handle form submission
|
||||
- Validate email format using shared function from `auth.js`
|
||||
- Call password reset request API
|
||||
- Show generic success message
|
||||
- Redirect to login page after showing message
|
||||
|
||||
### 4. New Page: Password Reset (`frontend_agents/public/reset.html`)
|
||||
|
||||
#### Purpose:
|
||||
Allow users to set a new password using the token from the magic link.
|
||||
|
||||
#### Structure:
|
||||
- Similar layout to `register.html`
|
||||
- Password field (with strength indicator)
|
||||
- Confirm password field
|
||||
- Submit button
|
||||
- Extract token from URL parameter (`?token=<UUID>`)
|
||||
- Success/error message area
|
||||
|
||||
#### Implementation:
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PowerOn - Neues Passwort setzen</title>
|
||||
<link rel="stylesheet" href="./htmlparts/styles_variables.css">
|
||||
<link rel="stylesheet" href="./htmlparts/styles_base.css">
|
||||
<link rel="stylesheet" href="./htmlparts/styles.css">
|
||||
<link rel="stylesheet" href="./htmlparts/styles_icons.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<h1>Neues Passwort setzen</h1>
|
||||
<div id="reset-error" class="login-error-message"></div>
|
||||
<form id="reset-password-form">
|
||||
<div class="login-form-group">
|
||||
<label for="password">Neues Passwort*</label>
|
||||
<input type="password" id="password" name="password" required minlength="8">
|
||||
<div class="field-error"></div>
|
||||
<small class="password-hint">Mindestens 8 Zeichen</small>
|
||||
</div>
|
||||
<div class="login-form-group">
|
||||
<label for="confirm-password">Passwort bestätigen*</label>
|
||||
<input type="password" id="confirm-password" name="confirm-password" required>
|
||||
<div class="field-error"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success login-btn">Passwort setzen</button>
|
||||
</form>
|
||||
<div class="register-link">
|
||||
<p>Zurück zum <a href="login.html">Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Benötigte globale Utilities -->
|
||||
<script type="module" src="js/shared/utils.js"></script>
|
||||
|
||||
<!-- Module -->
|
||||
<script type="module" src="js/security/reset.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### JavaScript Module (`frontend_agents/public/js/security/reset.js`):
|
||||
- Extract token from URL parameter
|
||||
- Validate token format (UUID)
|
||||
- Handle form submission
|
||||
- Validate password strength (min 8 chars, match requirements from config)
|
||||
- Validate password confirmation matches
|
||||
- Call password reset API
|
||||
- Show success message with spam folder reminder
|
||||
- Redirect to login page after 3 seconds
|
||||
|
||||
### 5. API Calls (`frontend_agents/public/js/shared/apiCalls.js`)
|
||||
|
||||
#### New Functions Required:
|
||||
|
||||
1. **`requestPasswordReset(email)`**
|
||||
```javascript
|
||||
requestPasswordReset: async function(email) {
|
||||
try {
|
||||
return await privateApi.post('/api/local/password-reset-request', { email });
|
||||
} catch (error) {
|
||||
ui.log.error('Password reset request error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **`resetPassword(token, password)`**
|
||||
```javascript
|
||||
resetPassword: async function(token, password) {
|
||||
try {
|
||||
return await privateApi.post('/api/local/password-reset', { token, password });
|
||||
} catch (error) {
|
||||
ui.log.error('Password reset error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Update `register()` function**
|
||||
- Remove password from request body
|
||||
- Update to handle new registration flow (no password required)
|
||||
|
||||
### 6. Shared Email Validation (`frontend_agents/public/js/security/auth.js`)
|
||||
|
||||
#### New Function:
|
||||
```javascript
|
||||
/**
|
||||
* Validates email format
|
||||
* @param {string} email - Email address to validate
|
||||
* @returns {boolean} - True if valid, false otherwise
|
||||
*/
|
||||
export function validateEmailFormat(email) {
|
||||
if (!email) return false;
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email.trim());
|
||||
}
|
||||
```
|
||||
|
||||
#### Usage:
|
||||
- Used by registration form
|
||||
- Used by password reset request form
|
||||
- Ensures consistent email validation across the application
|
||||
|
||||
## UI/UX Considerations
|
||||
|
||||
### Error Handling
|
||||
|
||||
1. **Registration Errors**:
|
||||
- Username already exists → Show error on username field
|
||||
- Email already exists → Show generic success (security: don't reveal email exists)
|
||||
- Email sending fails → Show generic success (don't reveal email issues)
|
||||
|
||||
2. **Password Reset Request Errors**:
|
||||
- Invalid email format → Show error on email field
|
||||
- Email not found → Show generic success (security: don't reveal email doesn't exist)
|
||||
- Rate limiting → Show error message
|
||||
|
||||
3. **Password Reset Errors**:
|
||||
- Invalid/expired token → Show error message, link back to password reset request
|
||||
- Password too weak → Show specific requirements
|
||||
- Password mismatch → Show error on confirm password field
|
||||
|
||||
### Success Messages
|
||||
|
||||
1. **Registration Success**:
|
||||
```
|
||||
"Registrierung erfolgreich! Bitte prüfen Sie Ihre E-Mail, um Ihr Passwort zu setzen.
|
||||
Falls Sie keine E-Mail erhalten, prüfen Sie bitte auch Ihren Spam-Ordner."
|
||||
```
|
||||
|
||||
2. **Password Reset Request Success**:
|
||||
```
|
||||
"Falls ein Konto mit dieser E-Mail-Adresse existiert, wurde ein Reset-Link gesendet.
|
||||
Bitte prüfen Sie Ihre E-Mail und auch Ihren Spam-Ordner."
|
||||
```
|
||||
|
||||
3. **Password Reset Success**:
|
||||
```
|
||||
"Passwort erfolgreich gesetzt! Sie werden zum Login weitergeleitet..."
|
||||
```
|
||||
|
||||
### Accessibility
|
||||
|
||||
- All form fields should have proper labels
|
||||
- Error messages should be associated with form fields using ARIA attributes
|
||||
- Success messages should be announced to screen readers
|
||||
- Form validation should provide clear, actionable feedback
|
||||
|
||||
### Responsive Design
|
||||
|
||||
- All pages should work on mobile devices
|
||||
- Form layouts should adapt to smaller screens
|
||||
- Buttons should be appropriately sized for touch interfaces
|
||||
- Error messages should be readable on all screen sizes
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Registration Flow
|
||||
- [ ] User can register without password
|
||||
- [ ] Email validation works correctly
|
||||
- [ ] Success message displays correctly
|
||||
- [ ] Redirect to login works
|
||||
- [ ] Error handling for duplicate username
|
||||
- [ ] Error handling for duplicate email (should show generic success)
|
||||
|
||||
### Password Reset Request Flow
|
||||
- [ ] User can access password reset request page from login
|
||||
- [ ] Email validation works correctly
|
||||
- [ ] Success message displays correctly
|
||||
- [ ] Redirect to login works
|
||||
- [ ] Error handling for invalid email format
|
||||
- [ ] Error handling for rate limiting
|
||||
|
||||
### Password Reset Flow
|
||||
- [ ] User can access reset page with valid token
|
||||
- [ ] Token extraction from URL works
|
||||
- [ ] Password validation works correctly
|
||||
- [ ] Password confirmation validation works
|
||||
- [ ] Success message displays correctly
|
||||
- [ ] Redirect to login works after 3 seconds
|
||||
- [ ] Error handling for invalid token
|
||||
- [ ] Error handling for expired token
|
||||
- [ ] Error handling for weak password
|
||||
|
||||
### Integration Testing
|
||||
- [ ] End-to-end registration flow works
|
||||
- [ ] End-to-end password reset flow works
|
||||
- [ ] Email links work correctly
|
||||
- [ ] Token expiration handling works
|
||||
- [ ] Multiple reset requests invalidate old tokens
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Backend Changes First** (prerequisites):
|
||||
- Add resetToken fields to UserInDB model
|
||||
- Implement password reset endpoints
|
||||
- Implement email sending functionality
|
||||
|
||||
2. **Frontend API Layer**:
|
||||
- Add password reset API calls to `apiCalls.js`
|
||||
- Update registration API call
|
||||
|
||||
3. **Frontend Pages**:
|
||||
- Create `password-reset-request.html`
|
||||
- Create `reset.html`
|
||||
- Update `login.html` (add reset button)
|
||||
- Update `register.html` (remove password fields)
|
||||
|
||||
4. **Frontend JavaScript**:
|
||||
- Create `passwordResetRequest.js`
|
||||
- Create `reset.js`
|
||||
- Update `auth.js` (remove password validation, add email validation function)
|
||||
- Update `register.js` if needed
|
||||
|
||||
5. **Testing**:
|
||||
- Test each flow independently
|
||||
- Test integration between frontend and backend
|
||||
- Test error scenarios
|
||||
- Test edge cases
|
||||
|
||||
## Notes
|
||||
|
||||
- All text should be in German to match existing UI (`login.html` uses German)
|
||||
- CSS classes should match existing patterns from `htmlparts/styles.css`
|
||||
- Form validation should use existing patterns from `auth.js`
|
||||
- Error handling should use existing patterns from `auth.js` (showFieldError, clearFieldError)
|
||||
- Success messages should use existing patterns (showSuccessMessage)
|
||||
- API calls should use existing patterns from `apiCalls.js` (privateApi.post, handleResponse)
|
||||
265
reviews/connector_generic_refactoring_analysis.md
Normal file
265
reviews/connector_generic_refactoring_analysis.md
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
# Connector Generic Refactoring Analysis
|
||||
|
||||
## Problem
|
||||
The `connectorDbPostgre.py` has **hardcoded field names** that make it non-generic:
|
||||
- Lines 61-70: Hardcoded field names in `_get_model_fields()` to force JSONB type
|
||||
- Lines 689-700: Hardcoded field names in `_loadTable()` for None value defaults
|
||||
- Lines 891-902: Hardcoded field names in `getRecordset()` for None value defaults
|
||||
|
||||
## Hardcoded Fields in Connector
|
||||
|
||||
### Location 1: `_get_model_fields()` (Lines 61-70)
|
||||
```python
|
||||
or field_name in [
|
||||
"execParameters",
|
||||
"expectedDocumentFormats",
|
||||
"resultDocuments",
|
||||
"logs",
|
||||
"messages",
|
||||
"stats",
|
||||
"tasks",
|
||||
]
|
||||
```
|
||||
|
||||
### Location 2: `_loadTable()` (Lines 689-700)
|
||||
```python
|
||||
if field_name in [
|
||||
"logs",
|
||||
"messages",
|
||||
"tasks",
|
||||
"expectedDocumentFormats",
|
||||
"resultDocuments",
|
||||
]:
|
||||
record[field_name] = []
|
||||
elif field_name in ["execParameters", "stats"]:
|
||||
record[field_name] = {}
|
||||
```
|
||||
|
||||
### Location 3: `getRecordset()` (Lines 891-902)
|
||||
```python
|
||||
if field_name in [
|
||||
"logs",
|
||||
"messages",
|
||||
"tasks",
|
||||
"expectedDocumentFormats",
|
||||
"resultDocuments",
|
||||
]:
|
||||
record[field_name] = []
|
||||
elif field_name in ["execParameters", "stats"]:
|
||||
record[field_name] = {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Field Usage Analysis
|
||||
|
||||
### Fields Used in Models:
|
||||
|
||||
1. **`logs`, `messages`, `stats`, `tasks`** → `ChatWorkflow` model (`datamodelChat.py`)
|
||||
- Used by: `interfaceDbChatObjects.py`
|
||||
- Type: `List[...]` (should be JSONB)
|
||||
|
||||
2. **`execParameters`, `expectedDocumentFormats`** → `ActionItem` model (`datamodelChat.py`)
|
||||
- Used by: `interfaceDbChatObjects.py` (via `AutomationDefinition`)
|
||||
- Type: `Dict[str, Any]` and `Optional[List[Dict[str, str]]]` (should be JSONB)
|
||||
|
||||
3. **`resultDocuments`** → Not found in current models (may be legacy or unused)
|
||||
|
||||
---
|
||||
|
||||
## Current Interface Handling
|
||||
|
||||
### ✅ `interfaceDbChatObjects.py` - HAS Field Separation Logic
|
||||
- **Has:** `_separateObjectFields()` method (lines 210-254)
|
||||
- **Handles:** Separates `documents`, `stats` to `objectFields` (lines 226-228)
|
||||
- **Still has hardcoded fields:** Lines 234: `execParameters`, `expectedDocumentFormats`, `resultDocuments`
|
||||
- **Status:** ✅ Partially handles field separation, but still relies on connector for JSONB detection
|
||||
|
||||
### ❌ `interfaceDbComponentObjects.py` - NO Field Separation Logic
|
||||
- **Has:** Direct calls to `db.recordCreate()`, `db.recordModify()`
|
||||
- **Handles:** None - relies entirely on connector's hardcoded logic
|
||||
- **Status:** ❌ Needs field separation logic
|
||||
|
||||
### ❌ `interfaceDbAppObjects.py` - NO Field Separation Logic
|
||||
- **Has:** Direct calls to `db.recordCreate()`, `db.recordModify()`
|
||||
- **Handles:** None - relies entirely on connector's hardcoded logic
|
||||
- **Status:** ❌ Needs field separation logic
|
||||
|
||||
### ⚠️ `interfaceRbac.py` - HAS Hardcoded Field Names
|
||||
- **Has:** Helper function `getRecordsetWithRBAC()` (lines 113-120)
|
||||
- **Handles:** Hardcoded field names for None defaults (same as connector)
|
||||
- **Status:** ⚠️ Also needs to be made generic
|
||||
|
||||
---
|
||||
|
||||
## Required Changes
|
||||
|
||||
### 1. **connectorDbPostgre.py** - REMOVE Hardcoded Logic
|
||||
|
||||
#### Change 1: `_get_model_fields()` (Lines 43-87)
|
||||
**REMOVE:**
|
||||
```python
|
||||
or field_name in [
|
||||
"execParameters",
|
||||
"expectedDocumentFormats",
|
||||
"resultDocuments",
|
||||
"logs",
|
||||
"messages",
|
||||
"stats",
|
||||
"tasks",
|
||||
]
|
||||
```
|
||||
|
||||
**REPLACE WITH:** Pure type-based detection only
|
||||
```python
|
||||
# Check for JSONB fields (Dict, List, or complex types)
|
||||
if (
|
||||
field_type == dict
|
||||
or field_type == list
|
||||
or (
|
||||
hasattr(field_type, "__origin__")
|
||||
and field_type.__origin__ in (dict, list)
|
||||
)
|
||||
):
|
||||
fields[field_name] = "JSONB"
|
||||
```
|
||||
|
||||
#### Change 2: `_loadTable()` (Lines 689-700)
|
||||
**REMOVE:** Hardcoded field name checks for None defaults
|
||||
**REPLACE WITH:** Generic type-based handling or remove entirely (let interfaces handle)
|
||||
|
||||
#### Change 3: `getRecordset()` (Lines 891-902)
|
||||
**REMOVE:** Hardcoded field name checks for None defaults
|
||||
**REPLACE WITH:** Generic type-based handling or remove entirely (let interfaces handle)
|
||||
|
||||
---
|
||||
|
||||
### 2. **interfaceDbChatObjects.py** - ENHANCE Field Separation
|
||||
|
||||
#### Change: `_separateObjectFields()` (Lines 210-254)
|
||||
**CURRENT:** Lines 226-228 handle `documents`, `stats` as object fields
|
||||
**CURRENT:** Lines 234 still has hardcoded `execParameters`, `expectedDocumentFormats`, `resultDocuments`
|
||||
|
||||
**UPDATE TO:**
|
||||
- Remove hardcoded field names from line 234
|
||||
- Rely purely on type detection (Dict, List)
|
||||
- Handle relational fields (`documents`, `stats`, `logs`, `messages`) as object fields (not stored in main table)
|
||||
- Handle JSONB fields (`execParameters`, `expectedDocumentFormats`) as simple fields (stored as JSONB)
|
||||
|
||||
**Logic should be:**
|
||||
```python
|
||||
# Relational fields (stored in separate normalized tables)
|
||||
if fieldName in ['documents', 'stats', 'logs', 'messages']:
|
||||
objectFields[fieldName] = value
|
||||
continue
|
||||
|
||||
# JSONB fields (stored as JSONB in main table)
|
||||
if (fieldType == dict or fieldType == list or
|
||||
(hasattr(fieldType, '__origin__') and fieldType.__origin__ in (dict, list))):
|
||||
simpleFields[fieldName] = value # Store as JSONB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. **interfaceDbComponentObjects.py** - ADD Field Separation
|
||||
|
||||
#### Add: `_separateObjectFields()` method
|
||||
**LOCATION:** After `_initializeDatabase()` method (around line 140)
|
||||
|
||||
**IMPLEMENTATION:** Similar to `interfaceDbChatObjects.py` but adapted for ComponentObjects models
|
||||
- Check which models used by this interface have relational/JSONB fields
|
||||
- Implement type-based field separation
|
||||
- Use before calling `db.recordCreate()` and `db.recordModify()`
|
||||
|
||||
**Models used:** `Prompt`, `FileItem`, `FileData`, `VoiceSettings`, `MessagingSubscription`, etc.
|
||||
- Check if any have relational fields that need separate handling
|
||||
|
||||
---
|
||||
|
||||
### 4. **interfaceDbAppObjects.py** - ADD Field Separation
|
||||
|
||||
#### Add: `_separateObjectFields()` method
|
||||
**LOCATION:** After `_initializeDatabase()` method (around line 135)
|
||||
|
||||
**IMPLEMENTATION:** Similar to `interfaceDbChatObjects.py` but adapted for AppObjects models
|
||||
- Check which models used by this interface have relational/JSONB fields
|
||||
- Implement type-based field separation
|
||||
- Use before calling `db.recordCreate()` and `db.recordModify()`
|
||||
|
||||
**Models used:** `User`, `Mandate`, `UserInDB`, `Token`, `AuthEvent`, `UserConnection`, etc.
|
||||
- Check if any have relational fields that need separate handling
|
||||
|
||||
---
|
||||
|
||||
## Impact Analysis
|
||||
|
||||
### Models Affected:
|
||||
|
||||
1. **ChatWorkflow** (`datamodelChat.py`)
|
||||
- Fields: `logs`, `messages`, `stats`, `tasks` (List types)
|
||||
- Currently: Hardcoded in connector
|
||||
- After: Handled by `interfaceDbChatObjects._separateObjectFields()`
|
||||
|
||||
2. **ActionItem** (`datamodelChat.py`)
|
||||
- Fields: `execParameters` (Dict), `expectedDocumentFormats` (List)
|
||||
- Currently: Hardcoded in connector
|
||||
- After: Handled by `interfaceDbChatObjects._separateObjectFields()`
|
||||
|
||||
3. **AutomationDefinition** (`datamodelChat.py`)
|
||||
- Contains `ActionItem` objects
|
||||
- Currently: Hardcoded in connector
|
||||
- After: Handled by `interfaceDbChatObjects._separateObjectFields()`
|
||||
|
||||
---
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### Step 1: Update `interfaceDbChatObjects.py`
|
||||
1. Enhance `_separateObjectFields()` to handle all JSONB fields based on type only
|
||||
2. Remove hardcoded field names
|
||||
3. Test with ChatWorkflow and AutomationDefinition models
|
||||
|
||||
### Step 2: Add Field Separation to Other Interfaces
|
||||
1. Add `_separateObjectFields()` to `interfaceDbComponentObjects.py`
|
||||
2. Add `_separateObjectFields()` to `interfaceDbAppObjects.py`
|
||||
3. Update all `recordCreate()` and `recordModify()` calls to use field separation
|
||||
4. Update `interfaceRbac.py` to remove hardcoded field names from `getRecordsetWithRBAC()`
|
||||
|
||||
### Step 3: Clean Up Connector
|
||||
1. Remove hardcoded field names from `_get_model_fields()`
|
||||
2. Remove hardcoded None defaults from `_loadTable()` and `getRecordset()`
|
||||
3. Make connector purely type-based
|
||||
|
||||
### Step 4: Testing
|
||||
1. Test all CRUD operations for ChatWorkflow
|
||||
2. Test all CRUD operations for AutomationDefinition
|
||||
3. Test all CRUD operations for ComponentObjects models
|
||||
4. Test all CRUD operations for AppObjects models
|
||||
5. Verify JSONB fields are stored/loaded correctly
|
||||
|
||||
---
|
||||
|
||||
## Summary of Changes Needed
|
||||
|
||||
| File | Change Type | Description |
|
||||
|------|-------------|-------------|
|
||||
| `connectorDbPostgre.py` | **REMOVE** | Remove hardcoded field names from `_get_model_fields()` (lines 61-70) |
|
||||
| `connectorDbPostgre.py` | **REMOVE** | Remove hardcoded None defaults from `_loadTable()` (lines 689-700) |
|
||||
| `connectorDbPostgre.py` | **REMOVE** | Remove hardcoded None defaults from `getRecordset()` (lines 891-902) |
|
||||
| `interfaceDbChatObjects.py` | **UPDATE** | Remove hardcoded fields from `_separateObjectFields()` line 234 |
|
||||
| `interfaceDbChatObjects.py` | **UPDATE** | Add `logs`, `messages` to relational fields list (line 226) |
|
||||
| `interfaceDbComponentObjects.py` | **ADD** | Add `_separateObjectFields()` method |
|
||||
| `interfaceDbComponentObjects.py` | **UPDATE** | Use field separation before all `recordCreate()`/`recordModify()` calls |
|
||||
| `interfaceDbAppObjects.py` | **ADD** | Add `_separateObjectFields()` method |
|
||||
| `interfaceDbAppObjects.py` | **UPDATE** | Use field separation before all `recordCreate()`/`recordModify()` calls |
|
||||
| `interfaceRbac.py` | **UPDATE** | Remove hardcoded field names from `getRecordsetWithRBAC()` (lines 115-118) |
|
||||
| `interfaceRbac.py` | **UPDATE** | Use type-based None defaults instead of hardcoded field names |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- The connector should be **completely generic** and only use type information from Pydantic models
|
||||
- Interfaces should handle domain-specific logic (which fields are relational vs JSONB)
|
||||
- The `_separateObjectFields()` pattern in `interfaceDbChatObjects.py` is the correct approach
|
||||
- Other interfaces should follow the same pattern
|
||||
|
|
@ -8,6 +8,29 @@ Dieses Dokument beschreibt die **UI-Architektur und Frontend-Implementierung** f
|
|||
|
||||
**UI-Demo**: Eine interaktive HTML-Demo zur Visualisierung der UI-Struktur findet sich in [`doc_trustee_feature_ui_demo.html`](./doc_trustee_feature_ui_demo.html).
|
||||
|
||||
### Backend-Status ✅ IMPLEMENTIERT
|
||||
|
||||
Das Backend ist vollständig implementiert und bereit für die Frontend-Entwicklung:
|
||||
|
||||
| Komponente | Datei | Status |
|
||||
|------------|-------|--------|
|
||||
| **Datenmodelle** | `gateway/modules/datamodels/datamodelTrustee.py` | ✅ |
|
||||
| **Interface** | `gateway/modules/interfaces/interfaceDbTrusteeObjects.py` | ✅ |
|
||||
| **API-Routes** | `gateway/modules/routes/routeDataTrustee.py` | ✅ |
|
||||
| **RBAC-Regeln** | `gateway/modules/interfaces/interfaceBootstrap.py` | ✅ |
|
||||
| **App-Registrierung** | `gateway/app.py` | ✅ |
|
||||
|
||||
**API-Basis-URL**: `/api/trustee/`
|
||||
|
||||
**Implementierte Entities**:
|
||||
- `TrusteeOrganisation` - Trustee-Organisationen
|
||||
- `TrusteeRole` - Feature-spezifische Rollen (userreport, admin, operate)
|
||||
- `TrusteeAccess` - Benutzerzugriffe auf Organisationen (mit optionalem Contract)
|
||||
- `TrusteeContract` - Kundenverträge
|
||||
- `TrusteeDocument` - Dokumente/Belege
|
||||
- `TrusteePosition` - Buchungspositionen
|
||||
- `TrusteePositionDocument` - Verknüpfung Position-Dokument
|
||||
|
||||
**Frontend-Stack**: React 19, TypeScript, Vite
|
||||
**Pattern**:
|
||||
- **Logic-Hook** (`use*Logic.tsx`) - Handhabt Daten laden, CRUD, State-Management, Column/Action/Field-Konfigurationen
|
||||
|
|
@ -1069,15 +1092,24 @@ export function useOrganisationenLogic() {
|
|||
let options = attr.options;
|
||||
|
||||
// Wenn options eine String-Referenz ist, dynamisch laden
|
||||
// Backend verwendet jetzt PascalCase-Referenzen: TrusteeOrganisation, TrusteeRole, etc.
|
||||
if (typeof attr.options === 'string' && attr.type === 'select') {
|
||||
// Beispiel: "trustee.organisation" -> API-Call zu /api/trustee/organisations/
|
||||
if (attr.options.startsWith('trustee.')) {
|
||||
const resource = attr.options.replace('trustee.', '');
|
||||
// Beispiel: "TrusteeOrganisation" -> API-Call zu /api/trustee/organisations
|
||||
const optionMap: Record<string, () => Promise<any>> = {
|
||||
'TrusteeOrganisation': trusteeApi.getOrganisationen,
|
||||
'TrusteeRole': trusteeApi.getRollen,
|
||||
'TrusteeContract': trusteeApi.getContracts,
|
||||
'TrusteeDocument': trusteeApi.getDocuments,
|
||||
'TrusteePosition': trusteeApi.getPositions,
|
||||
'User': () => api.get('/api/users'),
|
||||
};
|
||||
|
||||
if (optionMap[attr.options]) {
|
||||
try {
|
||||
const response = await trusteeApi[`get${resource.charAt(0).toUpperCase() + resource.slice(1)}`]();
|
||||
const response = await optionMap[attr.options]();
|
||||
options = (response.data.items || response.data).map((item: any) => ({
|
||||
value: item.id,
|
||||
label: item.label || item.name || item.id
|
||||
label: item.label || item.name || item.fullName || item.id
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error(`Error loading options for ${attr.options}:`, err);
|
||||
|
|
@ -1204,60 +1236,63 @@ export function useOrganisationenLogic() {
|
|||
**Datei**: `src/api.ts` erweitern
|
||||
|
||||
```typescript
|
||||
// Trustee API functions
|
||||
// Trustee API functions - Implementiert in gateway/modules/routes/routeDataTrustee.py
|
||||
export const trusteeApi = {
|
||||
// Organisationen
|
||||
getOrganisationen: () => api.get('/api/trustee/organisations/'),
|
||||
// Organisationen (TrusteeOrganisation)
|
||||
getOrganisationen: (pagination?: PaginationParams) => api.get('/api/trustee/organisations', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getOrganisation: (id: string) => api.get(`/api/trustee/organisations/${id}`),
|
||||
createOrganisation: (data: any) => api.post('/api/trustee/organisations/', data),
|
||||
createOrganisation: (data: any) => api.post('/api/trustee/organisations', data),
|
||||
updateOrganisation: (id: string, data: any) => api.put(`/api/trustee/organisations/${id}`, data),
|
||||
deleteOrganisation: (id: string) => api.delete(`/api/trustee/organisations/${id}`),
|
||||
|
||||
// Rollen
|
||||
getRollen: () => api.get('/api/trustee/roles/'),
|
||||
// Rollen (TrusteeRole)
|
||||
getRollen: (pagination?: PaginationParams) => api.get('/api/trustee/roles', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getRolle: (id: string) => api.get(`/api/trustee/roles/${id}`),
|
||||
createRolle: (data: any) => api.post('/api/trustee/roles/', data),
|
||||
createRolle: (data: any) => api.post('/api/trustee/roles', data),
|
||||
updateRolle: (id: string, data: any) => api.put(`/api/trustee/roles/${id}`, data),
|
||||
deleteRolle: (id: string) => api.delete(`/api/trustee/roles/${id}`),
|
||||
|
||||
// Access
|
||||
getAccess: () => api.get('/api/trustee/access/'),
|
||||
// Access (TrusteeAccess)
|
||||
getAccess: (pagination?: PaginationParams) => api.get('/api/trustee/access', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getAccessById: (id: string) => api.get(`/api/trustee/access/${id}`),
|
||||
getAccessForOrganisation: (orgId: string) => api.get(`/api/trustee/access/organisation/${orgId}`),
|
||||
createAccess: (data: any) => api.post('/api/trustee/access/', data),
|
||||
getAccessForUser: (userId: string) => api.get(`/api/trustee/access/user/${userId}`),
|
||||
createAccess: (data: any) => api.post('/api/trustee/access', data),
|
||||
updateAccess: (id: string, data: any) => api.put(`/api/trustee/access/${id}`, data),
|
||||
deleteAccess: (id: string) => api.delete(`/api/trustee/access/${id}`),
|
||||
|
||||
// Contracts
|
||||
getContracts: () => api.get('/api/trustee/contracts/'),
|
||||
// Contracts (TrusteeContract)
|
||||
getContracts: (pagination?: PaginationParams) => api.get('/api/trustee/contracts', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getContract: (id: string) => api.get(`/api/trustee/contracts/${id}`),
|
||||
getContractsForOrganisation: (orgId: string) => api.get(`/api/trustee/contracts/organisation/${orgId}`),
|
||||
createContract: (data: any) => api.post('/api/trustee/contracts/', data),
|
||||
createContract: (data: any) => api.post('/api/trustee/contracts', data),
|
||||
updateContract: (id: string, data: any) => api.put(`/api/trustee/contracts/${id}`, data),
|
||||
deleteContract: (id: string) => api.delete(`/api/trustee/contracts/${id}`),
|
||||
|
||||
// Documents
|
||||
getDocuments: () => api.get('/api/trustee/documents/'),
|
||||
// Documents (TrusteeDocument)
|
||||
getDocuments: (pagination?: PaginationParams) => api.get('/api/trustee/documents', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getDocument: (id: string) => api.get(`/api/trustee/documents/${id}`),
|
||||
getDocumentData: (id: string) => api.get(`/api/trustee/documents/${id}/data`, { responseType: 'blob' }),
|
||||
getDocumentsForContract: (contractId: string) => api.get(`/api/trustee/documents/contract/${contractId}`),
|
||||
getDocumentsForPosition: (positionId: string) => api.get(`/api/trustee/documents/position/${positionId}`),
|
||||
createDocument: (data: FormData) => api.post('/api/trustee/documents/', data, { headers: { 'Content-Type': 'multipart/form-data' } }),
|
||||
createDocument: (data: FormData) => api.post('/api/trustee/documents', data, { headers: { 'Content-Type': 'multipart/form-data' } }),
|
||||
updateDocument: (id: string, data: any) => api.put(`/api/trustee/documents/${id}`, data),
|
||||
deleteDocument: (id: string) => api.delete(`/api/trustee/documents/${id}`),
|
||||
|
||||
// Positions
|
||||
getPositions: () => api.get('/api/trustee/positions/'),
|
||||
// Positions (TrusteePosition)
|
||||
getPositions: (pagination?: PaginationParams) => api.get('/api/trustee/positions', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getPosition: (id: string) => api.get(`/api/trustee/positions/${id}`),
|
||||
getPositionsForContract: (contractId: string) => api.get(`/api/trustee/positions/contract/${contractId}`),
|
||||
getPositionsForDocument: (documentId: string) => api.get(`/api/trustee/positions/document/${documentId}`),
|
||||
createPosition: (data: any) => api.post('/api/trustee/positions/', data),
|
||||
getPositionsForOrganisation: (orgId: string) => api.get(`/api/trustee/positions/organisation/${orgId}`),
|
||||
createPosition: (data: any) => api.post('/api/trustee/positions', data),
|
||||
updatePosition: (id: string, data: any) => api.put(`/api/trustee/positions/${id}`, data),
|
||||
deletePosition: (id: string) => api.delete(`/api/trustee/positions/${id}`),
|
||||
|
||||
// Position-Documents
|
||||
getPositionDocuments: () => api.get('/api/trustee/position-documents/'),
|
||||
getPositionDocumentsForPosition: (positionId: string) => api.get(`/api/trustee/position-documents/position/${positionId}`),
|
||||
getPositionDocumentsForDocument: (documentId: string) => api.get(`/api/trustee/position-documents/document/${documentId}`),
|
||||
createPositionDocument: (data: any) => api.post('/api/trustee/position-documents/', data),
|
||||
// Position-Documents (TrusteePositionDocument)
|
||||
getPositionDocuments: (pagination?: PaginationParams) => api.get('/api/trustee/position-documents', { params: { pagination: pagination ? JSON.stringify(pagination) : undefined } }),
|
||||
getPositionDocument: (id: string) => api.get(`/api/trustee/position-documents/${id}`),
|
||||
getDocumentsForPosition: (positionId: string) => api.get(`/api/trustee/position-documents/position/${positionId}`),
|
||||
getPositionsForDocument: (documentId: string) => api.get(`/api/trustee/position-documents/document/${documentId}`),
|
||||
createPositionDocument: (data: any) => api.post('/api/trustee/position-documents', data),
|
||||
deletePositionDocument: (id: string) => api.delete(`/api/trustee/position-documents/${id}`),
|
||||
};
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue